Added state for users.

This commit is contained in:
Akif9748 2022-10-09 21:23:31 +03:00
parent a471f19f04
commit 410859fbe3
9 changed files with 43 additions and 29 deletions

View File

@ -20,7 +20,7 @@ Create a redirect url in discord developer portal:
### EMAIL AUTH: ### EMAIL AUTH:
You can configure it. Just edit `config.json` and `.env` files. You can configure it. Just edit `config.json` and `.env` files.
`"email_auth": true` in config.json. `"email_auth": true, "default_user_state": "APPROVAL"` in config.json.
Add your email credentials to `.env` as `EMAIL_USER` and `EMAIL_PASS`. Add your email credentials to `.env` as `EMAIL_USER` and `EMAIL_PASS`.
Add your email domain to `.env` as `EMAIL_SERVICE`. Add your email domain to `.env` as `EMAIL_SERVICE`.
@ -60,9 +60,7 @@ Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn
- user.state for ban, delete, etc. - user.state for ban, delete, etc.
- Add a feature list to README.md - Add a feature list to README.md
- delete admin??? - delete admin???
- MODALS'S CSS & JS
- change category name - change category name
- click to user message count to view message W/search
## Major Version History ## Major Version History
- V4: Caching - V4: Caching

View File

@ -16,5 +16,6 @@
"discord_auth": "", "discord_auth": "",
"defaultThreadState": "OPEN", "defaultThreadState": "OPEN",
"email_auth": false, "email_auth": false,
"default_user_state": "ACTIVE",
"host": "https://akf-forum.glitch.me" "host": "https://akf-forum.glitch.me"
} }

View File

@ -35,12 +35,13 @@ app.use(express.static("public"), express.json(), express.urlencoded({ extended:
res.error = (type, error) => res.reply("error", { type, error }, type); res.error = (type, error) => res.reply("error", { type, error }, type);
if (req.user && !req.user.approved&& !req.user.admin && !req.url.startsWith("/auth/email")) return res.error(403, "Your account is not approved yet.");
if (req.user?.deleted) { if (req.user?.deleted) {
req.session.destroy(); req.session.destroy();
return res.error(403, "Your account has been deleted."); return res.error(403, "Your account has been deleted.");
} }
if (req.user && req.user.state == "APPROVAL" && !req.user.admin && !req.url.startsWith("/auth/email")) return res.error(403, "Your account is not approved yet.");
next(); next();
} }
); );

1
lib.js
View File

@ -4,6 +4,7 @@ const config = require("./config.json");
require("dotenv").config(); require("dotenv").config();
module.exports = { module.exports = {
threadEnum: ["OPEN", "APPROVAL", "DELETED"], threadEnum: ["OPEN", "APPROVAL", "DELETED"],
userEnum: ["ACTIVE", "APPROVAL", "DELETED", "BANNED"],
themes: ["default", "black"], themes: ["default", "black"],
RL(windowMs = 60_000, max = 1) { RL(windowMs = 60_000, max = 1) {
return RL({ return RL({

View File

@ -22,7 +22,7 @@ const schema = new mongoose.Schema({
time: { type: Date, default: Date.now }, time: { type: Date, default: Date.now },
edited: { type: Boolean, default: false }, edited: { type: Boolean, default: false },
state: { type: String, default: defaultThreadState, enum: threadEnum }, state: { type: String, default: defaultThreadState, enum: threadEnum, uppercase: true },
messages: [String], messages: [String],
views: { type: Number, default: 0 } views: { type: Number, default: 0 }
}); });

View File

@ -1,12 +1,13 @@
const mongoose = require("mongoose") const mongoose = require("mongoose")
const { def_theme, limits, email_auth } = require("../config.json"); const { def_theme, limits, email_auth, default_user_state } = require("../config.json");
const { userEnum } = require("../lib");
const schema = new mongoose.Schema({ const schema = new mongoose.Schema({
id: { type: String, unique: true }, id: { type: String, unique: true },
discordID: { type: String }, discordID: { type: String },
name: { type: String, maxlength: limits.names }, name: { type: String, maxlength: limits.names },
avatar: { type: String, default: "/images/avatars/default.jpg" }, avatar: { type: String, default: "/images/avatars/default.jpg" },
time: { type: Date, default: Date.now }, time: { type: Date, default: Date.now },
deleted: { type: Boolean, default: false },
edited: { type: Boolean, default: false }, edited: { type: Boolean, default: false },
about: { type: String, default: "", maxlength: limits.desp }, about: { type: String, default: "", maxlength: limits.desp },
admin: { type: Boolean, default: false }, admin: { type: Boolean, default: false },
@ -16,11 +17,20 @@ const schema = new mongoose.Schema({
ips: { type: [String], default: [], select: false }, ips: { type: [String], default: [], select: false },
password: { type: String, select: false }, password: { type: String, select: false },
discord_code: { type: String, select: false }, discord_code: { type: String, select: false },
approved: { type: Boolean, default: !email_auth }, state: { type: String, default: default_user_state, enum: userEnum, uppercase: true },
email: { type: String, select: false }, email: { type: String, select: false },
email_code: { type: String, select: false }, email_code: { type: String, select: false },
}); });
schema.virtual("deleted").get(function () {
return this.state === "DELETED";
}).set(function (value) {
this.set({ state: value ? "DELETED" : "ACTIVE" });
});
schema.virtual("active").get(function () {
return this.state === "ACTIVE";
})
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;
@ -32,6 +42,6 @@ schema.methods.getLink = function (id = this.id) {
const model = mongoose.model('user', schema); const model = mongoose.model('user', schema);
model.get = (id, select) => model.findOne({ id }).select(select); model.get = (id, select = "") => model.findOne({ id }, select);
module.exports = model; module.exports = model;

View File

@ -20,7 +20,7 @@ app.use(async (req, res, next) => {
const user = await UserModel.findOne({ name }); const user = await UserModel.findOne({ name });
if (!user || user.deleted) return res.error(401, `We don't have any user with name ${name}.`) if (!user || user.deleted) return res.error(401, `We don't have any user with name ${name}.`)
if (!user.approved) return res.error(401, "Your account is not approved yet."); if (!user.active) return res.error(401, "Your account is not approved yet.");
if (!await bcrypt.compare(password, user.password)) return res.error(401, 'Incorrect Password!'); if (!await bcrypt.compare(password, user.password)) return res.error(401, 'Incorrect Password!');

View File

@ -90,7 +90,7 @@ app.get("/email", async (req, res) => {
const { code } = req.query; const { code } = req.query;
if (!code) return res.error(400, "No code provided"); if (!code) return res.error(400, "No code provided");
if (code !== req.user.email_code) return res.error(403, "Invalid code"); if (code !== req.user.email_code) return res.error(403, "Invalid code");
req.user.approved = true; req.user.state = "ACTIVE";
await req.user.save(); await req.user.save();
res.send("Your email has been linked to your forum account."); res.send("Your email has been linked to your forum account.");
}); });

View File

@ -16,7 +16,7 @@
<% if (!member.discordID && discord && user?.id === member.id) { %> <% if (!member.discordID && discord && user?.id === member.id) { %>
<a href="<%=discord%>" class="btn-outline-primary">Auth with discord</a> <a href="<%=discord%>" class="btn-outline-primary">Auth with discord</a>
<% } else if(member.discordID && user?.id === member.id) { %> <% } else if(member.discordID && user?.id === member.id) { %>
<a class="btn-outline-primary" id="un_discord">Unauth with discord!</a> <a class="btn-outline-primary" id="un_discord">Unauth with discord!</a>
<% } %> <% } %>
<% if (user?.admin || user?.id === member.id) { %> <% if (user?.admin || user?.id === member.id) { %>
<a href="/users/<%=member.id%>/avatar" class="btn-outline-primary">Upload avatar</a> <a href="/users/<%=member.id%>/avatar" class="btn-outline-primary">Upload avatar</a>
@ -32,7 +32,7 @@
<h1 class="title" style="text-align:center;">Edit <a class="see" href="/users/<%= member.id %>"><%= member.name %></a></h1> <h1 class="title" style="text-align:center;">Edit <a class="see" href="/users/<%= member.id %>"><%= member.name %></a></h1>
<div class="content"> <div class="content">
<form id="form" class="see" style="box-shadow:none"> <form id="form" class="see" style="box-shadow:none">
<input type="text" name="name" placeholder="<%=member.name%>" class="input" > <input type="text" name="name" placeholder="<%=member.name%>" class="input">
<textarea class="input" name="about" rows="4" cols="60" name="content" placeholder="<%=member.about%>"></textarea> <textarea class="input" name="about" rows="4" cols="60" name="content" placeholder="<%=member.about%>"></textarea>
<% if (user?.admin){ %> <% if (user?.admin){ %>
@ -56,20 +56,23 @@
document.addEventListener("click", async e => { document.addEventListener("click", async e => {
if (e.target.id == "delete") { if (e.target.id == "delete") {
const response = await request("/api/users/<%= member.id %>", "DELETE"); const response = await request("/api/users/<%= member.id %>", "DELETE");
if (!response.deleted) return if (response.state !== "DELETED") return
alert("User is deleted!"); alert("User is deleted!");
location.reload() location.reload()
} else if (e.target.id == "undelete") { } else if (e.target.id == "undelete") {
const response = await request("/api/users/<%= member.id %>/", "PATCH", { deleted: false }); const response = await request("/api/users/<%= member.id %>/", "PATCH", {
if (response.deleted) return; deleted: false
});
if (response.state == "DELETED") return;
alert("User is undeleted successfully!"); alert("User is undeleted successfully!");
location.reload() location.reload()
} else if (e.target.id == "un_discord") { } else if (e.target.id == "un_discord") {
const response = await fetch("/auth/discord/", {method:"DELETE"}); const response = await fetch("/auth/discord/", {
method: "DELETE"
});
alert(await response.text()); alert(await response.text());
location.reload() location.reload()
} } else if (e.target.id == "toogle")
else if (e.target.id == "toogle")
document.getElementById('user-edit').classList.toggle('no-active') document.getElementById('user-edit').classList.toggle('no-active')
}); });
@ -94,15 +97,15 @@
<a id="delete" class="btn-outline-primary">Delete user! </a> <a id="delete" class="btn-outline-primary">Delete user! </a>
<% } %> <% } %>
<% if (user?.admin) {%> <% if (user?.admin) {%>
<details> <details>
<summary class="btn-outline-primary">IP adresses of user:</summary> <summary class="btn-outline-primary">IP adresses of user:</summary>
<select> <select>
<% for(const ip of member.ips) { %> <% for(const ip of member.ips) { %>
<option><%= ip %></option> <option><%= ip %></option>
<% } %> <% } %>
</select> </select>
</details> </details>
<% } %> <% } %>
<div class="box" style="justify-content:center;"> <div class="box" style="justify-content:center;">