Caching for users, and rename is fixed

This commit is contained in:
Akif9748 2022-08-31 14:44:28 +03:00
parent 256b70c611
commit b0a7ac7605
15 changed files with 157 additions and 169 deletions

View File

@ -18,28 +18,28 @@ But in front end, the API will works with session.
### Request types: ### Request types:
- GET `/api/bans/` fetch all bans. - GET `/api/bans/` fetch all bans.
- GET `/api/bans/:id` fetch a ban. - GET `/api/bans/:id` fetch a ban.
- POST `/api/bans/:id?reason=flood` for ban an IP adress.
- DELETE `/api/bans/:id` for unban an IP adress. - DELETE `/api/bans/:id` for unban an IP adress.
- POST `/api/bans?reason=flood` for ban an IP adress.
- GET `/api/users/:id` for fetch user. - GET `/api/users/:id` for fetch user.
- DELETE `/api/users/:id/` for delete user. - DELETE `/api/users/:id/` for delete user.
- PATCH `/api/users/:id/` for edit user.
- POST `/api/users/:id/undelete` for undelete user. - POST `/api/users/:id/undelete` for undelete user.
- POST `/api/users/:id/admin` for give admin permissions for a user. - POST `/api/users/:id/admin` for give admin permissions for a user.
- PATCH `/api/users/:id/` for edit user.
- GET `/api/threads/:id` for fetch thread. - GET `/api/threads/:id` for fetch thread.
- GET `/api/threads/:id/messages/` for fetch messages in thread.
- POST `/api/threads` for create thread.
- DELETE `/api/threads/:id/` for delete thread. - DELETE `/api/threads/:id/` for delete thread.
- POST `/api/threads/:id/undelete` for undelete thread.
- PATCH `/api/threads/:id/` for edit thread. - PATCH `/api/threads/:id/` for edit thread.
- POST `/api/threads/:id/undelete` for undelete thread.
- GET `/api/threads/:id/messages?skip=0&limit=10` for fetch messages in thread.
- POST `/api/threads` for create thread.
- GET `/api/messages/:id` for fetch message. - GET `/api/messages/:id` for fetch message.
- POST `/api/messages` for create message.
- DELETE `/api/messages/:id/` for delete message. - DELETE `/api/messages/:id/` for delete message.
- PATCH `/api/messages/:id/` for edit message.
- POST `/api/messages/:id/undelete` for undelete message. - POST `/api/messages/:id/undelete` for undelete message.
- POST `/api/messages/:id/react/:type` for react to a message. - POST `/api/messages/:id/react/:type` for react to a message.
- PATCH `/api/messages/:id/` for edit message. - POST `/api/messages` for create message.
### Example request: ### Example request:
GET ```/api/messages/0``` GET ```/api/messages/0```
@ -67,7 +67,7 @@ GET ```/api/messages/0```
"__v": 0, "__v": 0,
"react": { "react": {
"like": [0], "like": [0],
"dislike":[] "dislike": []
}, },
"authorID": "0" "authorID": "0"
} }

View File

@ -29,74 +29,41 @@ Akf-forum has got an API for AJAX, other clients etc. And, you can learn about A
## Roadmap ## Roadmap
### TO-DO: ### TO-DO:
- If thread deleted, not show its messages in API. | To do | Is done? | Priority |
| ----- | -------- | -------- |
| Profile Message | 🔴 | LOW |
| from form to AJAX | 🟢 | HIGH |
| auto-scroll | 🟡 | LOW |
| Page support, support message limit correct | 🟢 | MEDIUM |
| Multi-theme support, black theme | 🟡 | LOW |
| Search | 🔴 | MEDIUM |
| Footer | 🔴 | LOW |
- If thread deleted, not show its messages in API. ?
- Profile photos will store in database - Profile photos will store in database
- replacer function global - replacer function global
- author name of thread - author name of thread
- page for threads - users [] - page for threads - users
- API, ?fast=
- extra ratelimits - extra ratelimits
- better edits - better edits
- IP BAN CLI IN ADMIN PANEL - IP BAN fix
- APIDOCS query
### Frontend - app.param for users in API
#### User - message counts for API
| To do | Is done? | Priority | - ZATEN SİLİNDİ BU KİŞİ & MESAJ
| ----- | -------- | -------- |
| Login via redirect query | 🟢 | HIGH |
| Register | 🟢 | HIGH |
| Logout | 🟢 | HIGH |
| Admin | 🟢 | HIGH |
| Message count | 🟢 | MEDIUM |
| Delete user | 🟢 | HIGH |
| Undelete | 🟢 | MEDIUM |
| About me | 🟢 | LOW |
| Edit user | 🟢 | HIGH |
| IP ban | 🟢 | MEDIUM |
| Profile Message | 🔴 | MEDIUM |
#### Messages
| To do | Is done? | Priority |
| ----- | -------- | -------- |
| Ratelimit | 🟢 | HIGH |
| Send | 🟢 | HIGH |
| Delete | 🟢 | HIGH |
| Regex for scripts | 🟢 | HIGH |
| Undelete | 🟢 | MEDIUM |
| React | 🟢 | MEDIUM |
| Edit | 🟢 | MEDIUM |
#### Threads
| To do | Is done? | Priority |
| ----- | -------- | -------- |
| Ratelimit | 🟢 | HIGH |
| Create | 🟢 | HIGH |
| Delete | 🟢 | HIGH |
| Undelete | 🟢 | MEDIUM |
| Edit | 🟢 | MEDIUM |
### API ### API
| To do | Is done? | To do | Is done?
| ----- | -------- | ----- | --------
| RATELIMITS | 🟢 | RATELIMITS | 🟢
| Get message**s** | 🟢 | Get a lots of message & thread & user | 🔴
| Create message & thread & user | 🟢 | Create message & thread & user | 🟢
| Get message & thread & user | 🟢 | Get message & thread & user | 🟢
| Delete message & thread & user | 🟢 | Delete message & thread & user | 🟢
| Undelete message & thread & user | 🟢 | Undelete message & thread & user | 🟢
| Edit message & thread & user | 🟢 | Edit message & thread & user | 🟢
### Other
| To do | Is done? | Priority |
| ----- | -------- | -------- |
| from form to AJAX | 🟢 | HIGH |
| auto-scroll | 🟢 | LOW |
| Page support, support message limit correct | 🟢 | MEDIUM |
| Multi-theme support, black theme | 🟡 | LOW |
| Search | 🔴 | MEDIUM |
| Footer | 🔴 | LOW |
## Major Version History ## Major Version History
- V4: Caching
- V3: New Theme - V3: New Theme
- V2: Backend fix, mongoose is fixed. Really big fix. - V2: Backend fix, mongoose is fixed. Really big fix.
- V1: Mongoose added. - V1: Mongoose added.

View File

@ -1,18 +1,19 @@
const { def_theme } = require("./config.json"), const { UserModel, BanModel } = require("./models"),
{ def_theme } = require("./config.json"),
ipBlock = require('express-ip-block'), ipBlock = require('express-ip-block'),
session = require('express-session'), session = require('express-session'),
{ UserModel, BanModel } = require("./models"),
bodyParser = require('body-parser'), bodyParser = require('body-parser'),
port = process.env.PORT || 3000, port = process.env.PORT || 3000,
mongoose = require("mongoose"), mongoose = require("mongoose"),
express = require('express'), express = require('express'),
fs = require("fs"), fs = require("fs"),
app = express(); app = express();
app.ips = []; app.ips = [];
require("dotenv").config(); require("dotenv").config();
mongoose.connect(process.env.MONGO_DB_URL, mongoose.connect(process.env.MONGO_DB_URL,
async () => console.log("Connected to mongoDB with", app.ips = await BanModel.find({}).select("ip"), "banned IPs")); async () => console.log("Database is connected with", (app.ips = await BanModel.find({})).length, "banned IPs"));
app.set("view engine", "ejs"); app.set("view engine", "ejs");
@ -20,7 +21,7 @@ app.use(session({ secret: 'secret', resave: true, saveUninitialized: true }),
bodyParser.urlencoded({ extended: true }), bodyParser.urlencoded({ extended: true }),
express.static("public"), express.json(), ipBlock(app.ips), express.static("public"), express.json(), ipBlock(app.ips),
async (req, res, next) => { async (req, res, next) => {
req.user = await UserModel.get(req.session.userid); req.user = await UserModel.get(req.session.userID);
res.reply = (page, options = {}, status = 200) => res.status(status) res.reply = (page, options = {}, status = 200) => res.status(status)
.render(page, { user: req.user, theme: req.user?.theme || def_theme, ...options }); .render(page, { user: req.user, theme: req.user?.theme || def_theme, ...options });

View File

@ -1,12 +1,10 @@
const mongoose = require("mongoose") const mongoose = require("mongoose")
const UserModel = require("./User"); const cache = require("./cache")
const schema = new mongoose.Schema({ const schema = new mongoose.Schema({
id: { type: String, unique: true }, id: { type: String, unique: true },
author: Object,
threadID: String, threadID: String,
author: UserModel.schema, // user-model authorID: String,
content: String, content: String,
time: { type: Date, default: Date.now }, time: { type: Date, default: Date.now },
deleted: { type: Boolean, default: false }, deleted: { type: Boolean, default: false },
@ -17,7 +15,7 @@ const schema = new mongoose.Schema({
} }
}) })
schema.virtual('authorID').get(function () { return this.author?.id; }); schema.methods.get_author = cache.getAuthor
schema.methods.takeId = async function () { schema.methods.takeId = async function () {
this.id = String(await model.count() || 0); this.id = String(await model.count() || 0);
@ -30,7 +28,9 @@ schema.methods.getLink = function (id = this.id) {
const model = mongoose.model('message', schema); const model = mongoose.model('message', schema);
model.get = id => model.findOne({ id }); model.get = async id => {
const message = await model.findOne({ id })
module.exports = model; return await message.get_author();
};
module.exports = model;

View File

@ -1,10 +1,11 @@
const mongoose = require("mongoose"); const mongoose = require("mongoose");
const UserModel = require("./User"); const cache = require("./cache")
const MessageModel = require("./Message"); const MessageModel = require("./Message");
const schema = new mongoose.Schema({ const schema = new mongoose.Schema({
id: { type: String, unique: true }, id: { type: String, unique: true },
author: UserModel.schema, authorID: String,
author: Object,
title: String, title: String,
time: { type: Date, default: Date.now }, time: { type: Date, default: Date.now },
@ -16,18 +17,20 @@ const schema = new mongoose.Schema({
}); });
schema.virtual('authorID').get(function () { return this.author?.id; });
schema.methods.get_author = cache.getAuthor;
schema.methods.messageCount = async function (admin = false) { schema.methods.messageCount = async function (admin = false) {
const query = { threadID: this.id }; const query = { threadID: this.id };
if (!admin) query.deleted = false; if (!admin) query.deleted = false;
return await MessageModel.count(query) || 0; return await MessageModel.count(query) || 0;
}; };
schema.methods.push = function (messageID) { schema.methods.push = function (messageID) {
this.messages.push(messageID); this.messages.push(messageID);
return this; return this;
} }
schema.methods.takeId = async function () { schema.methods.takeId = async function () {
this.id = await model.count() || 0; this.id = await model.count() || 0;
return this; return this;
@ -39,6 +42,9 @@ schema.methods.getLink = function (id = this.id) {
const model = mongoose.model('thread', schema); const model = mongoose.model('thread', schema);
model.get = id => model.findOne({ id }); model.get = async id => {
const thread = await model.findOne({ id })
return await thread.get_author();
};
module.exports = model; module.exports = model;

View File

@ -14,7 +14,6 @@ const schema = new mongoose.Schema({
}); });
schema.methods.takeId = async function () { schema.methods.takeId = async function () {
this.id = String(await model.count() || 0); this.id = String(await model.count() || 0);
return this; return this;

14
models/cache.js Normal file
View File

@ -0,0 +1,14 @@
const UserModel = require("./User");
const UserCache = [];
module.exports.getAuthor = async function () {
console.log("User Cache Length:", UserCache.length);
const id = this.authorID || this.author?.id;
let user = UserCache.find(user => user?.id == id)
if (!user) {
user = await UserModel.findOne({ id })
UserCache.push(user)
}
this.author = user;
return this;
}

View File

@ -9,8 +9,7 @@ app.get("/", async (req, res) => {
mem = process.memoryUsage().heapUsed / Math.pow(2, 20), mem = process.memoryUsage().heapUsed / Math.pow(2, 20),
users = await UserModel.count({deleted:false}), users = await UserModel.count({deleted:false}),
threads = await ThreadModel.count({deleted:false}), threads = await ThreadModel.count({deleted:false}),
messages = await MessageModel.count({deleted:false}), messages = await MessageModel.count({deleted:false});
user = req.user;
res.reply("index", { mem, users, threads, messages }) res.reply("index", { mem, users, threads, messages })

View File

@ -5,43 +5,47 @@ const { Router } = require("express")
const app = Router(); const app = Router();
app.param("id", async (req, res, next, id) => {
req.message = await ThreadModel.get(id);
if (!req.message) return res.error(404, `We don't have any message with id ${id}.`);
if(req.message.deleted && !req.user?.admin)
return res.error(404, `You do not have permissions to view this message with id ${id}.`)
next();
});
app.get("/:id", async (req, res) => { app.get("/:id", async (req, res) => {
const message = await MessageModel.get(req.params.id); res.complate(message);
if (!message || (message.deleted && req.user && !req.user.admin)) return res.error(404, `We don't have any message with id ${req.params.id}.`);
res.complate(message.toObject({ virtuals: true }));
}) })
app.patch("/:id/", async (req, res) => { app.patch("/:id/", async (req, res) => {
const message = await MessageModel.get(req.params.id); const { message, user } = req;
if (!message || (message.deleted && req.user && !req.user.admin)) return res.error(404, `We don't have any message with id ${req.params.id}.`); if (user.id !== message.authorID && !user.admin) return res.error(403, "You have not got permission for this.");
if (req.user.id !== message.authorID && !req.user.admin) return res.error(403, "You have not got permission for this.");
const { content = null } = req.body; const { content = null } = req.body;
if (!content) return res.error(400, "Missing message content in request body."); if (!content) return res.error(400, "Missing message content in request body.");
message.content = content; message.content = content;
message.edited=true; message.edited = true;
await message.save(); await message.save();
res.complate(message.toObject({ virtuals: true })); res.complate(message);
}) })
app.post("/", rateLimit({ app.post("/", rateLimit({
windowMs: 60_000, max: 1, standardHeaders: true, legacyHeaders: false, windowMs: 60_000, max: 1, standardHeaders: true, legacyHeaders: false,
handler: (request, response, next, options) => handler: (request, response, next, options) =>
!request.user.admin ? !request.user.admin ? response.error(options.statusCode, "You are begin ratelimited") : next()
response.error(options.statusCode, "You are begin ratelimited")
: next()
}), async (req, res) => { }), async (req, res) => {
const { threadID = null, content = null } = req.body; const { threadID, content } = req.body;
if (!content) return res.error(400, "Missing message content in request body."); if (!content) return res.error(400, "Missing message content in request body.");
const thread = await ThreadModel.get(threadID); const thread = await ThreadModel.get(threadID);
@ -52,13 +56,13 @@ app.post("/", rateLimit({
await message.save(); await message.save();
await thread.push(message.id).save(); await thread.push(message.id).save();
res.complate(message.toObject({ virtuals: true })); res.complate(message);
}) })
app.post("/:id/react/:type", async (req, res) => { app.post("/:id/react/:type", async (req, res) => {
const message = await MessageModel.get(req.params.id); const { message } = req;
if (!message) return error(res, 404, `We don't have any message with id ${req.params.id}.`);
if (req.params.type == "like") { if (req.params.type == "like") {
if (message.react.like.includes(req.user.id)) if (message.react.like.includes(req.user.id))
@ -78,43 +82,41 @@ app.post("/:id/react/:type", async (req, res) => {
message.react.like.pull(req.user.id); message.react.like.pull(req.user.id);
} }
} else { } else
return res.error(400, `We don't have any react type with name ${req.params.type}.`); return res.error(400, `We don't have any react type with name ${req.params.type}.`);
}
await message.save(); await message.save();
res.complate(message.toObject({ virtuals: true })); res.complate(message);
}); });
app.delete("/:id/", async (req, res) => { app.delete("/:id/", async (req, res) => {
const message = await MessageModel.get(req.params.id);
if (!message || (message.deleted && req.user && !req.user.admin))
return res.error(404, `We don't have any message with id ${req.params.id}.`); const { message, user } = req;
const user = req.user;
if (user.id != message.authorID && !user.admin) if (user.id != message.authorID && !user.admin)
return res.error(403, "You have not got permission for this."); return res.error(403, "You have not got permission for this.");
message.deleted = true; message.deleted = true;
await message.save(); await message.save();
res.complate(message.toObject({ virtuals: true })); res.complate(message);
}) })
app.post("/:id/undelete", async (req, res) => { app.post("/:id/undelete", async (req, res) => {
if (!req.user.admin) return res.error(403, "You have not got permission for this.");
const message = await MessageModel.get(req.params.id); const { message } = req;
if (!message) return res.error(404, `We don't have any message with id ${req.params.id}.`);
if (!message.deleted) return res.error(404, "This message is not deleted, first, delete it."); if (!message.deleted) return res.error(404, "This message is not deleted, first, delete it.");
message.deleted = false; message.deleted = false;
await message.save(); await message.save();
res.complate(message.toObject({ virtuals: true })); res.complate(message);
}) })

View File

@ -2,22 +2,20 @@ const { MessageModel, ThreadModel } = require("../../../models");
const { Router } = require("express") const { Router } = require("express")
const app = Router(); const app = Router();
app.param("id", async (req, res, next, id) => {
req.thread = await ThreadModel.get(id);
app.get("/:id", async (req, res) => { if (!req.thread) return res.error(404, `We don't have any thread with id ${id}.`);
const { id } = req.params;
const thread = await ThreadModel.get(id);
if (thread && (req.user?.admin || !thread.deleted))
res.complate(thread.toObject({ virtuals: true }));
else
return res.error(404, `We don't have any thread with id ${id}.`);
if (req.thread.deleted && !req.user?.admin)
return res.error(404, `You do not have permissions to view this thread with id ${id}.`)
next();
}); });
app.get("/:id/messages/", async (req, res) => { app.get("/:id", async (req, res) => res.complate(req.thread));
app.get("/:id/messages/", async (req, res) => {
const { id } = req.params; const { id } = req.params;
const limit = Number(req.query.limit); const limit = Number(req.query.limit);
@ -34,68 +32,62 @@ app.get("/:id/messages/", async (req, res) => {
if (!messages.length) return res.error(404, "We don't have any messages in this with your query thread."); if (!messages.length) return res.error(404, "We don't have any messages in this with your query thread.");
res.complate(messages.map(x => x.toObject({ virtuals: true }))); res.complate(messages);
}) })
app.post("/", async (req, res) => { app.post("/", async (req, res) => {
const { title = null, content = null } = req.body; const { title, content } = req.body;
if (!content || !title) return res.error(400, "Missing content/title in request body."); if (!content || !title) return res.error(400, "Missing content/title in request body.");
const user = req.user; const { user } = req;
const thread = await new ThreadModel({ title, author: user }).takeId() const thread = await new ThreadModel({ title, author: user }).takeId()
const message = await new MessageModel({ content, author: user, threadID: thread.id }).takeId() const message = await new MessageModel({ content, author: user, threadID: thread.id }).takeId()
await thread.push(message.id).save(); await thread.push(message.id).save();
await message.save(); await message.save();
res.complate(thread.toObject({ virtuals: true })); res.complate(thread);
}); });
app.patch("/:id/", async (req, res) => { app.patch("/:id/", async (req, res) => {
const thread = await ThreadModel.get(req.params.id); const { user, thread } = req;
if (!thread || (thread.deleted && req.user && !req.user.admin)) return res.error(404, `We don't have any message with id ${req.params.id}.`); if (user.id !== thread.authorID && !user.admin) return res.error(403, "You have not got permission for this.");
const { title } = req.body;
if (req.user.id !== thread.authorID && !req.user.admin) return res.error(403, "You have not got permission for this.");
const { title = null } = req.body;
if (!title) return res.error(400, "Missing thread title in request body."); if (!title) return res.error(400, "Missing thread title in request body.");
thread.title = title; thread.title = title;
await thread.save(); await thread.save();
res.complate(thread.toObject({ virtuals: true })); res.complate(thread);
}) })
app.delete("/:id/", async (req, res) => { app.delete("/:id/", async (req, res) => {
const thread = await ThreadModel.get(req.params.id);
if (!thread || thread.deleted) return res.error(404, `We don't have any thread with id ${req.params.id}.`); const { user, thread } = req;
const user = req.user;
if (user.id != thread.authorID && !user.admin) if (user.id != thread.authorID && !user.admin)
return res.error(403, "You have not got permission for this."); return res.error(403, "You have not got permission for this.");
thread.deleted = true; thread.deleted = true;
await thread.save(); await thread.save();
res.complate(thread.toObject({ virtuals: true })); res.complate(thread);
}) })
app.post("/:id/undelete", async (req, res) => { app.post("/:id/undelete", async (req, res) => {
if (!req.user.admin) return res.error(403, "You have not got permission for this.");
const thread = await ThreadModel.get(req.params.id); const { thread } = req;
if (!thread ) return res.error(404, `We don't have any thread with id ${req.params.id}.`);
if (!thread.deleted) return res.error(404, "This thread is not deleted, first, delete it."); if (!thread.deleted) return res.error(404, "This thread is not deleted, first, delete it.");
thread.deleted = false; thread.deleted = false;
thread.edited=true; thread.edited = true;
await thread.save(); await thread.save();
res.complate(thread.toObject({ virtuals: true })); res.complate(thread);
}) })
module.exports = app; module.exports = app;

View File

@ -1,28 +1,36 @@
const { UserModel } = require("../../../models"); const { UserModel, SecretModel } = require("../../../models");
const { Router } = require("express") const { Router } = require("express")
const app = Router(); const app = Router();
app.param("id", async (req, res, next, id) => {
req.member = await UserModel.get(id);
if (!req.member) return res.error(404, `We don't have any user with id ${id}.`);
if (req.member.deleted && !req.user?.admin)
return res.error(404, `You do not have permissions to view this user with id ${id}.`);
next();
});
app.get("/:id", async (req, res) => { app.get("/:id", async (req, res) => {
const { id = null } = req.params; if (req.member.not()) return;
const member = await UserModel.get(id);
if (!member || (member.deleted && !req.user.admin)) return res.error(404, `We don't have any user with id ${id}.`);
res.complate(member); res.complate(member);
}); });
app.delete("/:id/", async (req, res) => { app.delete("/:id/", async (req, res) => {
const user = req.user; const { user, member } = req;
if (req.member.not()) return;
if (!user.admin) if (!user.admin)
return res.error(403, "You have not got permission for this."); return res.error(403, "You have not got permission for this.");
const { id = null } = req.params; const { id = null } = req.params;
const member = await UserModel.get(id);
if (!member || member.deleted) return res.error(404, `We don't have any user with id ${id}.`); if (member.deleted) return res.error(404, `This user is with id ${id} already deleted.`);
member.deleted = true; member.deleted = true;
await member.save(); await member.save();
@ -32,7 +40,7 @@ app.delete("/:id/", async (req, res) => {
app.post("/:id/undelete/", async (req, res) => { app.post("/:id/undelete/", async (req, res) => {
if (!req.user.admin) return res.error(403, "You have not got permission for this."); if (!req.user.admin) return res.error(403, "You have not got permission for this.");
const member = await UserModel.get(req.params.id); const { user, member } = req;
if (!member) return res.error(404, `We don't have any user with id ${req.params.id}.`); if (!member) return res.error(404, `We don't have any user with id ${req.params.id}.`);
@ -48,16 +56,17 @@ app.post("/:id/undelete/", async (req, res) => {
app.patch("/:id/", async (req, res) => { app.patch("/:id/", async (req, res) => {
const member = await UserModel.get(req.params.id); const { user, member } = req;
if (!member || (member.deleted && !req.user.admin)) return res.error(404, `We don't have any message with id ${req.params.id}.`);
if (req.user.id !== member.id && !req.user.admin) return res.error(403, "You have not got permission for this."); if (req.user.id !== member.id && !req.user.admin) return res.error(403, "You have not got permission for this.");
const { avatar, name, about } = req.body; const { avatar, name, about } = req.body;
if (!avatar && !name) return res.error(400, "Missing member informations in request body."); if (!avatar && !name) return res.error(400, "Missing member informations in request body.");
if (avatar && /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g.test(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))
member.avatar = avatar; member.avatar = avatar;
if (name) member.name = name; if (name) {
await SecretModel.findOneAndUpdate({ name: member.name }, { name });
member.name = name;
}
if (about) member.about = about; if (about) member.about = about;
member.edited = true; member.edited = true;

View File

@ -6,7 +6,7 @@ const bcrypt = require("bcrypt");
app.get("/", (req, res) => res.reply("login", { redirect: req.query.redirect, user: null })); app.get("/", (req, res) => res.reply("login", { redirect: req.query.redirect, user: null }));
app.post("/", async (req, res) => { app.post("/", async (req, res) => {
req.session.userid = null; req.session.userID = null;
const { username = null, password = null } = req.body; const { username = null, password = null } = req.body;
@ -18,7 +18,7 @@ app.post("/", async (req, res) => {
const member = await UserModel.findOne({ name: username }); const member = await UserModel.findOne({ name: username });
if (!member || member.deleted) return res.error(403, 'Incorrect Username and/or Password!') if (!member || member.deleted) return res.error(403, 'Incorrect Username and/or Password!')
req.session.userid = user.id; req.session.userID = user.id;
res.redirect(req.query.redirect || '/'); res.redirect(req.query.redirect || '/');
} else } else

View File

@ -7,7 +7,7 @@ const app = Router();
app.get("/:id", async (req, res) => { app.get("/:id", async (req, res) => {
const message = await MessageModel.get(req.params.id); const message = await MessageModel.get(req.params.id);
if (!message || (message.deleted && req.user && !req.user.admin)) return res.error( 404, if (!message || (message.deleted && !req.user?.admin)) return res.error( 404,
`We don't have any message with id ${req.params.id}.`); `We don't have any message with id ${req.params.id}.`);
res.redirect(`/threads/${message.threadID}?scroll=${message.id}`); res.redirect(`/threads/${message.threadID}?scroll=${message.id}`);

View File

@ -11,7 +11,7 @@ app.post("/", rateLimit({
windowMs: 24 * 60 * 60_000, max: 10, standardHeaders: true, legacyHeaders: false, windowMs: 24 * 60 * 60_000, max: 10, standardHeaders: true, legacyHeaders: false,
handler: (_r, response, _n, options) => response.error(options.statusCode, "You are begin ratelimited") handler: (_r, response, _n, options) => response.error(options.statusCode, "You are begin ratelimited")
}), async (req, res) => { }), async (req, res) => {
req.session.userid = null; req.session.userID = null;
let { username = null, password: body_pass = null, avatar, about } = req.body; let { username = null, password: body_pass = null, avatar, about } = req.body;
@ -33,7 +33,7 @@ app.post("/", rateLimit({
const salt = await bcrypt.genSalt(10); const salt = await bcrypt.genSalt(10);
const password = await bcrypt.hash(body_pass, salt); const password = await bcrypt.hash(body_pass, salt);
await SecretModel.create({ username, password, id: user2.id }) await SecretModel.create({ username, password, id: user2.id })
req.session.userid = user2.id; req.session.userID = user2.id;
res.redirect('/'); res.redirect('/');

View File

@ -6,8 +6,8 @@ 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); let threads = await ThreadModel.find(req.user?.admin ? {} : { deleted: false })//.limit(10);
threads = await Promise.all(threads.map(thread => thread.get_author()));
return res.reply("threads", { threads }); return res.reply("threads", { threads });
}); });
@ -18,7 +18,7 @@ app.get("/:id/", async (req, res) => {
const { user, params: { id } } = req const { user, params: { id } } = req
let page = Number(req.query.page || 0); const page = Number(req.query.page || 0);
const thread = await ThreadModel.get(id) const thread = await ThreadModel.get(id)
thread.count = await thread.messageCount(user?.admin); thread.count = await thread.messageCount(user?.admin);
@ -28,15 +28,14 @@ app.get("/:id/", async (req, res) => {
const query = { threadID: id }; const query = { threadID: id };
if (!user || !user.admin) query.deleted = false; if (!user || !user.admin) query.deleted = false;
const messages = await MessageModel.find(query).sort({ time: 1 }).limit(10).skip(page * 10) const messages = await Promise.all(await MessageModel.find(query).sort({ time: 1 }).limit(10).skip(page * 10)
.then(messages => messages.map(message => { .then(messages => messages.map(async message => {
message.content = message.content.replaceAll("&", "&") message.content = message.content.replaceAll("&", "&")
.replaceAll("<", "&lt;").replaceAll(">", "&gt;") .replaceAll("<", "&lt;").replaceAll(">", "&gt;")
.replaceAll("\"", "&quot;").replaceAll("'", "&#39;") .replaceAll("\"", "&quot;").replaceAll("'", "&#39;")
.replaceAll("\n", "<br>"); .replaceAll("\n", "<br>");
return message.toObject({ virtuals: true }); return await message.get_author();
})) })));
res.reply("thread", { page, thread, messages, scroll: req.query.scroll || thread.messages[0].id }); res.reply("thread", { page, thread, messages, scroll: req.query.scroll || thread.messages[0].id });
thread.save(); thread.save();