mirror of
https://github.com/Akif9748/akf-forum.git
synced 2024-11-22 20:10:40 +03:00
Pages for threads and users pages, and lib/
This commit is contained in:
parent
3a4390074e
commit
ca66d44825
12 changed files with 133 additions and 83 deletions
|
@ -38,12 +38,10 @@ Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn
|
||||||
| Profile Message | 🔴 | LOW |
|
| Profile Message | 🔴 | LOW |
|
||||||
| Search | 🔴 | MEDIUM |
|
| Search | 🔴 | MEDIUM |
|
||||||
| Footer | 🟡 | LOW |
|
| Footer | 🟡 | LOW |
|
||||||
|
| Better Auth | 🔴 | MEDIUM |
|
||||||
|
| Local pfp store | 🔴 | MEDIUM |
|
||||||
|
| IPs of users will add SecretModel | 🔴 | MEDIUM |
|
||||||
|
|
||||||
- Better Auth
|
|
||||||
- Profile photos will store in a folder
|
|
||||||
- replacer function global
|
|
||||||
- page for threads - users
|
|
||||||
- IPs of users will add SecretModel
|
|
||||||
- message counts for API
|
- message counts for API
|
||||||
- better theme patch UserModel
|
- better theme patch UserModel
|
||||||
- ajax, delete update thread dom
|
- ajax, delete update thread dom
|
||||||
|
|
11
lib/index.js
Normal file
11
lib/index.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
URLRegex: /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g,
|
||||||
|
clearContent: (content) => {
|
||||||
|
if (!content) return "";
|
||||||
|
return content.replaceAll("&", "&")
|
||||||
|
.replaceAll("<", "<").replaceAll(">", ">")
|
||||||
|
.replaceAll("\"", """).replaceAll("'", "'")
|
||||||
|
.replaceAll("\n", "<br>");
|
||||||
|
}
|
||||||
|
}
|
41
public/css/pages.css
Normal file
41
public/css/pages.css
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
.pagination {
|
||||||
|
box-shadow: 0 0 5px 0 var(--box-shadow);
|
||||||
|
margin: 10px auto;
|
||||||
|
padding: 8px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
max-width: 400px;
|
||||||
|
gap: 10px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination .back,
|
||||||
|
.pagination .after {
|
||||||
|
color: var(--second);
|
||||||
|
font-size: 26px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.pagination .numbers {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination .number {
|
||||||
|
color: var(--second);
|
||||||
|
font-size: 22px;
|
||||||
|
border: 0 0 5px var(--second);
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination .number.active {
|
||||||
|
color: var(--main);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
|
@ -67,50 +67,6 @@
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.pagination {
|
|
||||||
box-shadow: 0 0 5px 0 var(--box-shadow);
|
|
||||||
margin: 10px auto;
|
|
||||||
padding: 8px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
max-width: 400px;
|
|
||||||
gap: 10px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination .back,
|
|
||||||
.pagination .after {
|
|
||||||
color: var(--second);
|
|
||||||
font-size: 26px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.pagination .numbers {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination .number {
|
|
||||||
color: var(--second);
|
|
||||||
font-size: 22px;
|
|
||||||
border: 0 0 5px var(--second);
|
|
||||||
padding: 8px;
|
|
||||||
border-radius: 2px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
margin: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination .number.active {
|
|
||||||
color: var(--main);
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.dots {
|
.dots {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const { UserModel, SecretModel } = require("../../../models");
|
const { UserModel, SecretModel } = require("../../../models");
|
||||||
const { Router } = require("express")
|
const { Router } = require("express");
|
||||||
|
const { URLRegex } = require("../../../lib");
|
||||||
|
|
||||||
const app = Router();
|
const app = Router();
|
||||||
|
|
||||||
|
@ -58,11 +59,11 @@ app.patch("/:id/", async (req, res) => {
|
||||||
|
|
||||||
if (admin?.length && !req.user.admin) return res.error(403, "You have not got permission for edit 'admin' information, or bad request.");
|
if (admin?.length && !req.user.admin) return res.error(403, "You have not got permission for edit 'admin' information, or bad request.");
|
||||||
|
|
||||||
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 && URLRegex.test(avatar))
|
||||||
member.avatar = avatar;
|
member.avatar = avatar;
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
await SecretModel.findOneAndUpdate({ name: member.name }, { name });
|
await SecretModel.updateOne({ id: member.id }, { username: name });
|
||||||
member.name = name;
|
member.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ const { UserModel, SecretModel } = require("../models");
|
||||||
const { Router } = require("express")
|
const { Router } = require("express")
|
||||||
const bcrypt = require("bcrypt");
|
const bcrypt = require("bcrypt");
|
||||||
const rateLimit = require('express-rate-limit')
|
const rateLimit = require('express-rate-limit')
|
||||||
|
const {URLRegex} = require("../lib")
|
||||||
const app = Router();
|
const app = Router();
|
||||||
|
|
||||||
app.get("/", (req, res) => res.reply("register", { user: null }));
|
app.get("/", (req, res) => res.reply("register", { user: null }));
|
||||||
|
@ -22,7 +22,7 @@ app.post("/", rateLimit({
|
||||||
|
|
||||||
|
|
||||||
const user2 = new UserModel({ name: req.body.username })
|
const user2 = new UserModel({ name: req.body.username })
|
||||||
if (avatar && /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g.test(avatar)) user2.avatar = avatar;
|
if (avatar && URLRegex.test(avatar)) user2.avatar = avatar;
|
||||||
|
|
||||||
if (about) user2.about = about;
|
if (about) user2.about = about;
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
const { Router } = require("express");
|
const { Router } = require("express");
|
||||||
const app = Router();
|
const app = Router();
|
||||||
|
const {clearContent} = require("../lib");
|
||||||
const { ThreadModel, MessageModel } = require("../models")
|
const { ThreadModel, MessageModel } = require("../models")
|
||||||
|
|
||||||
|
|
||||||
app.get("/", async (req, res) => {
|
app.get("/", async (req, res) => {
|
||||||
|
const page = Number(req.query.page) || 0;
|
||||||
let threads = await ThreadModel.find(req.user?.admin ? {} : { deleted: false })//.limit(10);
|
const query = req.user?.admin ? {} : { deleted: false };
|
||||||
|
let threads = await ThreadModel.find(query).limit(10).skip(page * 10);
|
||||||
threads = await Promise.all(threads.map(thread => thread.get_author()));
|
threads = await Promise.all(threads.map(thread => thread.get_author()));
|
||||||
return res.reply("threads", { threads });
|
|
||||||
|
return res.reply("threads", { threads, page, pages: Math.ceil(await ThreadModel.count(query) / 10) });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,10 +31,7 @@ app.get("/:id/", async (req, res) => {
|
||||||
|
|
||||||
const messages = await Promise.all(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(async message => {
|
.then(messages => messages.map(async message => {
|
||||||
message.content = message.content.replaceAll("&", "&")
|
message.content =clearContent( message.content)
|
||||||
.replaceAll("<", "<").replaceAll(">", ">")
|
|
||||||
.replaceAll("\"", """).replaceAll("'", "'")
|
|
||||||
.replaceAll("\n", "<br>");
|
|
||||||
return await message.get_author();
|
return await message.get_author();
|
||||||
})));
|
})));
|
||||||
res.reply("thread", { page, thread, messages, scroll: req.query.scroll || messages[0]?.id });
|
res.reply("thread", { page, thread, messages, scroll: req.query.scroll || messages[0]?.id });
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
const { Router } = require("express");
|
const { Router } = require("express");
|
||||||
const app = Router();
|
const app = Router();
|
||||||
|
const {clearContent} = require("../lib");
|
||||||
|
|
||||||
const { UserModel, MessageModel, ThreadModel } = require("../models");
|
const { UserModel, MessageModel, ThreadModel } = require("../models");
|
||||||
|
|
||||||
app.get("/", async ({ user }, res) => {
|
app.get("/", async (req, res) => {
|
||||||
const users = await UserModel.find(user?.admin ? {} : { deleted: false });
|
const page = Number(req.query.page) || 0;
|
||||||
return res.reply("users", { users })
|
const query = req.user?.admin ? {} : { deleted: false };
|
||||||
|
let users = await UserModel.find(query).limit(10).skip(page * 10);
|
||||||
|
return res.reply("users", { users, page, pages: Math.ceil(await UserModel.count(query) / 10) });
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get("/:id/edit", async (req, res) => {
|
app.get("/:id/edit", async (req, res) => {
|
||||||
|
@ -29,10 +31,7 @@ app.get("/:id", async (req, res) => {
|
||||||
|
|
||||||
const message = await MessageModel.count({ authorID: id });
|
const message = await MessageModel.count({ authorID: id });
|
||||||
const thread = await ThreadModel.count({ authorID: id });
|
const thread = await ThreadModel.count({ authorID: id });
|
||||||
member.about = member.about.replaceAll("&", "&")
|
member.about = clearContent( member.about)
|
||||||
.replaceAll("<", "<").replaceAll(">", ">")
|
|
||||||
.replaceAll("\"", """).replaceAll("'", "'")
|
|
||||||
.replaceAll("\n", "<br>");
|
|
||||||
res.reply("user", { member, counts: { message, thread } })
|
res.reply("user", { member, counts: { message, thread } })
|
||||||
}
|
}
|
||||||
else res.error(404, `We don't have any user with id ${id}.`);
|
else res.error(404, `We don't have any user with id ${id}.`);
|
||||||
|
|
|
@ -7,13 +7,17 @@
|
||||||
<body style="text-align: center;">
|
<body style="text-align: center;">
|
||||||
<link rel="stylesheet" href="/css/login.css" />
|
<link rel="stylesheet" href="/css/login.css" />
|
||||||
<link rel="stylesheet" href="/css/user.css" />
|
<link rel="stylesheet" href="/css/user.css" />
|
||||||
|
<style>
|
||||||
|
.see {
|
||||||
|
color: var(--reaction-hover)
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<%- include("extra/navbar") %>
|
<%- include("extra/navbar") %>
|
||||||
|
|
||||||
<h1 class="title">Edit <a href="/users/<%= member.id %>"><%= member.name %></a></h1>
|
<h1 class="title">Edit <a class="see" href="/users/<%= member.id %>"><%= member.name %></a></h1>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<form id="form">
|
<form id="form" class="see">
|
||||||
<input type="text" name="name" placeholder="<%=member.name%>" class="input">
|
<input type="text" name="name" placeholder="<%=member.name%>" class="input">
|
||||||
<input type="url" name="avatar" placeholder="<%=member.avatar%>" class="input">
|
<input type="url" name="avatar" placeholder="<%=member.avatar%>" class="input">
|
||||||
<textarea class="input" name="about" rows="4" name="content" placeholder="<%=member.about%>"></textarea>
|
<textarea class="input" name="about" rows="4" name="content" placeholder="<%=member.about%>"></textarea>
|
||||||
|
@ -41,8 +45,6 @@
|
||||||
if (res) alert(`User is updated!`);
|
if (res) alert(`User is updated!`);
|
||||||
location.reload();
|
location.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
|
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/thread.css" />
|
<link rel="stylesheet" href="/css/thread.css" />
|
||||||
|
<link rel="stylesheet" href="/css/pages.css" />
|
||||||
|
|
||||||
<% if (user){ %>
|
<% if (user){ %>
|
||||||
<script type="module" src="/js/thread.js"></script>
|
<script type="module" src="/js/thread.js"></script>
|
||||||
<% }; %>
|
<% }; %>
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
<%- include("extra/meta", {title: "Thread list!" }) %>
|
<%- include("extra/meta", {title: "Thread list!" }) %>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/css/threads.css" />
|
<link rel="stylesheet" href="/css/threads.css" />
|
||||||
|
<link rel="stylesheet" href="/css/pages.css" />
|
||||||
|
|
||||||
<%- include("extra/navbar") %>
|
<%- include("extra/navbar") %>
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="box-username">
|
<div class="box-username">
|
||||||
<% if (user && !thread.deleted){ %>
|
<% if (user && !thread.deleted){ %>
|
||||||
<a class="btn-danger" onclick="fetch('/api/threads/<%= thread.id %>/',{method:'DELETE'})"><i class="fa-solid fa-trash-can"></i></a>
|
<a class="btn-danger" onclick="fetch('/api/threads/<%= thread.id %>/',{method:'DELETE'})"><i class="bx bx-trash bx-sm"></i></a>
|
||||||
<% } %>
|
<% } %>
|
||||||
<%= thread.author.name %> <div class="avatar"><img src="<%=thread.author.avatar %>"> </div>
|
<%= thread.author.name %> <div class="avatar"><img src="<%=thread.author.avatar %>"> </div>
|
||||||
|
|
||||||
|
@ -32,5 +33,24 @@
|
||||||
<% }); %>
|
<% }); %>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pagination">
|
||||||
|
<div class="back">
|
||||||
|
<% if (page > 0){ %>
|
||||||
|
<a href="/threads?page=<%= page-1 %>" class='bx bxs-chevron-left'></a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="numbers">
|
||||||
|
<% for(let i=0; i < pages; i++){ %>
|
||||||
|
<a class="number <%= i==page?'active':'' %>" href="/threads?page=<%= i %>"><%= i+1 %></a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="after">
|
||||||
|
<% if (pages-1 > page) { %>
|
||||||
|
<a href="/threads?page=<%= page +1 %>" class='bx bxs-chevron-right'></a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
|
@ -5,7 +5,9 @@
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'>
|
||||||
<link rel="stylesheet" href="/css/users.css" />
|
<link rel="stylesheet" href="/css/users.css" />
|
||||||
|
<link rel="stylesheet" href="/css/pages.css" />
|
||||||
|
|
||||||
<%- include("extra/navbar") %>
|
<%- include("extra/navbar") %>
|
||||||
|
|
||||||
|
@ -22,4 +24,24 @@
|
||||||
<% }); %>
|
<% }); %>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pagination">
|
||||||
|
<div class="back">
|
||||||
|
<% if (page > 0){ %>
|
||||||
|
<a href="/users?page=<%= page-1 %>" class='bx bxs-chevron-left'></a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="numbers">
|
||||||
|
<% for(let i=0; i < pages; i++){ %>
|
||||||
|
<a class="number <%= i==page?'active':'' %>" href="/users?page=<%= i %>"><%= i+1 %></a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="after">
|
||||||
|
<% if (pages-1 > page) { %>
|
||||||
|
<a href="/users?page=<%= page +1 %>" class='bx bxs-chevron-right'></a>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
Loading…
Reference in a new issue