From facaf105eb982138811dfd71c965c5775c41c0e3 Mon Sep 17 00:00:00 2001 From: Akif9748 <akif9748@gmail.com> Date: Mon, 29 Aug 2022 16:16:44 +0300 Subject: [PATCH] new theme is fixed in everypage --- README.md | 6 +- models/Message.js | 3 +- models/Secret.js | 11 ++-- public/js/thread.js | 83 ++++++------------------ routes/admin.js | 14 +---- routes/api/routes/messages.js | 2 +- routes/api/routes/threads.js | 2 +- routes/login.js | 12 ++-- routes/messages.js | 5 +- routes/threads.js | 40 +++++++----- routes/users.js | 7 +-- views/extra/ot.ejs | 97 ---------------------------- views/thread.ejs | 115 +++++++++++++++++++++------------- 13 files changed, 143 insertions(+), 254 deletions(-) diff --git a/README.md b/README.md index b259af2..5c106b9 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,13 @@ Akf-forum has got an API for AJAX, other clients etc. And, you can learn about A - Profile photos will store in database - regex for pfp for now and - admin perm for undelete, thread + message -- page support for threads +- page support for threads, send, if multi page, send => other page - message "<b>" - author name of thread - page for threads - users -- edit & delete button for thread +- API, ?fast= +- fix error messages, ~~declared as id~~, other... + ### Frontend ### User | To do | Is done? | Priority | diff --git a/models/Message.js b/models/Message.js index 2808503..5cdeff9 100644 --- a/models/Message.js +++ b/models/Message.js @@ -14,7 +14,8 @@ const schema = new mongoose.Schema({ react: { like: [Number], dislike: [Number] - } + }, + index: { type: Number, default: 0 } }) diff --git a/models/Secret.js b/models/Secret.js index b4bb922..e8c71e0 100644 --- a/models/Secret.js +++ b/models/Secret.js @@ -1,12 +1,9 @@ -const { Schema, model } = require("mongoose") - -const schema = new Schema({ +const mongoose = require("mongoose") +const schema = new mongoose.Schema({ username: { type: String, unique: true }, password: String, - id: { type:String, unique: true } - - + id: { type: String, unique: true } }); -module.exports = model('secret', schema); \ No newline at end of file +module.exports = mongoose.model('secret', schema); \ No newline at end of file diff --git a/public/js/thread.js b/public/js/thread.js index 0f14e00..1e6b885 100644 --- a/public/js/thread.js +++ b/public/js/thread.js @@ -1,46 +1,4 @@ import request from "./request.js"; -const message_div = document.getElementById("messages"); - - -function render_message(message) { - const messageElement = document.createElement("div"); - messageElement.classList.add("message"); - messageElement.setAttribute("id", "message-" + message.id); - - messageElement.innerHTML = ` - - <h3 style="float:right;">${new Date(message.time).toLocaleString()}</h3> - - <h2> - <img class="circle" src="${message.author.avatar}"> - <a href="/users/${message.author.id}"> ${message.author.name}</a>: - </h2> - - <p>${message.content.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\n", "<br>")}</p><br> - <div id="message-delete-${message.id}"> - ${/* if */!message.deleted ? - ` - <a onclick="delete_message('${message.id}');">DELETE</a> - <a onclick="edit_message('${message.id}');">EDIT</a> - ` /* else */ : - `<h3 style=\"display:inline;\">This message has been deleted</h3> - <a onclick="undelete_message('${message.id}');">UNDELETE</a> - ` - - } - </div> - <div style="float: right;"> - <h3 id="count${message.id}" style="display:inline;">${message.reactCount}</h3> - <a onclick="react('${message.id}', 'like');">+🔼</a> - <a onclick="react('${message.id}', 'dislike');">-🔽</a> - </div> -`; - - message_div.appendChild(messageElement); - message_div.innerHTML += "<br>"; -}; - -window.scrollTo(0, document.body.scrollHeight); /** * Message Sender @@ -52,10 +10,7 @@ document.getElementById("send")?.addEventListener("submit", async e => { const data = new FormData(form); request("/api/messages", "POST", { threadID: data.get("threadID"), content: data.get("content") }) .then(res => { - if (!res) return; - form.reset(); - res.reactCount = 0; - render_message(res); + if (res) location.href = `/messages/${res.id}`; }); }); @@ -63,7 +18,7 @@ document.getElementById("send")?.addEventListener("submit", async e => { * OTHER FUNCTIONS */ -async function delete_thread(id) { +window.delete_thread = async function (id) { const response = await request("/api/threads/" + id + "/delete"); if (response.deleted) { alert("Thread deleted"); @@ -71,37 +26,39 @@ async function delete_thread(id) { } } -async function undelete_thread(id) { +window.undelete_thread = async function (id) { const response = await request("/api/threads/" + id + "/undelete"); if (!response.deleted) { alert("Thread undeleted"); location.reload(); - } } -async function undelete_message(id) { +window.undelete_message = async function (id) { const response = await request(`/api/messages/${id}/undelete`); - if (!response.deleted) - document.getElementById("message-delete-" + id).innerHTML = `<a onclick=\"delete_message('${id}');\">DELETE</a>`; + if (response.deleted) return; + document.getElementById("deleted-" + id).remove(); + document.getElementById("dot-" + id).innerHTML = ` + <a onclick="delete_message('${id}');">DELETE</a> + <a onclick="edit_message('${id}');">EDIT</a> + ` + } -async function delete_message(id) { +window.delete_message = async function (id) { const response = await request(`/api/messages/${id}/delete`); if (response.deleted) { alert("Message deleted"); - document.getElementById("message-delete-" + id).innerHTML = ` - <h3 style=\"display:inline;\">This message has been deleted</h3> + document.getElementById("dots-" + id).innerHTML = ` + <i class='bx bx-trash bx-sm' id="deleted-${id}" style="color: RED;"></i> + `+ document.getElementById("dots-" + id).innerHTML; + + document.getElementById("dot-" + id).innerHTML = ` <a onclick="undelete_message('${id}');">UNDELETE</a>`;// ADMIN PERM FIX } } -async function react(id, type) { +window.react = async function (id, type) { const res = await request(`/api/messages/${id}/react/${type}`) - document.getElementById(`count${id}`).innerHTML = res.reactCount; + document.getElementById(`like-${id}`).innerHTML = res.react.like.length; + document.getElementById(`dislike-${id}`).innerHTML = res.react.dislike.length; } - -window.delete_message = delete_message; -window.undelete_message = undelete_message; -window.react = react; -window.delete_thread = delete_thread; -window.undelete_thread = undelete_thread; \ No newline at end of file diff --git a/routes/admin.js b/routes/admin.js index d662bef..fec6ead 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -3,15 +3,7 @@ const { Router } = require("express") const app = Router(); app.get("/", async (req, res) => { - if (!req.session.userid) return res.redirect('/login'); - - const user = req.user; - - if (!user?.admin) return res.error( 403, "You have not got permissions for view to this page."); - - res.reply("admin", { user2: false }) + if (!req.user?.admin) return res.error(403, "You have not got permissions for view to this page."); + res.reply("admin") }); - - - -module.exports = app; +module.exports = app; \ No newline at end of file diff --git a/routes/api/routes/messages.js b/routes/api/routes/messages.js index 6c194c0..fc80d0c 100644 --- a/routes/api/routes/messages.js +++ b/routes/api/routes/messages.js @@ -31,7 +31,7 @@ app.post("/", rateLimit({ if (!thread) return res.error(404, `We don't have any thread with id ${threadID}.`); - const message = await new MessageModel({ content, author: req.user, threadID: thread.id }).takeId(); + const message = await new MessageModel({ content, author: req.user, threadID: thread.id, index: thread.messages.length }).takeId(); await message.save(); await thread.push(message.id).save(); diff --git a/routes/api/routes/threads.js b/routes/api/routes/threads.js index 9d9ab0b..a2c6df9 100644 --- a/routes/api/routes/threads.js +++ b/routes/api/routes/threads.js @@ -26,7 +26,7 @@ app.get("/:id/messages/", async (req, res) => { const query = { threadID: id }; if (!req.user.admin) query.deleted = false; - const options = { sort: { date: -1 } }; + const options = { sort: { time: -1 } }; if (limit) options.limit = limit; if (skip) options.skip = skip; diff --git a/routes/login.js b/routes/login.js index abb073e..12d9c03 100644 --- a/routes/login.js +++ b/routes/login.js @@ -3,7 +3,7 @@ const { Router } = require("express"); const app = Router(); const bcrypt = require("bcrypt"); -app.get("/", (req, res) => res.reply("login",{redirect: req.query.redirect,user:null})); +app.get("/", (req, res) => res.reply("login", { redirect: req.query.redirect, user: null })); app.post("/", async (req, res) => { req.session.userid = null; @@ -14,19 +14,19 @@ app.post("/", async (req, res) => { const user = await SecretModel.findOne({ username }); if (user) { - if (!await bcrypt.compare(password, user.password)) return res.error( 403, 'Incorrect Password!') + if (!await bcrypt.compare(password, user.password)) return res.error(403, 'Incorrect Password!') const member = await UserModel.findOne({ name: username }); - if (!member || member.deleted) return res.error( 403, 'Incorrect Username and/or Password!') + if (!member || member.deleted) return res.error(403, 'Incorrect Username and/or Password!') req.session.userid = user.id; - res.redirect( req.query.redirect || '/'); + res.redirect(req.query.redirect || '/'); } else - res.error( 403, 'Incorrect Username and/or Password!') + res.error(403, 'Incorrect Username and/or Password!') } else - res.error( 400, "You forgot entering some values") + res.error(400, "You forgot entering some values") diff --git a/routes/messages.js b/routes/messages.js index 917ed5f..26fdc2e 100644 --- a/routes/messages.js +++ b/routes/messages.js @@ -7,8 +7,9 @@ const app = Router(); app.get("/:id", async (req, res) => { const message = await MessageModel.get(req.params.id); - if (!message || (message.deleted && req.user && !req.user.admin)) return res.error( 404, "We have not got any message declared as this id."); - res.redirect("/threads/" + message.threadID+"?scroll="+req.params.id); + if (!message || (message.deleted && req.user && !req.user.admin)) return res.error( 404, + `We don't have any message with id ${req.params.id}.`); + res.redirect(`/threads/${message.threadID}?scroll=${req.params.id}`); }); diff --git a/routes/threads.js b/routes/threads.js index 6d01ae3..e539a0d 100644 --- a/routes/threads.js +++ b/routes/threads.js @@ -1,7 +1,7 @@ const { Router } = require("express"); const app = Router(); -const { ThreadModel,MessageModel } = require("../models") +const { ThreadModel, MessageModel } = require("../models") app.get("/", async (req, res) => { @@ -14,27 +14,33 @@ app.get("/", async (req, res) => { app.get("/create*", (req, res) => res.reply("create_thread")); -app.get("/:id", async (req, res) => { +app.get("/:id/", async (req, res) => { - const { id } = req.params; - const page = req.query.page || 0; - const thread = await ThreadModel.get(id).skip(page * 10).limit(page * 10 + 10); - thread.views++; + const { user, params: { id } } = req - if (thread && (req.user?.admin || !thread.deleted)) { - const messages = await Promise.all(thread.messages.map(async id => { - const message = await MessageModel.get(id) - message.content = message.content.replaceAll("&", "&") - .replaceAll("<", "<").replaceAll(">", ">") - .replaceAll("\"", """).replaceAll("'", "'") - .replaceAll("\n", "<br>"); - return req.user?.admin || !message?.deleted ? message.toObject({ virtuals: true }) : null; - })); + const page = Number(req.query.page || 0); + const thread = await ThreadModel.get(id) + if (thread && (user?.admin || !thread.deleted)) { + thread.views++; + const query = { threadID: id }; + if (!user || !user.admin) query.deleted = false; + + const messages = await MessageModel.find(query).sort({ time: 1 }).skip(page * 10).limit(page * 10 + 10) + .then(messages => messages.map(message => { + message.content = message.content.replaceAll("&", "&") + .replaceAll("<", "<").replaceAll(">", ">") + .replaceAll("\"", """).replaceAll("'", "'") + .replaceAll("\n", "<br>"); + return message.toObject({ virtuals: true }); + })) + + + res.reply("thread", { page, thread, messages, scroll: req.query.scroll || thread.messages[0].id }); + + thread.save(); - res.reply("thread", { page,thread, messages, scroll: req.query.scroll || thread.messages[0]}); } else res.error(404, "We have not got this thread."); - thread.save(); }); diff --git a/routes/users.js b/routes/users.js index 2bcda13..76400f1 100644 --- a/routes/users.js +++ b/routes/users.js @@ -11,17 +11,16 @@ app.get("/", async ({ user }, res) => { app.get("/:id", async (req, res) => { const user = req.user - const { id = null } = req.params; + const { id } = req.params; const member = await UserModel.get(id); - if (member && (user?.admin || !member.deleted)) { - const message = await MessageModel.count({ "author.id": id });// this place was having problem. fixed + const message = await MessageModel.count({ "author.id": id }); const thread = await ThreadModel.count({ "author.id": id }); res.reply("user", { member, counts: { message, thread } }) } - else res.error(404, "We have not got this user."); + else res.error(404, `We don't have any user with id ${id}.`); }); diff --git a/views/extra/ot.ejs b/views/extra/ot.ejs index 7c59c47..e69de29 100644 --- a/views/extra/ot.ejs +++ b/views/extra/ot.ejs @@ -1,97 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - -<%- include("extra/meta", {title: "Thread list!" }) %> - - -<body style="text-align: center;"> - <%- include("extra/navbar") %> - - <link rel="stylesheet" href="/css/thread.css" /> - <% if (user){ %> - <script type="module" src="/js/thread.js"></script> - <% }%> - - - <h1 style="font-size: 35px;color: #4d18e6;" ><%= thread.title %></h1> - <h3 >View count: <%= thread.views %></h1> - - <h2 style="display:inline;">By <a href="<%='/users/' + thread.author.id %>"> <%= thread.author.name %></a> - <img class="circle" src="<%=thread.author.avatar %>"> - </h2> - - <% if (user && !thread.deleted){ %> - - <a onclick="delete_thread('<%= thread.id %>' )" value=style="display:inline;" >DELETE</a> - <a onclick="edit_thread('<%= thread.id %>')" style="display:inline;" >EDIT</a> - <% } else if (thread.deleted) { %> - <h3 style="display:inline;">This thread has been deleted</h3> - <a onclick="undelete_thread('<%= thread.id %>')" style="display:inline;" >UNDELETE</a> - - <% }; %> - - - <hr> - - - <div id="messages" value="<%= thread.id %>"> - - <% messages.filter(Boolean).forEach(message=>{ %> - - <div class="message" id="message-<%= message.id %>"> - - <h3 style="float:right;"><%= new Date(message.time).toLocaleString() %> </h3> - - <h2> - <img class="circle" src="<%= message.author.avatar %>"> - <a href="/users/<%=message.author.id %>"><%=message.author.name %></a>: - </h2> - - <p><%= message.content%></p><br> - <div id="message-delete-<%=message.id %>"> - - <% if (!message.deleted){ %> - - <a onclick="delete_message('<%=message.id %>');">DELETE</a> - <a onclick="edit_message('<%=message.id %>');">EDIT</a> - <% }else{ %> - <h3 style="display:inline;">This message has been deleted</h3> - <a onclick="undelete_message('<%=message.id %>');">UNDELETE</a> - <% } %> - - </div> - <div style="float: right;"> - <h3 id="count<%=message.id %>" style="display:inline;"><%=message.reactCount %></h3> - <a onclick="react('<%=message.id %>', 'like');">+🔼</a> - <a onclick="react('<%=message.id %>', 'dislike');">-🔽</a> - </div> - </div> - <% }); %> - - </div> - - <hr> - - - <form id="send"> - <textarea rows="4" cols="133" name="content"></textarea> - <input name="threadID" type="hidden" value="<%= thread.id %>"></input> - - <br> - <% if (user){ %> - <button type="submit">Send!</button> - <%} else {%> - <button disabled>Login for send</button> - <% }%> - - </form> - - - <script> - document.getElementById("message-<%= scroll %>").scrollIntoView(); - </script> - - <!-- BURAYA Bİ İLERİ BİR GERİ SAYFA BUTONU GELMEZ Mİ BE--> -</body> - -</html> \ No newline at end of file diff --git a/views/thread.ejs b/views/thread.ejs index 7c4bb62..aaa5f8d 100644 --- a/views/thread.ejs +++ b/views/thread.ejs @@ -12,65 +12,96 @@ <% if (user){ %> <script type="module" src="/js/thread.js"></script> <% }; %> + <div style="text-align:center;padding:8px"> <div class="title"><%= thread.title %></div> <div class="date"> <%= new Date(thread.time).toLocaleString() %> • Views: <%= thread.views %> </div> + </div> + <div style="text-align:center;padding:8px"> + <!-- THREAD AUTHOR AND PROFILE PHOTO --> + + <% if (user && !thread.deleted){ %> + + <a onclick="delete_thread('<%= thread.id %>' )" class="btn-outline-primary" >DELETE</a> + <a onclick="edit_thread('<%= thread.id %>')" class="btn-outline-primary" >EDIT</a> + <% } else if (thread.deleted) { %> + <h3 style="display:inline;">This thread has been deleted</h3> + <a onclick="undelete_thread('<%= thread.id %>')" class="btn-primary" >UNDELETE</a> + + <% }; %> + </div> + <div id="messages" value="<%= thread.id %>"> - <% messages.filter(Boolean).forEach(message=>{ %> + <% messages.filter(Boolean).forEach(message=>{ %> - <div class="message" id="message-<%= message.id %>"> - <div class="left"> - <img src="<%= message.author.avatar || '/images/guest.png' %>"/> - <div class="username"><a href="/users/<%=message.author.id %>"><%=message.author.name %></a></div> - <div class="date"> - <%= new Date(message.time).toLocaleDateString() %> + <div class="message" id="message-<%= message.id %>"> + <div class="left"> + <img src="<%= message.author.avatar || '/images/guest.png' %>"/> + <div class="username"><a href="/users/<%=message.author.id %>"><%=message.author.name %></a></div> + <div class="date"> + <%= new Date(message.time).toLocaleDateString() %> + </div> + <div class="date"> + <%= new Date(message.time).toLocaleTimeString() %> + </div> + </div> + + <div class="content"><%- message.content %></div> + <% if(user){ %> + <% if(user.id === message.author.id || user.admin){ %> + + <div class="dots" id="dots-<%=message.id %>" onclick="dots('<%=message.id %>')"> + <% if (message.deleted){ %> + <i class='bx bx-trash bx-sm' id="deleted-<%=message.id %>" style="color: RED;"></i> + <% } %> + <i class='bx bx-dots-horizontal-rounded' ></i> </div> - <div class="date"> - <%= new Date(message.time).toLocaleTimeString() %> - </div> - </div> - <div class="content"><%- message.content %></div> -<% if(user){ %> - <% if(user.id === message.author.id || user.admin){ %> - - <div class="dots" onclick="dots('<%=message.id %>')"> - <i class='bx bx-dots-horizontal-rounded' ></i> - </div> - - <% if (!message.deleted){ %> - <div class="dots-menu" id="dot-<%=message.id %>"> + <% if (!message.deleted){ %> <a onclick="delete_message('<%=message.id %>');">Delete</a> <a onclick="edit_message('<%=message.id %>');">Edit</a> - </div> <% }else if (user.admin){ %> - <div class="dots-menu" id="dot-<%=message.id %>"> - <a onclick="undelete_message('<%=message.id %>');">UNDELETE</a> - </div> - <% } %> - - <% } %> + <a onclick="undelete_message('<%=message.id %>');">Undelete</a> + <% } %> + </div> + <% } %> - <div class="reactions"> - <div> -<i class='bx bx-like'></i> <%=message.react.like.length %> - </div> - <div> -<i class='bx bx-dislike'></i> <%=message.react.dislike.length %> - </div> - </div> - <% }; %> + <div class="reactions"> + <div> + <i onclick='react("<%= message.id %>","like");' class='bx bx-like'></i> + <div id="like-<%= message.id %>"><%=message.react.like.length %></div> + </div> + <div> + <i onclick='react("<%= message.id %>","dislike");' class='bx bx-dislike'></i> + <div id="dislike-<%= message.id %>"><%=message.react.dislike.length %></div> + </div> + </div> + <% }; %> - </div> + </div> <% }); %> </div> + <div class="message"> + <form id="send"> + <textarea rows="4" cols="100" name="content"></textarea> + <input name="threadID" type="hidden" value="<%= thread.id %>"></input> + + + + <% if (user){ %> + <button class="btn-primary">Send!</button> + <%} else {%> + <a class="btn-outline-primary" href="/login?redirect=<%= thread.getLink() %>">Login for send</a> + <% }%> + </form> + </div> <div class="pagination"> <div class="back"> <% if (page > 0){ %> @@ -80,15 +111,15 @@ <div class="numbers"> - <% for(let i=0;i< Math.ceil(messages.length/10);i++){ %> - <a class="number <%= i==page?'active':'' %>" href="<%= thread.getLink() %>?page=<%= i %>"><%= i %></a> + <% for(let i=0;i <= Math.ceil(messages.length/10) ;i++){ %> + <a class="number <%= i==page?'active':'' %>" href="<%= thread.getLink() %>?page=<%= i %>"><%= i+1 %></a> <% } %> </div> <div class="after"> - <% if (Math.ceil(messages.length/10)-1 > page){ %> + <% if (Math.ceil(messages.length/10) > page){ %> <a href="<%= thread.getLink() %>?page=<%= page +1 %>" class='bx bxs-chevron-right'></a> - <% } %> + <% } %> </div> </div>