mirror of
https://github.com/Akif9748/akf-forum.git
synced 2024-12-22 23:59:08 +03:00
Better user edit. Added footer. better API way.
This commit is contained in:
parent
4e12c2d325
commit
f11711ac76
12 changed files with 186 additions and 170 deletions
|
@ -28,7 +28,6 @@ But in front end, the API will works with session.
|
|||
- GET `/api/users/:id` for fetch user.
|
||||
- DELETE `/api/users/:id/` for delete user.
|
||||
- PATCH `/api/users/:id/` for edit user.
|
||||
- POST `/api/users/:id/undelete` for undelete user.
|
||||
|
||||
- GET `/api/threads/:id` for fetch thread.
|
||||
- DELETE `/api/threads/:id/` for delete thread.
|
||||
|
|
10
README.md
10
README.md
|
@ -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...
|
||||
|
||||
## 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
|
||||
* [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 | Is done? | Priority |
|
||||
| ----- | -------- | -------- |
|
||||
| Search & message a | 🟡 | MEDIUM |
|
||||
| Footer | 🟡 | LOW |
|
||||
| Page support for search | 🟡 | LOW |
|
||||
| Footer | 🟢 | LOW |
|
||||
| Local pfp store | 🔴 | MEDIUM |
|
||||
| IPs of users will add SecretModel | 🔴 | MEDIUM |
|
||||
| better theme patch UserModel | 🟡 | VERY LOW |
|
||||
| Category | ⚪ | MEDIUM |
|
||||
| Profile Message | 🔴 | LOW |
|
||||
| Last seen, last seen info | 🔴 | LOW |
|
||||
| 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
|
||||
- V4: Caching
|
||||
- V3: New Theme
|
||||
|
|
15
index.js
15
index.js
|
@ -7,11 +7,24 @@ const { UserModel, BanModel } = require("./models"),
|
|||
port = process.env.PORT || 3000,
|
||||
mongoose = require("mongoose"),
|
||||
express = require('express'),
|
||||
// multer = require("multer"),
|
||||
fs = require("fs"),
|
||||
app = express();
|
||||
|
||||
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();
|
||||
mongoose.connect(process.env.MONGO_DB_URL,
|
||||
async () => console.log("Database is connected with", (app.ips = await BanModel.find({})).length, "banned IPs"));
|
||||
|
|
|
@ -1,48 +1,75 @@
|
|||
/* MODAL */
|
||||
.modal{
|
||||
position:fixed;
|
||||
left:0;
|
||||
top:0;
|
||||
width:100%;
|
||||
height:100%;
|
||||
background:red;
|
||||
background: #5b5b5bde;
|
||||
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.modal-content{
|
||||
position:absolute;
|
||||
top:50%;
|
||||
left:50%;
|
||||
transform: translate(-50%,-50%);
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
padding: 10px;
|
||||
opacity: 0;
|
||||
display: block;
|
||||
animation: fadeIn 1s forwards;
|
||||
opacity: 0;
|
||||
display: block;
|
||||
animation: fadeIn 1s forwards;
|
||||
}
|
||||
|
||||
.modal.no-active{
|
||||
display:none;
|
||||
.modal.no-active {
|
||||
display: none;
|
||||
|
||||
}
|
||||
|
||||
.modal-close{
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 10px;
|
||||
font-size: 36px;
|
||||
color: var(--main);
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
right: 10px;
|
||||
font-size: 36px;
|
||||
color: var(--main);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1;}
|
||||
}
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@media(max-width:760px){
|
||||
.modal-content{
|
||||
width:95%;
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width:760px) {
|
||||
.modal-content {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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 (!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))
|
||||
member.avatar = avatar;
|
||||
|
@ -68,10 +68,10 @@ app.patch("/:id/", async (req, res) => {
|
|||
}
|
||||
|
||||
if (about) member.about = about;
|
||||
if (theme)
|
||||
member.theme = member.theme === "default" ? "black" : "default";
|
||||
if (theme || ["default", "black"].includes(theme)) member.theme = theme;
|
||||
|
||||
if (typeof admin === "boolean" || ["false", "true"].includes(admin)) member.admin = admin;
|
||||
if (deleted === false) member.deleted = false;
|
||||
member.edited = true;
|
||||
|
||||
await member.save();
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
const object = {};
|
||||
new FormData(e.target).forEach((value, key) => object[key] = value);
|
||||
console.log(object)
|
||||
|
||||
const res = await request("/api/users/<%=member.id%>", "PATCH", object);
|
||||
if (res) alert(`User is updated!`);
|
||||
location.reload();
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
<style>
|
||||
select{
|
||||
background: #ebebeb;
|
||||
border: white;
|
||||
padding: 8px 20px;
|
||||
font-size: 16px;
|
||||
font-family: inherit;
|
||||
}
|
||||
select option{
|
||||
font-family: inherit;
|
||||
width:290px;
|
||||
}
|
||||
|
||||
select {
|
||||
background: #ebebeb;
|
||||
border: white;
|
||||
padding: 8px 20px;
|
||||
font-size: 16px;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
select option {
|
||||
font-family: inherit;
|
||||
width: 290px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
background-color: var(--main);
|
||||
padding: 10px;
|
||||
color: white;
|
||||
|
@ -19,21 +23,19 @@ select option{
|
|||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div class="footer">
|
||||
<select style="">
|
||||
<option value="tr">TR</option>
|
||||
<option value="eng">ENG</option>
|
||||
<select>
|
||||
<option value="default">Default theme</option>
|
||||
<option value="black">Black theme</option>
|
||||
</select>
|
||||
<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: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>
|
||||
<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>
|
||||
|
||||
|
||||
</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>
|
||||
</div>
|
|
@ -6,8 +6,9 @@
|
|||
<meta name="author" content="Akif9748">
|
||||
<link rel="icon" type="image/x-icon" href="/images/favicon.jpg">
|
||||
<link rel="stylesheet" href="/css/themes/<%= theme %>.css" />
|
||||
<% if (theme === "black") { %>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<% } %>
|
||||
<link rel="stylesheet" href="/css/common.css" />
|
||||
|
||||
<% if (theme === "black") { %>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<% } %>
|
||||
</head>
|
|
@ -1,4 +1,3 @@
|
|||
<link rel="stylesheet" href="/css/navbar.css" />
|
||||
|
||||
<% if (user?.admin){ %>
|
||||
<div class="admin-bar">
|
||||
|
@ -23,13 +22,11 @@
|
|||
async function invert() {
|
||||
await fetch('/api/users/<%= user.id %>', {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
theme: "<%=user.theme === `default` ? `black` : `default` %>"
|
||||
}),
|
||||
body: JSON.stringify({ theme: "<%=user.theme === `default` ? `black` : `default` %>" }),
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
})
|
||||
});
|
||||
location.reload()
|
||||
}
|
||||
</script>
|
||||
|
|
144
views/user.ejs
144
views/user.ejs
|
@ -6,46 +6,74 @@
|
|||
|
||||
<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="/css/login.css" />
|
||||
<link rel="stylesheet" href="/css/user.css" />
|
||||
<link rel="stylesheet" href="/css/modal.css" />
|
||||
|
||||
<%if (user) {%>
|
||||
<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>
|
||||
<% }; %>
|
||||
<link rel="stylesheet" href="/css/user.css" />
|
||||
<%- include("extra/navbar") %>
|
||||
|
||||
<!--
|
||||
|
||||
data-modal-open="#deneme" bu elemente tıklanıldığında
|
||||
modal yükletir
|
||||
|
||||
|
||||
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<div class="content">
|
||||
|
||||
<% 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) {%>
|
||||
|
@ -53,15 +81,18 @@ data-modal-open="#deneme" bu elemente tıklanıldığında
|
|||
<a id="undelete" class="btn-primary">Undelete user! </a>
|
||||
<% } else if (user?.admin){ %>
|
||||
<a id="delete" class="btn-outline-primary">Delete user! </a>
|
||||
<% }; %>
|
||||
<% } %>
|
||||
|
||||
|
||||
<div class="box" style="justify-content:center;">
|
||||
<img style="width:150px;height:150px;border-radius:50%;" src="<%=member.avatar %>">
|
||||
</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;
|
||||
box-shadow: 0 0 5px 0 var(--second);
|
||||
padding: 10px;
|
||||
|
@ -73,18 +104,15 @@ background: none;
|
|||
color: black;">
|
||||
<%= member.about %>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="box">
|
||||
<h2 class="box-title">Name:</h2>
|
||||
<h2 class="box-value"><%= member.name %></h2>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="box">
|
||||
<h2 class="box-title">Created at:</h2>
|
||||
<h2 class="box-value"><%= new Date(member.time).toLocaleString() %></h2>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<h2 class="box-title">Message:</h2>
|
||||
<h2 class="box-value"><%= counts.message %></h2>
|
||||
|
@ -93,40 +121,8 @@ color: black;">
|
|||
<h2 class="box-title">Thread:</h2>
|
||||
<h2 class="box-value"><%= counts.thread %></h2>
|
||||
</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>
|
||||
|
||||
<script src="../js/modal.js"></script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
<%- include("extra/footer") %>
|
||||
|
||||
</html>
|
||||
</html>
|
Loading…
Reference in a new issue