mirror of
https://github.com/Akif9748/akf-forum.git
synced 2024-11-26 13:20:41 +03:00
V3
This commit is contained in:
parent
0d158c4fef
commit
aa33c97db3
11 changed files with 108 additions and 68 deletions
|
@ -20,15 +20,15 @@ And, you can learn about API in `util/APIDOCS.md`.
|
||||||
* [Camroku](https://github.com/Camroku) - Made stylesheets
|
* [Camroku](https://github.com/Camroku) - Made stylesheets
|
||||||
|
|
||||||
## To do (Backend, bug fixes)
|
## To do (Backend, bug fixes)
|
||||||
- `/errors/error` will ~~change~~ deprecate, it will be in res.error . And we will use "alert" for errors with fetch api.
|
- `/errors/error` will ~~change~~ deprecate, it will be in res.error . And we will use "alert" for errors with fetch api. this added for messages and reactions...
|
||||||
- message.js/12, so, admin perms,(req.user?.admin || !thread.deleted), and api in message.
|
- message.js/12, so, admin perms,(req.user?.admin || !thread.deleted), and api in message.
|
||||||
- the forum will only use api path...
|
- the forum will only use api path... this added for messages and reactions...
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
### User
|
### User
|
||||||
| To do | Is done? | Priority |
|
| To do | Is done? | Priority |
|
||||||
| ----- | -------- | -------- |
|
| ----- | -------- | -------- |
|
||||||
| Login | 🟢 | HIGH |
|
| Login via redirect query | 🟢 | HIGH |
|
||||||
| Register | 🟢 | HIGH |
|
| Register | 🟢 | HIGH |
|
||||||
| Logout | 🟢 | HIGH |
|
| Logout | 🟢 | HIGH |
|
||||||
| Admin | 🟢 | HIGH |
|
| Admin | 🟢 | HIGH |
|
||||||
|
|
2
index.js
2
index.js
|
@ -22,4 +22,4 @@ for (const file of fs.readdirSync("./routes"))
|
||||||
|
|
||||||
app.all("*", (req, res) => error(res, 404, "We have not got this page."));
|
app.all("*", (req, res) => error(res, 404, "We have not got this page."));
|
||||||
|
|
||||||
app.listen(port, () => console.log("Akf-forum on port:", port));
|
app.listen(port, () => console.log("akf-forum on port:", port));
|
44
public/js/send.js
Normal file
44
public/js/send.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
document.getElementById("send").addEventListener("submit", async e => {
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
const form = e.target;
|
||||||
|
const data = new FormData(form);
|
||||||
|
fetch("/api/messages", {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ threadID: data.get("threadID"), content: data.get("content") }),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
}).then(res => res.json())
|
||||||
|
.then(res => {
|
||||||
|
if (res.result.error) return alert(res.result.error);
|
||||||
|
|
||||||
|
form.reset();
|
||||||
|
const message = res.result;
|
||||||
|
document.getElementById("messages").innerHTML += `<br>
|
||||||
|
<div id="message-${message.id}" style="border: 2px solid #444444; padding: 10px;">
|
||||||
|
|
||||||
|
<h3 style="float:right;">${new Date(message.time).toLocaleString()}</h3>
|
||||||
|
|
||||||
|
<h2>
|
||||||
|
<img class="yuvarlak" src=${message.author.avatar} alt=${message.author.name}>
|
||||||
|
<a href="/users/${message.author.id}"> ${message.author.name}</a>:
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<h2>${message.content}</h2><br>
|
||||||
|
|
||||||
|
<form style="display:inline;" action="/message/${message.id}/delete/" method="post">
|
||||||
|
</a><button type="submit">DELETE</button>
|
||||||
|
</form>
|
||||||
|
<div style="float: right;">
|
||||||
|
<h3 id="count${message.id}" style="display:inline;">0</h3>
|
||||||
|
<button style="display:inline;" id="like" value="${message.id}">+🔼</button>
|
||||||
|
<button style="display:inline;" id="dislike" value="${message.id}" >-🔽</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
}).catch(err => {
|
||||||
|
alert(err);
|
||||||
|
});
|
||||||
|
});
|
|
@ -38,6 +38,7 @@ app.use(async (req, res, next) => {
|
||||||
|
|
||||||
res.complate = result => res.status(200).json({ status: 200, result });
|
res.complate = result => res.status(200).json({ status: 200, result });
|
||||||
|
|
||||||
|
if (req.user) return next();
|
||||||
const { username = null, password = null } = req.headers;
|
const { username = null, password = null } = req.headers;
|
||||||
|
|
||||||
if (!username || !password)
|
if (!username || !password)
|
||||||
|
|
|
@ -40,5 +40,23 @@ app.post("/", rateLimit({
|
||||||
res.complate(message);
|
res.complate(message);
|
||||||
|
|
||||||
})
|
})
|
||||||
|
app.post("/:id/react/:type", async (req, res) => {
|
||||||
|
|
||||||
|
const message = await MessageModel.get(req.params.id);
|
||||||
|
if (message) {
|
||||||
|
|
||||||
|
if (req.user.id in message.react)
|
||||||
|
delete message.react[req.session.userid];
|
||||||
|
else
|
||||||
|
message.react[req.session.userid] = req.params.type === "like";
|
||||||
|
message.markModified("react");
|
||||||
|
await message.save();
|
||||||
|
|
||||||
|
const arr = Object.values(message.react)
|
||||||
|
res.complate(arr.filter(Boolean).length - arr.filter(x => !x).length)
|
||||||
|
} else error(res, 404, "We have not got this Message for reacting.");
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
|
@ -4,7 +4,7 @@ const error = require("../errors/error");
|
||||||
const app = Router();
|
const app = Router();
|
||||||
const bcrypt = require("bcrypt");
|
const bcrypt = require("bcrypt");
|
||||||
|
|
||||||
app.get("/", (req, res) => res.render("login"));
|
app.get("/", (req, res) => res.render("login",{redirect: req.query.redirect}));
|
||||||
|
|
||||||
app.post("/", async (req, res) => {
|
app.post("/", async (req, res) => {
|
||||||
req.session.userid = null;
|
req.session.userid = null;
|
||||||
|
@ -23,7 +23,7 @@ app.post("/", async (req, res) => {
|
||||||
|
|
||||||
req.session.userid = user.id;
|
req.session.userid = user.id;
|
||||||
|
|
||||||
res.redirect('/');
|
res.redirect( req.query.redirect || '/');
|
||||||
} else
|
} else
|
||||||
error(res, 403, 'Incorrect Username and/or Password!')
|
error(res, 403, 'Incorrect Username and/or Password!')
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
const { ThreadModel, MessageModel } = require("../models");
|
const { MessageModel } = require("../models");
|
||||||
const error = require("../errors/error")
|
const error = require("../errors/error")
|
||||||
const rateLimit = require('express-rate-limit')
|
|
||||||
|
|
||||||
const { Router } = require("express");
|
const { Router } = require("express");
|
||||||
|
|
||||||
|
@ -16,28 +15,6 @@ app.get("/:id", async (req, res) => {
|
||||||
|
|
||||||
app.use(require("../middlewares/login"));
|
app.use(require("../middlewares/login"));
|
||||||
|
|
||||||
app.post("/", rateLimit({
|
|
||||||
windowMs: 60_000, max: 1, standardHeaders: true, legacyHeaders: false,
|
|
||||||
handler: (request, response, next, options) =>
|
|
||||||
!request.user.admin ?
|
|
||||||
error(response, options.statusCode, "You are begin ratelimited")
|
|
||||||
: next()
|
|
||||||
}), async (req, res) => {
|
|
||||||
|
|
||||||
const thread = await ThreadModel.get(req.body.threadID);
|
|
||||||
if (thread) {
|
|
||||||
const message = await new MessageModel({ content: req.body.content, author: req.user, threadID: thread.id }).takeId();
|
|
||||||
await message.save();
|
|
||||||
await thread.push(message.id).save();
|
|
||||||
|
|
||||||
res.redirect('/threads/' + req.body.threadID);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
error(res, 404, "We have not got this thread.");
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/:id/delete", async (req, res) => {
|
app.post("/:id/delete", async (req, res) => {
|
||||||
const message = await MessageModel.get(req.params.id);
|
const message = await MessageModel.get(req.params.id);
|
||||||
if (!message || message.deleted) return error(res, 404, "We have not got any message declared as this id.");
|
if (!message || message.deleted) return error(res, 404, "We have not got any message declared as this id.");
|
||||||
|
@ -51,23 +28,6 @@ app.post("/:id/delete", async (req, res) => {
|
||||||
res.status(200).redirect("/threads/" + message.threadID);
|
res.status(200).redirect("/threads/" + message.threadID);
|
||||||
|
|
||||||
})
|
})
|
||||||
app.post("/:id/react", async (req, res) => {
|
|
||||||
const info = req.body;
|
|
||||||
const message = await MessageModel.get(req.params.id);
|
|
||||||
if (message) {
|
|
||||||
if (req.user.id in message.react)
|
|
||||||
delete message.react[req.session.userid];
|
|
||||||
else
|
|
||||||
message.react[req.session.userid] = "like" in info;
|
|
||||||
|
|
||||||
|
|
||||||
await message.save();
|
|
||||||
res.redirect("/threads/" + message.threadID);
|
|
||||||
} else error(res, 404, "We have not got this Message for reacting.");
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,9 @@ app.get("/:id", async (req, res) => {
|
||||||
|
|
||||||
const messages = await Promise.all(thread.messages.map(async id => {
|
const messages = await Promise.all(thread.messages.map(async id => {
|
||||||
const message = await MessageModel.get(id)
|
const message = await MessageModel.get(id)
|
||||||
|
const arr = Object.values(message.react)
|
||||||
|
message.reactCount = arr.filter(Boolean).length - arr.filter(x => !x).length;
|
||||||
|
|
||||||
return user?.admin || !message?.deleted ? message : null;
|
return user?.admin || !message?.deleted ? message : null;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,10 @@
|
||||||
|
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
<a style="float: right; background-color: #5F875F;" href="/register">REGISTER</a>
|
<a style="float: right; background-color: #5F875F;" href="/register">REGISTER</a>
|
||||||
|
<a id="login" style="float: right; background-color:#5F87AF; " href="/login">LOGIN</a>
|
||||||
<a style="float: right; background-color:#5F87AF; " href="/login">LOGIN</a>
|
<script>
|
||||||
|
document.getElementById("login").href += "?redirect="+window.location.pathname
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
<h1>Login</h1>
|
<h1>Login</h1>
|
||||||
<hr>
|
<hr>
|
||||||
<form action="/login" method="post">
|
<form action="/login?redirect=<%= redirect %>" method="post">
|
||||||
<input type="text" name="username" placeholder="Username" id="username" required>
|
<input type="text" name="username" placeholder="Username" id="username" required>
|
||||||
<input type="password" name="password" placeholder="Password" id="password" required>
|
<input type="password" name="password" placeholder="Password" id="password" required>
|
||||||
<input type="submit" value="Login">
|
<input type="submit" value="Login">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<%- include("extra/header", { title : thread.title }) %>
|
<%- include("extra/header", { title: thread.title }) %>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@
|
||||||
|
|
||||||
<% }; %>
|
<% }; %>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
<div id="messages">
|
||||||
<% messages.forEach(message=>{ %>
|
<% messages.forEach(message=>{ %>
|
||||||
<% if (message){ %>
|
<% if (message){ %>
|
||||||
|
|
||||||
|
@ -50,18 +51,13 @@
|
||||||
<form style="display:inline;" action="/message/<%= message.id %>/delete/" method="post">
|
<form style="display:inline;" action="/message/<%= message.id %>/delete/" method="post">
|
||||||
</a><button type="submit">DELETE</button>
|
</a><button type="submit">DELETE</button>
|
||||||
</form>
|
</form>
|
||||||
<form style="float: right;" action="/message/<%= message.id %>/react/" method="POST">
|
<div style="float: right;">
|
||||||
|
<h3 id="count<%= message.id %>" style="display:inline;"><%= message.reactCount %></h3>
|
||||||
|
<button style="display:inline;" id="like" value="<%= message.id %>">+🔼</button>
|
||||||
|
<button style="display:inline;" id="dislike" value="<%= message.id %>" >-🔽</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3 style="display:inline;">
|
|
||||||
<%= Object.values(message.react).filter(Boolean).length - Object.values(message.react).filter(x=>
|
|
||||||
!x).length %>
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
|
|
||||||
<button style="display:inline;" name="like" type="submit">+🔼</button>
|
|
||||||
<button style="display:inline;" name="dislike" type="submit">-🔽</button>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,22 +72,38 @@
|
||||||
|
|
||||||
<% }); %>
|
<% }); %>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener("click", async e=>{
|
||||||
|
if (!e.target.id.includes("like"))return;
|
||||||
|
const res = await fetch("/api/messages/"+e.target.value+"/react/" + e.target.id, { method: 'POST' })
|
||||||
|
.then(res=>res.json());
|
||||||
|
document.getElementById("count"+e.target.value).innerHTML = res.result;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
|
||||||
<form action="/message" method="POST">
|
<form id= "send">
|
||||||
<textarea rows="4" cols="133" name="content"></textarea>
|
<textarea rows="4" cols="133" name="content"></textarea>
|
||||||
<input name="threadID" type="hidden" value="<%= thread.id %>"></input>
|
<input name="threadID" type="hidden" value="<%= thread.id %>"></input>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<% if (user){ %>
|
<% if (user){ %>
|
||||||
<button type="submit">Send!</button>
|
<button type="submit">Send!</button>
|
||||||
<%} else {%>
|
<%} else {%>
|
||||||
<button disabled>Login for send</button>
|
<button disabled>Login for send</button>
|
||||||
<% }%>
|
<% }%>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
<% if (user){ %>
|
||||||
|
<script src="/js/send.js"></script>
|
||||||
|
<% }%>
|
||||||
|
|
||||||
<%- include("extra/footer") %>
|
<%- include("extra/footer") %>
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in a new issue