view count for threads

This commit is contained in:
Akif9748 2022-08-28 15:00:53 +03:00
parent 1f737277b2
commit 98356a2a90
8 changed files with 95 additions and 170 deletions

View file

@ -58,7 +58,7 @@ Akf-forum has got an API for AJAX, other clients etc. And, you can learn about A
| Send | 🟢 | HIGH | | Send | 🟢 | HIGH |
| Delete | 🟢 | HIGH | | Delete | 🟢 | HIGH |
| Regex for scripts | 🔴 | HIGH | | Regex for scripts | 🔴 | HIGH |
| Undelete | 🟡 | MEDIUM | | Undelete | 🟢 | MEDIUM |
| React | 🟢 | MEDIUM | | React | 🟢 | MEDIUM |
| Edit | 🔴 | MEDIUM | | Edit | 🔴 | MEDIUM |

View file

@ -1,5 +1,5 @@
const mongoose = require("mongoose") const mongoose = require("mongoose")
const UserModel = require("./User"); const UserModel = require("./User");
const schema = new mongoose.Schema({ const schema = new mongoose.Schema({
id: { type: String, unique: true }, id: { type: String, unique: true },
@ -8,7 +8,8 @@ const schema = new mongoose.Schema({
title: String, title: String,
time: { type: Date, default: Date.now }, time: { type: Date, default: Date.now },
deleted: { type: Boolean, default: false }, deleted: { type: Boolean, default: false },
messages: [String] messages: [String],
views: { type: Number, default: 0 }
}); });

View file

@ -19,7 +19,10 @@
"bugs": { "bugs": {
"url": "https://github.com/Akif9748/akf-forum/issues" "url": "https://github.com/Akif9748/akf-forum/issues"
}, },
"homepage": "https://akf-forum.herokuapp.com/", "engines": {
"node": ">=16"
},
"homepage": "https://akf-forum.glitch.me/",
"dependencies": { "dependencies": {
"bcrypt": "^5.0.1", "bcrypt": "^5.0.1",
"body-parser": "^1.19.2", "body-parser": "^1.19.2",
@ -30,4 +33,4 @@
"express-session": "^1.17.2", "express-session": "^1.17.2",
"mongoose": "^6.5.1" "mongoose": "^6.5.1"
} }
} }

View file

@ -28,7 +28,6 @@
textarea { textarea {
font-family: monospace; font-family: monospace;
background-color: var(--col-bg);
border: 2px solid var(--col-8); border: 2px solid var(--col-8);
color: var(--col-fg); color: var(--col-fg);
width: auto; width: auto;
@ -130,36 +129,6 @@ button:hover {
/* NAVBAR: */ /* NAVBAR: */
.navbar {
background-color: #333;
overflow: hidden;
}
.navbar a {
float: left;
color: var(--col-fg);
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-size: 17px;
border-right: 3px solid var(--col-8);
}
.navbar a:hover {
background-color: var(--col-bg);
color: var(--col-fg);
}
.navbar h1:hover {
background-color: var(--col-bg);
color: var(--col-fg);
}
.navbar a.active {
background-color: var(--col-14);
color: var(--col-15);
}
.user { .user {
margin: 0; margin: 0;
@ -211,52 +180,9 @@ img.logo {
} }
/*
*****************************
FOOTER
*****************************
*/
.footer {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
background-color: var(--col-8);
color: white;
text-align: center;
}
.footer p {
font-size: 16px;
color: white;
}
/* /*
***************************** *****************************
ADMIN TEXT ADMIN TEXT
***************************** *****************************
*/ */
.admin {
position: fixed;
left: 0;
top: 0;
height: 20px;
width: 100%;
text-align: center;
}
.admin p {
font-size: 16px;
margin: auto;
color: var(--col-15);
}
.admin a {
background-color: var(--col-13);
border: 2px solid var(--col-8);
height: 20px;
color: var(--col-15);
}
p {font-size: 18px;} p {font-size: 18px;}

View file

@ -23,7 +23,7 @@ app.post("/", rateLimit({
const user2 = new UserModel({ name: req.body.username }) const user2 = new UserModel({ name: req.body.username })
if (avatar&&/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g.test(avatar)) user2.avatar = avatar; if (avatar && /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g.test(avatar)) user2.avatar = avatar;
await user2.takeId() await user2.takeId()
await user2.save(); await user2.save();

View file

@ -6,33 +6,26 @@ const { ThreadModel, MessageModel } = require("../models")
app.get("/", async (req, res) => { app.get("/", async (req, res) => {
const threads = await ThreadModel.find(req.user?.admin ? {} : { deleted: false }).limit(10); const threads = await ThreadModel.find(req.user?.admin ? {} : { deleted: false })//.limit(10);
return res.reply("threads", { threads }); return res.reply("threads", { threads });
}); });
app.get("/create*", async (req, res) => { app.get("/create*", (req, res) => res.reply("create_thread"));
res.reply("create_thread")
});
app.get("/:id", async (req, res) => { app.get("/:id", async (req, res) => {
const { id } = req.params; const { id } = req.params;
const thread = await ThreadModel.get(id); const thread = await ThreadModel.get(id);
const user = req.user; thread.views++;
if (thread && (user?.admin || !thread.deleted)) { if (thread && (req.user?.admin || !thread.deleted))
res.reply("thread", { thread, scroll: req.query.scroll || false });
const messages = await Promise.all(thread.messages.map(async id => { else
const message = await MessageModel.get(id)
return user?.admin || !message?.deleted ? message.toObject({ virtuals: true }) : null;
}));
res.reply("thread", { thread, messages, scroll: req.query.scroll || false });
} else
res.error(404, "We have not got this thread."); res.error(404, "We have not got this thread.");
thread.save();
}); });

View file

@ -14,6 +14,7 @@
<h1 style="font-size: 35px;color: #4d18e6;" ><%= thread.title %></h1> <h1 style="font-size: 35px;color: #4d18e6;" ><%= thread.title %></h1>
<h1 style="font-size: 19px; " >View count: <%= thread.views %></h1>
<h2 style="display:inline;">By <a href="<%='/users/' + thread.author.id %>"> <%= thread.author.name %></a> <h2 style="display:inline;">By <a href="<%='/users/' + thread.author.id %>"> <%= thread.author.name %></a>
<img class="circle" src="<%=thread.author.avatar %>"> <img class="circle" src="<%=thread.author.avatar %>">

View file

@ -4,93 +4,94 @@
<%- include("extra/meta", {title: member.name }) %> <%- include("extra/meta", {title: member.name }) %>
<body style="text-align: center;"> <body style="text-align: center;">
<%- include("extra/navbar") %> <%- include("extra/navbar") %>
<ul> <ul>
<li> <li>
<h1 style="color: #4d18e6;">Avatar:</h1> <h1 style="color: #4d18e6;">Avatar:</h1>
<img style="width:256px;height:256px;" src="<%=member.avatar %>"> <img style="width:256px;height:256px;" src="<%=member.avatar %>">
</li> </li>
<li> <li>
<h2 style="color: #606060;">Name: <%= member.name %> <h2 style="color: #606060;">Name: <%= member.name %>
</h2> </h2>
</li> </li>
<li> <li>
<h2 style="color: #606060;">Created at: <h2 style="color: #606060;">Created at:
<%= new Date(member.time).toLocaleString() %> <%= new Date(member.time).toLocaleString() %>
</h2> </h2>
</li> </li>
<li> <li>
<h2 style="color: #606060;">Is admin? <%= member.admin ? "Yes" : "No" %> <h2 style="color: #606060;">Is admin? <%= member.admin ? "Yes" : "No" %>
</h2> </h2>
</li> </li>
<li> <li>
<h2 style="color: #606060;"> Message: <%= counts.message %> <h2 style="color: #606060;"> Message: <%= counts.message %>
</h2> </h2>
</li> </li>
<li> <li>
<h2 style="color: #606060;"> Thread: <%= counts.thread %> <h2 style="color: #606060;"> Thread: <%= counts.thread %>
</h2> </h2>
</li> </li>
</ul> </ul>
<% if (user?.admin && !member.deleted) {%> <% if (user?.admin && !member.deleted) {%>
<a class="big" id="admin">Give admin permissions!</a> <a class="big" id="admin">Give admin permissions!</a>
<a class="big" id="delete">Delete user!</a> <a class="big" id="delete">Delete user!</a>
<script type="module">
import request from "../../js/request.js";
document.addEventListener("click", async e => {
e.preventDefault();
if (e.target.id == "admin") {
const response = await request("/api/users/<%= member.id %>/admin");
if (response.admin)
return alert("Making admin of " + response.name + " is success!");
}
const response = await request("/api/users/<%= member.id %>/delete");
if (!response.deleted) return
alert("User is deleted!");
location.reload()
});
</script>
<% }; %>
<% if (member.deleted) {%>
<h1>This user has been deleted!</h1>
<a onclick="undelete();" type="">Undelete user! </a>
<script type="module"> <script type="module">
import request from "../../js/request.js"; import request from "../../js/request.js";
async function undelete(params) { document.addEventListener("click", async e => {
e.preventDefault();
const response = await request("/api/users/<%= member.id %>/undelete"); if (e.target.id == "admin") {
if (response.deleted) return; const response = await request("/api/users/<%= member.id %>/admin");
alert("User is undeleted successfully!");
if (response.admin)
return alert("Making admin of " + response.name + " is success!");
} else if (e.target.id == "delete") {
const response = await request("/api/users/<%= member.id %>/delete");
if (!response.deleted) return;
alert("User is deleted!");
location.reload() location.reload()
} }
});
</script> </script>
<% }; %> <% }; %>
</body> <% if (member.deleted) {%>
<h1>This user has been deleted!</h1>
<a onclick="undelete();" type="">Undelete user! </a>
<script type="module">
import request from "../../js/request.js";
async function undelete(params) {
const response = await request("/api/users/<%= member.id %>/undelete");
if (response.deleted) return;
alert("User is undeleted successfully!");
location.reload()
}
</script>
<% }; %>
</body>
</html> </html>