mirror of
https://github.com/Akif9748/akf-forum.git
synced 2024-11-01 03:25:04 +03:00
Caching for users, and rename is fixed
This commit is contained in:
parent
256b70c611
commit
b0a7ac7605
15 changed files with 157 additions and 169 deletions
16
APIDOCS.md
16
APIDOCS.md
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
69
README.md
69
README.md
|
@ -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.
|
||||||
|
|
9
index.js
9
index.js
|
@ -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 });
|
||||||
|
|
||||||
|
|
|
@ -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 })
|
||||||
|
return await message.get_author();
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = model;
|
module.exports = model;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
@ -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
14
models/cache.js
Normal 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;
|
||||||
|
}
|
|
@ -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 })
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
if (req.user.id !== message.authorID && !req.user.admin) return res.error(403, "You have not got permission for this.");
|
if (user.id !== message.authorID && !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);
|
|
||||||
if (!message) return error(res, 404, `We don't have any message with id ${req.params.id}.`);
|
const { message } = req;
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (!message) return res.error(404, `We don't have any message with id ${req.params.id}.`);
|
const { message } = req;
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}`);
|
||||||
|
|
||||||
|
|
|
@ -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('/');
|
||||||
|
|
||||||
|
|
|
@ -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("<", "<").replaceAll(">", ">")
|
.replaceAll("<", "<").replaceAll(">", ">")
|
||||||
.replaceAll("\"", """).replaceAll("'", "'")
|
.replaceAll("\"", """).replaceAll("'", "'")
|
||||||
.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();
|
||||||
|
|
Loading…
Reference in a new issue