mirror of
https://github.com/Akif9748/akf-forum.git
synced 2024-12-22 07:39:08 +03:00
Major update named as minor!
Forum Setup page, edit forum config w/api ban user's all ips, secretmodel deleted, /undelete is disabled and better themes if user reacted a message, view it fixs for reactions of messages discord auth in config.json
This commit is contained in:
parent
98d379d82c
commit
db61361a95
25 changed files with 228 additions and 153 deletions
|
@ -1,3 +1,2 @@
|
|||
MONGO_DB_URL = mongodb://localhost:27017/akf-forum
|
||||
DISCORD_CLIENT = discord_app_id
|
||||
SECRET = secret
|
|
@ -41,11 +41,9 @@ You can change them in config.json.
|
|||
- GET `/:id` for fetch message.
|
||||
- DELETE `/:id` for delete message.
|
||||
- PATCH `/:id` for edit message.
|
||||
- POST `/:id/undelete` for undelete message.
|
||||
- POST `/:id/react/:type` for react to a message.
|
||||
- POST `/` for create message.
|
||||
|
||||
|
||||
#### `/api/search` use `?limit=&skip=` for skip and limit
|
||||
- GET `/users?q=query` find users.
|
||||
- GET `/threads?q=query&authorID=not_required` find threads.
|
||||
|
@ -63,6 +61,7 @@ You can change them in config.json.
|
|||
- DELETE `/:id` for delete user.
|
||||
- PATCH `/:id` for edit user.
|
||||
- PUT `/:id` for add profile photo to user.
|
||||
- POST `/:id/ban` for ban all ips of user.
|
||||
|
||||
### Example request:
|
||||
GET ```/api/messages/0```
|
||||
|
|
31
README.md
31
README.md
|
@ -6,14 +6,14 @@ A Node.js based forum software.
|
|||
- Run `npm i` to install **dependencies**.
|
||||
- Enter your database credentials in `.env`.
|
||||
- Run `npm start` for run it.
|
||||
- Go /setup page for setup your forum.
|
||||
|
||||
### Extra
|
||||
### Extra (If you are not use `setup` page)
|
||||
Run `node util/reset` to **reset the database** for duplicate key errors, and run `node util/admin` for give admin perms to first member.
|
||||
Edit `config.json` for default themes (`black` or `default`) of users, and forum name, meta description, character limits, discord auth enabler, global ratelimit.
|
||||
|
||||
### DISCORD AUTH:
|
||||
`discord_auth: true` in config.json.
|
||||
Enter application id to `.env`.
|
||||
`"discord_auth": "your_app_id"` in config.json.
|
||||
Create a redirect url in discord developer portal:
|
||||
`https://forum_url.com/discord_auth/hash`
|
||||
|
||||
|
@ -39,21 +39,20 @@ Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn
|
|||
</details>
|
||||
|
||||
## TO-DO list
|
||||
| To do | Is done? |
|
||||
| ----- | -------- |
|
||||
| Profile Message or DM | ⚪ |
|
||||
| Better Auth for API way | 🟢 |
|
||||
| mod role, permissions | ⚪ |
|
||||
| upload other photos, model for it | ⚪ |
|
||||
| categories page is need a update, thread count in category | ⚪ |
|
||||
| preview for send messages in markdown format | 🟢 |
|
||||
| DC auth will store code for taking tokens, and create secret model setting | ⚪ |
|
||||
- IF a person liked a message, view.
|
||||
- Disable last seen button.
|
||||
- Profile Message or DM
|
||||
- mod role, permissions
|
||||
- upload other photos, model for it
|
||||
- categories page is need a update, thread count in category
|
||||
- DC auth will store code for taking tokens, and create secret model setting
|
||||
- Disable last seen button for web.
|
||||
- email auth.
|
||||
- thread.state =="approval" for threads.
|
||||
- old contents / titles add to forum interface
|
||||
- limits
|
||||
- add ban button to user profile.
|
||||
- change password.
|
||||
- add approval threads page.
|
||||
- who liked a message for web.
|
||||
- edit config from web admin panel.
|
||||
|
||||
## Major Version History
|
||||
- V4: Caching
|
||||
- V3: New Theme
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"max": 25,
|
||||
"windowMs": 60000
|
||||
},
|
||||
"discord_auth": false,
|
||||
"discord_auth": "",
|
||||
"defaultThreadState": "OPEN",
|
||||
"host": "https://akf-forum.glitch.me"
|
||||
}
|
2
index.js
2
index.js
|
@ -44,7 +44,7 @@ app.use(express.static("public"), express.json(), express.urlencoded({extended:t
|
|||
);
|
||||
|
||||
if (discord_auth)
|
||||
app.set("discord_auth", `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT}&redirect_uri=${host}%2Fdiscord_auth%2Fhash&response_type=token&scope=identify`);
|
||||
app.set("discord_auth", `https://discord.com/api/oauth2/authorize?client_id=${discord_auth}&redirect_uri=${host}%2Fdiscord_auth%2Fhash&response_type=token&scope=identify`);
|
||||
|
||||
if (RLS.enabled) app.use(RL(RLS.windowMs, RLS.max));
|
||||
|
||||
|
|
1
lib.js
1
lib.js
|
@ -2,6 +2,7 @@ const RL = require('express-rate-limit');
|
|||
|
||||
module.exports = {
|
||||
threadEnum: ["OPEN", "APPROVAL", "DELETED"],
|
||||
themes: ["default", "black"],
|
||||
RL(windowMs = 60_000, max = 1) {
|
||||
return RL({
|
||||
windowMs, max, standardHeaders: true, legacyHeaders: false,
|
||||
|
|
|
@ -6,4 +6,5 @@ const schema = new mongoose.Schema({
|
|||
authorID: { type: String }
|
||||
});
|
||||
|
||||
module.exports = mongoose.model('ban', schema);
|
||||
const model = mongoose.model('ban', schema);
|
||||
module.exports = model;
|
|
@ -13,8 +13,8 @@ const schema = new mongoose.Schema({
|
|||
deleted: { type: Boolean, default: false },
|
||||
edited: { type: Boolean, default: false },
|
||||
react: {
|
||||
like: [Number],
|
||||
dislike: [Number]
|
||||
like: [String],
|
||||
dislike: [String]
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
const mongoose = require("mongoose")
|
||||
const { limits } = require("../config.json");
|
||||
|
||||
const schema = new mongoose.Schema({
|
||||
username: { type: String, unique: true, maxlength: limits.names },
|
||||
password: String,
|
||||
id: { type: String, unique: true }
|
||||
});
|
||||
|
||||
module.exports = mongoose.model('secret', schema);
|
|
@ -2,7 +2,7 @@ const mongoose = require("mongoose")
|
|||
const { def_theme, limits } = require("../config.json");
|
||||
const schema = new mongoose.Schema({
|
||||
id: { type: String, unique: true },
|
||||
discordID: { type: String, unique: true },
|
||||
discordID: { type: String },
|
||||
name: { type: String, maxlength: limits.names },
|
||||
avatar: { type: String, default: "/images/avatars/default.jpg" },
|
||||
time: { type: Date, default: Date.now },
|
||||
|
@ -13,7 +13,8 @@ const schema = new mongoose.Schema({
|
|||
theme: { type: String, default: def_theme },
|
||||
lastSeen: { type: Date, default: Date.now, select: false },
|
||||
hideLastSeen: { type: Boolean, default: false },
|
||||
ips: { type: [String], select: false }
|
||||
ips: { type: [String], default: [], select: false },
|
||||
password: { type: String, select: false }
|
||||
});
|
||||
|
||||
schema.methods.takeId = async function () {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
const CategoryModel = require("./Category"),
|
||||
MessageModel = require("./Message"),
|
||||
ThreadModel = require("./Thread"),
|
||||
SecretModel = require("./Secret"),
|
||||
UserModel = require("./User"),
|
||||
BanModel = require("./Ban");
|
||||
|
||||
module.exports = { CategoryModel, MessageModel, ThreadModel, SecretModel, UserModel, BanModel };
|
||||
module.exports = {
|
||||
CategoryModel: require("./Category"),
|
||||
MessageModel: require("./Message"),
|
||||
ThreadModel: require("./Thread"),
|
||||
UserModel: require("./User"),
|
||||
BanModel: require("./Ban")
|
||||
};
|
26
public/css/setup.css
Normal file
26
public/css/setup.css
Normal file
|
@ -0,0 +1,26 @@
|
|||
.title {
|
||||
color: var(--main);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
|
||||
input {
|
||||
padding: 8px 10px;
|
||||
font-family: inherit;
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
color: var(--input-clr);
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
border: 2px solid var(--borders);
|
||||
}
|
|
@ -53,7 +53,7 @@ window.edit_message = async function (id) {
|
|||
editing.value(content.rawText)
|
||||
}
|
||||
window.undelete_message = async function (id) {
|
||||
const response = await request(`/api/messages/${id}/undelete`);
|
||||
const response = await request(`/api/messages/${id}/`, "PATCH", { deleted: false });
|
||||
if (response.deleted) return;
|
||||
const message = document.getElementById("message-" + id);
|
||||
|
||||
|
|
30
routes/.js
30
routes/.js
|
@ -1,17 +1,45 @@
|
|||
const { UserModel, ThreadModel, MessageModel } = require("../models")
|
||||
const { Router } = require("express");
|
||||
const app = Router();
|
||||
const fs = require("fs");
|
||||
|
||||
app.get("/", async (req, res) => {
|
||||
|
||||
const
|
||||
mem = process.memoryUsage().heapUsed / Math.pow(2, 20),
|
||||
users = await UserModel.count({ deleted: false }),
|
||||
threads = await ThreadModel.count({ deleted: false }),
|
||||
threads = await ThreadModel.count({ state: "DELETED"}),
|
||||
messages = await MessageModel.count({ deleted: false });
|
||||
|
||||
res.reply("index", { mem, users, threads, messages })
|
||||
|
||||
})
|
||||
|
||||
app.get("/setup", async (req, res) => {
|
||||
if (await UserModel.exists({ admin: true })) return res.error(400, "You have already setuped the site.");
|
||||
res.reply("setup");
|
||||
})
|
||||
app.post("/setup", async (req, res) => {
|
||||
if (await UserModel.exists({ admin: true })) return res.error(400, "You have already setuped the site.");
|
||||
let original = {};
|
||||
|
||||
try {
|
||||
original = JSON.parse(fs.readFileSync("./config.json", "utf8"));
|
||||
} catch (e) {
|
||||
try {
|
||||
original = JSON.parse(fs.readFileSync("./config.json.example", "utf8"));
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
const content = req.body;
|
||||
|
||||
for (const key in content)
|
||||
if (key in original && content[key])
|
||||
original[key] = content[key];
|
||||
|
||||
fs.writeFileSync("./config.json", JSON.stringify(original,null,4));
|
||||
require.cache[require.resolve("../config.json")] = require("../config.json");
|
||||
res.redirect("/register");
|
||||
})
|
||||
|
||||
module.exports = app;
|
|
@ -1,36 +1,29 @@
|
|||
const { Router, request, response } = require("express");
|
||||
const { Router } = require("express");
|
||||
const app = Router();
|
||||
const fs = require("fs");
|
||||
const bcrypt = require("bcrypt");
|
||||
const { SecretModel, UserModel } = require("../../models");
|
||||
const { UserModel } = require("../../models");
|
||||
|
||||
/**
|
||||
* Auth checker
|
||||
* @param {request} req
|
||||
* @param {response} res
|
||||
*/
|
||||
|
||||
app.use(async (req, res, next) => {
|
||||
res.error = (status, error) => res.status(status).json({ error });
|
||||
|
||||
res.complate = result => res.status(200).json(result);
|
||||
|
||||
if (req.user) return next();
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader) return res.error(401, "No authorization header");
|
||||
const [username, password] = Buffer.from(authHeader.split(' ')[1], "base64").toString().split(":");
|
||||
const [name, password] = Buffer.from(authHeader.split(' ')[1], "base64").toString().split(":");
|
||||
|
||||
if (!username || !password)
|
||||
return res.error(401, "Authorise headers are missing")
|
||||
if (!name || !password)
|
||||
return res.error(400, "Authorise headers are not well formed");
|
||||
|
||||
const user = await SecretModel.findOne({ username });
|
||||
const user = await UserModel.findOne({ name });
|
||||
|
||||
if (!user)
|
||||
return res.error(401, `We don't have any thread with name ${username}.`)
|
||||
if (!user || user.deleted) return res.error(401, `We don't have any user with name ${name}.`)
|
||||
|
||||
if (!await bcrypt.compare(password, user.password)) return res.error(401, 'Incorrect Password!');
|
||||
|
||||
req.user = await UserModel.findOne({ name: req.headers.username });
|
||||
req.user = user;
|
||||
|
||||
next();
|
||||
});
|
||||
|
|
24
routes/api/routes/config.js
Normal file
24
routes/api/routes/config.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
const { Router } = require("express")
|
||||
const fs = require("fs");
|
||||
const app = Router();
|
||||
|
||||
app.use((req, res, next) => {
|
||||
if (!req.user.admin)
|
||||
return res.error(403, "You have not got permission for this.");
|
||||
next();
|
||||
});
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
try {
|
||||
return res.reply(JSON.parse(fs.readFileSync("./config.json", "utf8")));
|
||||
} catch (e) {
|
||||
res.error(500, e.message);
|
||||
}
|
||||
});
|
||||
app.post("/", (req, res) => {
|
||||
fs.writeFileSync("./config.json", JSON.stringify(req.body, null, 4));
|
||||
require.cache[require.resolve("../config.json")] = require("../../../config.json");
|
||||
res.complate(require("../../../config.json"));
|
||||
});
|
||||
|
||||
module.exports = app;
|
|
@ -18,18 +18,35 @@ app.param("id", async (req, res, next, id) => {
|
|||
|
||||
app.get("/:id", async (req, res) => res.complate(req.message));
|
||||
|
||||
app.delete("/:id/", async (req, res) => {
|
||||
|
||||
|
||||
const { message, user } = req;
|
||||
|
||||
if (user.id != message.authorID && !user.admin)
|
||||
return res.error(403, "You have not got permission for this.");
|
||||
if (message.deleted) return res.error(403, "This message is already deleted.");
|
||||
|
||||
message.deleted = true;
|
||||
await message.save()
|
||||
res.complate(message);
|
||||
|
||||
});
|
||||
|
||||
app.patch("/:id/", async (req, res) => {
|
||||
|
||||
|
||||
const { message, user } = req;
|
||||
|
||||
if (user.id !== message.authorID && !user.admin) return res.error(403, "You have not got permission for this.");
|
||||
const { content = null } = req.body;
|
||||
if (!content) return res.error(400, "Missing message content in request body.");
|
||||
if (!Object.values(req.body).some(Boolean)) return res.error(400, "Missing message informations for update in request body.");
|
||||
const { content, deleted } = req.body;
|
||||
|
||||
const limits = req.app.get("limits");
|
||||
if (content.length < 5 || content.length > limits.message) return res.error(400, `content must be between 5 - ${limits.message} characters`);
|
||||
|
||||
if (deleted === false) message.deleted = false;
|
||||
|
||||
message.content = content;
|
||||
|
||||
if (!message.oldContents.includes(content))
|
||||
|
@ -93,33 +110,4 @@ app.post("/:id/react/:type", async (req, res) => {
|
|||
|
||||
});
|
||||
|
||||
app.delete("/:id/", async (req, res) => {
|
||||
|
||||
|
||||
const { message, user } = req;
|
||||
|
||||
if (user.id != message.authorID && !user.admin)
|
||||
return res.error(403, "You have not got permission for this.");
|
||||
if (message.deleted) return res.error(403, "This message is already deleted.");
|
||||
|
||||
message.deleted = true;
|
||||
await message.save();
|
||||
res.complate(message);
|
||||
|
||||
})
|
||||
|
||||
app.post("/:id/undelete", async (req, res) => {
|
||||
|
||||
|
||||
const { message } = req;
|
||||
|
||||
if (!message.deleted) return res.error(404, "This message is not deleted, first, delete it.");
|
||||
|
||||
message.deleted = false;
|
||||
await message.save();
|
||||
|
||||
res.complate(message);
|
||||
|
||||
})
|
||||
|
||||
module.exports = app;
|
|
@ -1,11 +1,12 @@
|
|||
const { UserModel, SecretModel } = require("../../../models");
|
||||
const { UserModel, BanModel } = require("../../../models");
|
||||
const { Router } = require("express");
|
||||
const multer = require("multer");
|
||||
const { themes } = require("../../../lib");
|
||||
|
||||
const app = Router();
|
||||
|
||||
app.param("id", async (req, res, next, id) => {
|
||||
req.member = await UserModel.get(id, req.user.admin ? "+lastSeen" : "");
|
||||
req.member = await UserModel.get(id, req.user.admin ? "+lastSeen +ips" : "");
|
||||
|
||||
if (!req.member) return res.error(404, `We don't have any user with id ${id}.`);
|
||||
|
||||
|
@ -17,37 +18,21 @@ app.param("id", async (req, res, next, id) => {
|
|||
|
||||
app.get("/:id", async (req, res) => res.complate(req.member));
|
||||
|
||||
app.delete("/:id/", async (req, res) => {
|
||||
app.delete("/:id", async (req, res) => {
|
||||
const { user, member } = req;
|
||||
|
||||
if (!user.admin)
|
||||
return res.error(403, "You have not got permission for this.");
|
||||
|
||||
const { id = null } = req.params;
|
||||
|
||||
if (member.deleted) return res.error(404, `This user is with id ${id} already deleted.`);
|
||||
if (member.deleted) return res.error(404, `This user is with id ${member.id} already deleted.`);
|
||||
|
||||
member.deleted = true;
|
||||
await member.save();
|
||||
|
||||
res.complate(member);
|
||||
});
|
||||
app.post("/:id/undelete/", async (req, res) => {
|
||||
if (!req.user.admin) return res.error(403, "You have not got permission for this.");
|
||||
|
||||
const { user, member } = req;
|
||||
|
||||
if (!member) return res.error(404, `We don't have any user with id ${req.params.id}.`);
|
||||
|
||||
if (!member.deleted) return res.error(404, "This user is not deleted, first, delete it.");
|
||||
|
||||
member.deleted = false;
|
||||
|
||||
res.complate(await member.save());
|
||||
|
||||
})
|
||||
|
||||
app.patch("/:id/", async (req, res) => {
|
||||
app.patch("/:id", async (req, res) => {
|
||||
const { user, member } = req;
|
||||
|
||||
if (req.user.id !== member.id && !user.admin) return res.error(403, "You have not got permission for this.");
|
||||
|
@ -59,9 +44,7 @@ app.patch("/:id/", async (req, res) => {
|
|||
const { names, desp } = req.app.get("limits");
|
||||
|
||||
if (name) {
|
||||
|
||||
if (name.length < 3 || names > 25) return res.error(400, `Username must be between 3 - ${names} characters`);
|
||||
await SecretModel.updateOne({ id: member.id }, { username: name });
|
||||
member.name = name;
|
||||
}
|
||||
|
||||
|
@ -69,7 +52,7 @@ app.patch("/:id/", async (req, res) => {
|
|||
if (about.length > desp) return res.error(400, `About must be under ${desp} characters`);
|
||||
member.about = about;
|
||||
}
|
||||
if (theme || ["default", "black"].includes(theme)) member.theme = theme;
|
||||
if (theme || themes.includes(theme)) member.theme = theme;
|
||||
|
||||
if (typeof admin === "boolean" || ["false", "true"].includes(admin)) member.admin = admin;
|
||||
if (deleted === false) member.deleted = false;
|
||||
|
@ -78,6 +61,22 @@ app.patch("/:id/", async (req, res) => {
|
|||
res.complate(await member.save());
|
||||
|
||||
})
|
||||
|
||||
app.post("/:id/ban", async (req, res) => {
|
||||
if (!req.user.admin) return res.error(403, "You have not got permission for this.");
|
||||
const { member } = req;
|
||||
for (const ip of member.ips)
|
||||
try {
|
||||
await BanModel.create({ ip, reason: `Ban for ${member.name}`, authorID: req.user.id });
|
||||
req.app.ips.push(ip);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
res.complate(member);
|
||||
});
|
||||
|
||||
|
||||
const storage = multer.diskStorage({
|
||||
destination: function (_req, _file, cb) {
|
||||
cb(null, './public/images/avatars')
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { UserModel, SecretModel } = require("../models");
|
||||
const { UserModel } = require("../models");
|
||||
const { Router } = require("express");
|
||||
const app = Router();
|
||||
const bcrypt = require("bcrypt");
|
||||
|
@ -8,19 +8,15 @@ app.get("/", (req, res) => res.reply("login", { redirect: req.query.redirect, us
|
|||
app.post("/", async (req, res) => {
|
||||
req.session.userID = null;
|
||||
|
||||
const { username = null, password = null } = req.body;
|
||||
const { name, password } = req.body;
|
||||
|
||||
if (!username || !password)
|
||||
return res.error(400, "You forgot entering some values")
|
||||
if (!name || !password) return res.error(400, "You forgot entering some values")
|
||||
|
||||
const user = await SecretModel.findOne({ username });
|
||||
if (!user) return res.error(403, 'Incorrect Username and/or Password!');
|
||||
const member = await UserModel.findOne({ name }, "+password");
|
||||
if (!member || member.deleted) return res.error(403, 'Incorrect username!');
|
||||
if (!await bcrypt.compare(password, member.password)) return res.error(403, 'Incorrect password!');
|
||||
|
||||
if (!await bcrypt.compare(password, user.password)) return res.error(403, 'Incorrect Password!')
|
||||
const member = await UserModel.findOne({ name: username });
|
||||
if (!member || member.deleted) return res.error(403, 'Incorrect Username and/or Password!')
|
||||
|
||||
req.session.userID = user.id;
|
||||
req.session.userID = member.id;
|
||||
|
||||
res.redirect(req.query.redirect || '/');
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { UserModel, SecretModel } = require("../models");
|
||||
const { UserModel } = require("../models");
|
||||
const { Router } = require("express")
|
||||
const bcrypt = require("bcrypt");
|
||||
const { RL } = require('../lib');
|
||||
|
@ -10,37 +10,31 @@ app.post("/", RL(24 * 60 * 60_000, 5), async (req, res) => {
|
|||
|
||||
req.session.userID = null;
|
||||
|
||||
let { username, password: body_pass, about } = req.body;
|
||||
let { name, password, about } = req.body;
|
||||
|
||||
if (!username || !body_pass) return res.error(400, "You forgot entering some values");
|
||||
if (!name || !password) return res.error(400, "You forgot entering some values");
|
||||
const { names } = req.app.get("limits");
|
||||
if (username.length < 3 || names > 25) return res.error(400, "Username must be between 3 - 25 characters");
|
||||
if (body_pass.length < 3 || names > 25) return res.error(400, "Password must be between 3 - 25 characters");
|
||||
if (name.length < 3 || names > 25) return res.error(400, "Name must be between 3 - 25 characters");
|
||||
if (password.length < 3 || names > 25) return res.error(400, "Password must be between 3 - 25 characters");
|
||||
|
||||
const user = await SecretModel.findOne({ username });
|
||||
|
||||
if (user) return res.error(400, `We have got an user named ${username}!`)
|
||||
|
||||
const user2 = new UserModel({ name: username })
|
||||
if (await UserModel.exists({ name })) return res.error(400, `We have got an user named ${name}!`)
|
||||
const user = new UserModel({ name });
|
||||
|
||||
if (about) {
|
||||
if (about.length > 256) return res.error(400, "about must be under 256 characters");
|
||||
user2.about = about;
|
||||
user.about = about;
|
||||
}
|
||||
|
||||
await user2.takeId()
|
||||
await user2.save();
|
||||
await user.takeId()
|
||||
if (user.id === "0") user.admin = true;
|
||||
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
const password = await bcrypt.hash(body_pass, salt);
|
||||
await SecretModel.create({ username, password, id: user2.id })
|
||||
req.session.userID = user2.id;
|
||||
user.password = await bcrypt.hash(password, await bcrypt.genSalt(10));
|
||||
await user.save();
|
||||
|
||||
req.session.userID = user.id;
|
||||
|
||||
res.redirect('/');
|
||||
|
||||
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
module.exports = app;
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<form action="/login?redirect=<%= redirect !== "/register" ? redirect : "/" %>" method="post">
|
||||
|
||||
<input type="text" name="username" placeholder="Username" class="input" required>
|
||||
<input type="text" name="name" placeholder="Username" class="input" required>
|
||||
<input type="password" name="password" placeholder="Password" class="input" required>
|
||||
<input type="submit" style="width:100%" class="btn-primary" value="Login">
|
||||
</form>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
|
||||
<form action="/register" method="post">
|
||||
<input type="text" name="username" placeholder="Username" class="input" required>
|
||||
<input type="text" name="name" placeholder="Username" class="input" required>
|
||||
<input type="password" name="password" placeholder="Password" class="input" required>
|
||||
<textarea class="input" name="about" rows="4" placeholder="About you... You can use markdown"></textarea>
|
||||
<input type="submit" class="btn-primary" style="width:100%;" value="Register">
|
||||
|
|
34
views/setup.ejs
Normal file
34
views/setup.ejs
Normal file
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<%- include("extra/meta", {title: "Setup the Akf-forum!" }) %>
|
||||
|
||||
|
||||
<body style="text-align: center;">
|
||||
<%- include("extra/navbar") %>
|
||||
<link rel="stylesheet" href="/css/setup.css">
|
||||
<h1 style="color: var(--main);">Setup</h1>
|
||||
<h2 style="color: var(--second);">There is default settings for akf-forum, you not need to edit them, but you can! And, the first registered user will be admin.</h2>
|
||||
<form method="POST">
|
||||
Default theme:
|
||||
<input type="text" name="def_theme" value="default" required>
|
||||
Forum name:
|
||||
<input type="text" name="forum_name" value="akf" required>
|
||||
Forum description:
|
||||
<input type="text" name="description" value="Akf-forum!" required>
|
||||
Default state for new threads, change with "APPROVAL" for approval system:
|
||||
<input type="text" name="defaultThreadState" value="OPEN" required>
|
||||
Domain of the forum, defaulty setted:
|
||||
<input type="text" name="host" id="domain" value="Akf-forum!" required>
|
||||
<hr>
|
||||
(Optional) Discord app ID for Discord login:
|
||||
<input type="text" name="discord_auth">
|
||||
|
||||
<input type="submit" class="btn-primary" value="Setup">
|
||||
</form>
|
||||
<script>
|
||||
document.getElementById("domain").value = location.origin;
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -78,11 +78,15 @@
|
|||
<% } %>
|
||||
|
||||
<div class="reactions">
|
||||
<div>
|
||||
<div <% if (message.react.like.includes(user?.id)) { %>
|
||||
style="color: var(--main)"
|
||||
<% } %> >
|
||||
<i onclick='react("<%= message.id %>","like");' class='bx bx-like'></i>
|
||||
<div id="like"><%=message.react.like.length %></div>
|
||||
</div>
|
||||
<div>
|
||||
<div <% if (message.react.dislike.includes(user?.id)) { %>
|
||||
style="color: var(--main)"
|
||||
<% } %>>
|
||||
<i onclick='react("<%= message.id %>","dislike");' class='bx bx-dislike'></i>
|
||||
<div id="dislike"><%=message.react.dislike.length %></div>
|
||||
</div>
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
alert("User is deleted!");
|
||||
location.reload()
|
||||
} else if (e.target.id == "undelete") {
|
||||
const response = await request("/api/users/<%= member.id %>/undelete");
|
||||
const response = await request("/api/users/<%= member.id %>/", "PATCH", { deleted: false });
|
||||
if (response.deleted) return;
|
||||
alert("User is undeleted successfully!");
|
||||
location.reload()
|
||||
|
|
Loading…
Reference in a new issue