Better user edit. Added footer. better API way.

This commit is contained in:
Akif9748 2022-09-09 15:10:44 +03:00
parent 4e12c2d325
commit f11711ac76
12 changed files with 186 additions and 170 deletions

View file

@ -28,7 +28,6 @@ But in front end, the API will works with session.
- 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. - PATCH `/api/users/:id/` for edit user.
- POST `/api/users/:id/undelete` for undelete user.
- GET `/api/threads/:id` for fetch thread. - GET `/api/threads/:id` for fetch thread.
- DELETE `/api/threads/:id/` for delete thread. - DELETE `/api/threads/:id/` for delete thread.

View file

@ -13,7 +13,7 @@ Run `node util/reset` to **reset the database**, and run `node util/admin` for g
Edit `config.json` for default themes of users... Edit `config.json` for default themes of users...
## API ## API
Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn about API in `util/APIDOCS.md`. Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn about API in `APIDOCS.md`.
## Credits ## Credits
* [Akif9748](https://github.com/Akif9748) - Project mainteiner, main developer, made **old** frontend * [Akif9748](https://github.com/Akif9748) - Project mainteiner, main developer, made **old** frontend
@ -34,15 +34,15 @@ Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn
## TO-DO list ## TO-DO list
| To do | Is done? | Priority | | To do | Is done? | Priority |
| ----- | -------- | -------- | | ----- | -------- | -------- |
| Search & message a | 🟡 | MEDIUM | | Page support for search | 🟡 | LOW |
| Footer | 🟡 | LOW | | Footer | 🟢 | LOW |
| Local pfp store | 🔴 | MEDIUM | | Local pfp store | 🔴 | MEDIUM |
| IPs of users will add SecretModel | 🔴 | MEDIUM | | IPs of users will add SecretModel | 🔴 | MEDIUM |
| better theme patch UserModel | 🟡 | VERY LOW |
| Category | ⚪ | MEDIUM | | Category | ⚪ | MEDIUM |
| Profile Message | 🔴 | LOW | | Profile Message | 🔴 | LOW |
| Last seen, last seen info | 🔴 | LOW |
| Better Auth | 🔴 | MEDIUM | | Better Auth | 🔴 | MEDIUM |
- Add theme selection to footer, and, and, and fix footer, add navbar css to footer and import common css in meta
## Major Version History ## Major Version History
- V4: Caching - V4: Caching
- V3: New Theme - V3: New Theme

View file

@ -7,11 +7,24 @@ const { UserModel, BanModel } = require("./models"),
port = process.env.PORT || 3000, port = process.env.PORT || 3000,
mongoose = require("mongoose"), mongoose = require("mongoose"),
express = require('express'), express = require('express'),
// multer = require("multer"),
fs = require("fs"), fs = require("fs"),
app = express(); app = express();
app.ips = []; app.ips = [];
//Upload file
/*
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public/data");
},
filename: function (req, file, cb) {
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
cb(null, file.fieldname + "-" + uniqueSuffix + ".png");
},
});
const upload = multer({ storage: storage });
app.post("/stats", upload.single("uploaded_file"),*/
require("dotenv").config(); require("dotenv").config();
mongoose.connect(process.env.MONGO_DB_URL, mongoose.connect(process.env.MONGO_DB_URL,
async () => console.log("Database is connected with", (app.ips = await BanModel.find({})).length, "banned IPs")); async () => console.log("Database is connected with", (app.ips = await BanModel.find({})).length, "banned IPs"));

View file

@ -1,48 +1,75 @@
/* MODAL */
.modal{ form {
position:fixed; display: flex;
left:0; align-items: center;
top:0; flex-direction: column;
width:100%; max-width: 500px;
height:100%; margin: 0 auto;
background:red; width: 100%;
background: #5b5b5bde; padding: 8px;
} }
.modal-content{
position:absolute; .input {
top:50%; padding: 8px 10px;
left:50%; font-family: inherit;
transform: translate(-50%,-50%); display: block;
font-weight: 600;
color: var(--input-clr);
width: 100%;
margin-bottom: 10px;
border: 2px solid var(--borders);
}
/* MODAL */
.modal {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #5b5b5bde;
}
.modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white; background: white;
padding: 10px; padding: 10px;
opacity: 0; opacity: 0;
display: block; display: block;
animation: fadeIn 1s forwards; animation: fadeIn 1s forwards;
} }
.modal.no-active{ .modal.no-active {
display:none; display: none;
} }
.modal-close{ .modal-close {
position: absolute; position: absolute;
top: 0; top: 0;
right: 10px; right: 10px;
font-size: 36px; font-size: 36px;
color: var(--main); color: var(--main);
cursor: pointer; cursor: pointer;
} }
@keyframes fadeIn { @keyframes fadeIn {
from { opacity: 0; } from {
to { opacity: 1;} opacity: 0;
} }
@media(max-width:760px){ to {
.modal-content{ opacity: 1;
width:95%;
} }
} }
@media(max-width:760px) {
.modal-content {
width: 95%;
}
}

View file

@ -1,19 +0,0 @@
var modal_open = document.querySelectorAll("[data-modal-open]");
modal_open.forEach(onclick_load)
function onclick_load(item, index) {
var block = item.getAttribute("data-modal-open");
console.log("evet");
console.log(item)
document.querySelector(block).classList.toggle("no-active");
document.querySelector("#user-edit").querySelector(".modal-close").onclick = function() {
document.querySelector(block).classList.toggle("no-active");
}
item.onclick = function() {
document.querySelector(block).classList.toggle("no-active");
};
}

View file

@ -55,9 +55,9 @@ app.patch("/:id/", async (req, res) => {
if (req.user.id !== member.id && !user.admin) return res.error(403, "You have not got permission for this."); if (req.user.id !== member.id && !user.admin) return res.error(403, "You have not got permission for this.");
if (!Object.values(req.body).some(Boolean)) return res.error(400, "Missing member informations in request body."); if (!Object.values(req.body).some(Boolean)) return res.error(400, "Missing member informations in request body.");
const { avatar, name, about, theme, admin } = req.body; const { avatar, name, about, theme, admin, deleted } = req.body;
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 || "deleted" in req.body) && !req.user.admin) return res.error(403, "You have not got permission for edit 'admin' and 'deleted' information, or bad request.");
if (avatar && URLRegex.test(avatar)) if (avatar && URLRegex.test(avatar))
member.avatar = avatar; member.avatar = avatar;
@ -68,10 +68,10 @@ app.patch("/:id/", async (req, res) => {
} }
if (about) member.about = about; if (about) member.about = about;
if (theme) if (theme || ["default", "black"].includes(theme)) member.theme = theme;
member.theme = member.theme === "default" ? "black" : "default";
if (typeof admin === "boolean" || ["false", "true"].includes(admin)) member.admin = admin; if (typeof admin === "boolean" || ["false", "true"].includes(admin)) member.admin = admin;
if (deleted === false) member.deleted = false;
member.edited = true; member.edited = true;
await member.save(); await member.save();

View file

@ -40,7 +40,7 @@
const object = {}; const object = {};
new FormData(e.target).forEach((value, key) => object[key] = value); new FormData(e.target).forEach((value, key) => object[key] = value);
console.log(object)
const res = await request("/api/users/<%=member.id%>", "PATCH", object); const res = await request("/api/users/<%=member.id%>", "PATCH", object);
if (res) alert(`User is updated!`); if (res) alert(`User is updated!`);
location.reload(); location.reload();

View file

@ -1,17 +1,21 @@
<style> <style>
select{
background: #ebebeb; select {
border: white; background: #ebebeb;
padding: 8px 20px; border: white;
font-size: 16px; padding: 8px 20px;
font-family: inherit; font-size: 16px;
} font-family: inherit;
select option{ }
font-family: inherit;
width:290px; select option {
} font-family: inherit;
width: 290px;
}
.footer { .footer {
width: 100%; width: 100%;
margin-top: 10px;
background-color: var(--main); background-color: var(--main);
padding: 10px; padding: 10px;
color: white; color: white;
@ -19,21 +23,19 @@ select option{
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
</style> </style>
<div class="footer"> <div class="footer">
<select style=""> <select>
<option value="tr">TR</option> <option value="default">Default theme</option>
<option value="eng">ENG</option> <option value="black">Black theme</option>
</select> </select>
<a href="https://github.com/Akif9748/akf-forum" style="color: white;"> This website is powered by <a href="https://github.com/Akif9748/akf-forum" style="color: white;"> This website is powered by
<span style="color: #ffbf00;">AKF-Forum </span> </a><div> <span style="color: #ffbf00;">AKF-Forum </span> </a>
<div>
<span style="color:white">Coders</span> <br>
<div style="text-align:center;">
By <a href="https://github.com/Akif9748/" style="color: #ffbf00;">Akf</a> <br> By <a href="#" style="color:#ffbf00;">Tokmak</a>
</div>
</div>
<span style="color:white">Coders</span> <br>
</div> <div style="text-align:center;">
By <a href="https://github.com/Akif9748/" style="color: #ffbf00;">Akf</a> <br> By <a href="#" style="color:#ffbf00;">Tokmak</a>
</div>
</div>
</div>

View file

@ -6,8 +6,9 @@
<meta name="author" content="Akif9748"> <meta name="author" content="Akif9748">
<link rel="icon" type="image/x-icon" href="/images/favicon.jpg"> <link rel="icon" type="image/x-icon" href="/images/favicon.jpg">
<link rel="stylesheet" href="/css/themes/<%= theme %>.css" /> <link rel="stylesheet" href="/css/themes/<%= theme %>.css" />
<% if (theme === "black") { %> <link rel="stylesheet" href="/css/common.css" />
<meta name="theme-color" content="#000000" />
<% } %>
<% if (theme === "black") { %>
<meta name="theme-color" content="#000000" />
<% } %>
</head> </head>

View file

@ -1,4 +1,3 @@
<link rel="stylesheet" href="/css/navbar.css" />
<% if (user?.admin){ %> <% if (user?.admin){ %>
<div class="admin-bar"> <div class="admin-bar">
@ -23,13 +22,11 @@
async function invert() { async function invert() {
await fetch('/api/users/<%= user.id %>', { await fetch('/api/users/<%= user.id %>', {
method: 'PATCH', method: 'PATCH',
body: JSON.stringify({ body: JSON.stringify({ theme: "<%=user.theme === `default` ? `black` : `default` %>" }),
theme: "<%=user.theme === `default` ? `black` : `default` %>"
}),
headers: { headers: {
"Content-Type": "application/json" "Content-Type": "application/json"
} }
}) });
location.reload() location.reload()
} }
</script> </script>

View file

@ -6,46 +6,74 @@
<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 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 rel="stylesheet" href="/css/login.css" />
<link rel="stylesheet" href="/css/user.css" />
<link rel="stylesheet" href="/css/modal.css" />
<%if (user) {%> <link rel="stylesheet" href="/css/user.css" />
<script type="module">
import request from "../../js/request.js";
document.addEventListener("click", async e => {
if (e.target.id == "delete") {
const response = await request("/api/users/<%= member.id %>", "DELETE");
if (!response.deleted) return
alert("User is deleted!");
location.reload()
} else if (e.target.id == "undelete") {
const response = await request("/api/users/<%= member.id %>/undelete");
if (response.deleted) return;
alert("User is undeleted successfully!");
location.reload()
}
});
</script>
<% }; %>
<%- include("extra/navbar") %> <%- include("extra/navbar") %>
<!--
data-modal-open="#deneme" bu elemente tıklanıldığında
modal yükletir
-->
<div class="content"> <div class="content">
<% if (user?.admin || user?.id === member.id) { %> <% if (user?.admin || user?.id === member.id) { %>
<a class="btn-outline-primary" data-modal-open="#user-edit" style="align-self:baseline;">Edit user!</a> <link rel="stylesheet" href="/css/modal.css" />
<a class="btn-outline-primary" id="toogle">Edit user!</a>
<div class="modal no-active" id="user-edit">
<div class="modal-content">
<div class="modal-close">
<i id="toogle" class="fa-solid fa-square-xmark"></i>
</div>
<h1 class="title" style="text-align:center;">Edit <a class="see" href="/users/<%= member.id %>"><%= member.name %></a></h1>
<div class="content">
<form id="form" class="see" style="box-shadow:none">
<input type="text" name="name" placeholder="<%=member.name%>" class="input">
<input type="url" name="avatar" placeholder="<%=member.avatar%>" class="input">
<textarea class="input" name="about" rows="4" cols="60" name="content" placeholder="<%=member.about%>"></textarea>
<% if (user?.admin){ %>
Is Admin? <input id='admin' type='checkbox' value='true' name='admin' <%=member.admin ? "checked": ""%>>
<input id='adminHidden' type='hidden' value='false' name='admin'>
<% } %>
<button class="btn-primary" style="width:100%;">Update User!</button>
</form>
</div>
</div>
</div>
<script type="module">
import request from "../../js/request.js";
const form = document.getElementById("form");
document.addEventListener("click", async e => {
if (e.target.id == "delete") {
const response = await request("/api/users/<%= member.id %>", "DELETE");
if (!response.deleted) return
alert("User is deleted!");
location.reload()
} else if (e.target.id == "undelete") {
const response = await request("/api/users/<%= member.id %>/undelete");
if (response.deleted) return;
alert("User is undeleted successfully!");
location.reload()
} else if (e.target.id == "toogle")
document.getElementById('user-edit').classList.toggle('no-active')
});
form.addEventListener("submit", async e => {
e.preventDefault();
document.getElementById('adminHidden').disabled = document.getElementById("admin").checked;
const object = {};
new FormData(e.target).forEach((value, key) => object[key] = value);
const res = await request("/api/users/<%=member.id%>", "PATCH", object);
if (res) alert(`User is updated!`);
location.reload();
});
</script>
<% } %> <% } %>
<% if (member.deleted) {%> <% if (member.deleted) {%>
@ -53,15 +81,18 @@ data-modal-open="#deneme" bu elemente tıklanıldığında
<a id="undelete" class="btn-primary">Undelete user! </a> <a id="undelete" class="btn-primary">Undelete user! </a>
<% } else if (user?.admin){ %> <% } else if (user?.admin){ %>
<a id="delete" class="btn-outline-primary">Delete user! </a> <a id="delete" class="btn-outline-primary">Delete user! </a>
<% }; %> <% } %>
<div class="box" style="justify-content:center;"> <div class="box" style="justify-content:center;">
<img style="width:150px;height:150px;border-radius:50%;" src="<%=member.avatar %>"> <img style="width:150px;height:150px;border-radius:50%;" src="<%=member.avatar %>">
</div> </div>
<h2 class="box-value" style="align-self: center;">Admin</h2>
<div class="box-value" style=" <% if (member.admin) { %>
<h2 class="box-value" style="align-self: center;">Admin</h2>
<% } %>
<div class="box-value" style="
margin: 10px auto; margin: 10px auto;
box-shadow: 0 0 5px 0 var(--second); box-shadow: 0 0 5px 0 var(--second);
padding: 10px; padding: 10px;
@ -73,18 +104,15 @@ background: none;
color: black;"> color: black;">
<%= member.about %> <%= member.about %>
</div> </div>
<div class="box"> <div class="box">
<h2 class="box-title">Name:</h2> <h2 class="box-title">Name:</h2>
<h2 class="box-value"><%= member.name %></h2> <h2 class="box-value"><%= member.name %></h2>
</div> </div>
<div class="box"> <div class="box">
<h2 class="box-title">Created at:</h2> <h2 class="box-title">Created at:</h2>
<h2 class="box-value"><%= new Date(member.time).toLocaleString() %></h2> <h2 class="box-value"><%= new Date(member.time).toLocaleString() %></h2>
</div> </div>
<div class="box"> <div class="box">
<h2 class="box-title">Message:</h2> <h2 class="box-title">Message:</h2>
<h2 class="box-value"><%= counts.message %></h2> <h2 class="box-value"><%= counts.message %></h2>
@ -93,40 +121,8 @@ color: black;">
<h2 class="box-title">Thread:</h2> <h2 class="box-title">Thread:</h2>
<h2 class="box-value"><%= counts.thread %></h2> <h2 class="box-value"><%= counts.thread %></h2>
</div> </div>
<div class="modal" id="user-edit">
<div class="modal-content">
<div class="modal-close">
<i class="fa-solid fa-square-xmark"></i>
</div>
<h1 class="title" style="text-align:center;">Edit <a class="see" href="/users/<%= member.id %>"><%= member.name %></a></h1>
<div class="content">
<form id="form" class="see" style="box-shadow:none">
<input type="text" name="name" placeholder="<%=member.name%>" 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>
<% if (user.admin){ %>
Is Admin? <input id='admin' type='checkbox' value='true' name='admin' <%=member.admin ? "checked": ""%>>
<input id='adminHidden' type='hidden' value='false' name='admin'>
<% } %>
<button class="btn-primary" style="width:100%;">Update User!</button>
</form>
</div> </div>
</div>
</div>
</div>
<script src="../js/modal.js"></script>
</body> </body>
<%- include("extra/footer") %>
</html> </html>