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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;").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("&", "&amp;")
-            .replaceAll("<", "&lt;").replaceAll(">", "&gt;")
-            .replaceAll("\"", "&quot;").replaceAll("'", "&#39;")
-            .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("&", "&amp;")
+                    .replaceAll("<", "&lt;").replaceAll(">", "&gt;")
+                    .replaceAll("\"", "&quot;").replaceAll("'", "&#39;")
+                    .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>