diff --git a/README.md b/README.md index 0796139..a373d42 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ Run `node util/reset` to **reset the database** for duplicate key errors, and ru Edit `config.json` for default theme color (`black` or `white`) of users, and forum name, meta description, character limits, discord auth enabler, global ratelimit. ### How to install theme: -- Copy your theme to `public/themes` folder. +- Copy your theme to `themes` folder. +Additional note for themes: If a theme has not got any .ejs file, it will use default theme's .ejs files. default theme is in themes folder, named as `common`. ### DISCORD AUTH: `"discord_auth": "your_app_id"` in config.json. @@ -61,6 +62,7 @@ Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn - add support for transition around gravatar - BETTER SETUP PAGE - add used open source libraries to README.md +- better folder system ### front-end - text alling center body - add a css file for CodeMirror in threads / send message ok diff --git a/index.js b/index.js index 07f2c9f..d6f33f8 100644 --- a/index.js +++ b/index.js @@ -23,6 +23,12 @@ app.ips = []; app.set("view engine", "ejs"); app.set("limits", limits); +if (RLS.enabled) app.use(RL(RLS.windowMs, RLS.max)); + +for (const theme of fs.readdirSync("./themes")) + app.use(`/themes/${theme}`, express.static(`./themes/${theme}/public/`)); + + app.use(express.static("public"), express.json(), express.urlencoded({ extended: true }), IP(), SES({ secret: process.env.SECRET, store: MS.create({ clientPromise: DB, stringify: false }), resave: false, saveUninitialized: false }), async (req, res, next) => { @@ -37,15 +43,19 @@ app.use(express.static("public"), express.json(), express.urlencoded({ extended: if (!themes.some(t => t.codename === theme.codename)) theme = def_theme; - res.reply = (page, options = {}, status = 200) => res.status(status).render(page, { - dataset: { - themes, theme, forum_name, description, - getFile: file => join(__dirname, "public", "themes", file), - }, - user: req.user, - ...options - }); + res.reply = (page, options = {}, status = 200) => { + const road = join(__dirname, "themes", theme.codename, "views", `${page}.ejs`); + const renderpage = fs.existsSync(road) ? road : join(__dirname, "themes", "common", "views", `${page}.ejs`); + return res.status(status).render(renderpage, { + dataset: { + themes, theme, forum_name, description, + getFile: file => join(__dirname, "themes", file), + }, + user: req.user, + ...options + }); + } res.error = (type, error) => res.reply("error", { type, error }, type); @@ -63,11 +73,9 @@ app.use(express.static("public"), express.json(), express.urlencoded({ extended: if (discord_auth) app.set("discord_auth", `https://discord.com/api/oauth2/authorize?client_id=${discord_auth}&redirect_uri=${host}%2Fauth%2Fdiscord&response_type=code&scope=identify`); -if (RLS.enabled) app.use(RL(RLS.windowMs, RLS.max)); - for (const file of fs.readdirSync("./routes")) app.use("/" + file.replace(".js", ""), require(`./routes/${file}`)); app.all("*", (req, res) => res.error(404, "This page does not exist on this forum.")); -app.listen(port, () => console.log(`${forum_name}-forum on port:`, port)); \ No newline at end of file +app.listen(port, () => console.log(`${forum_name} on port:`, port)); \ No newline at end of file diff --git a/lib.js b/lib.js index d297dff..7866db1 100644 --- a/lib.js +++ b/lib.js @@ -6,7 +6,7 @@ const { readdirSync } = require('fs'); require("dotenv").config(); module.exports = { - themes: readdirSync("./public/themes").filter(f => f !== "common").map(f => require(`./public/themes/${f}`)), + themes: readdirSync("./themes").filter(f => f !== "common").map(f => require(`./themes/${f}`)), threadEnum: ["OPEN", "APPROVAL", "DELETED"], userEnum: ["ACTIVE", "APPROVAL", "DELETED", "BANNED"], RL(windowMs = 60_000, max = 1) { diff --git a/public/themes/common/footer.ejs b/public/themes/common/footer.ejs deleted file mode 100644 index f209e01..0000000 --- a/public/themes/common/footer.ejs +++ /dev/null @@ -1,44 +0,0 @@ -<div class="footer"> - <% if (user){ %> - <select id="theme_select"> - <% for(const theme of dataset.themes){%> - <option value="<%= theme.codename %>"><%= theme.name %></option> - <% } %> - </select> - <script> - const theme_select = document.getElementById("theme_select"); - theme_select.querySelector(`option[value=<%= user.theme.codename %>]`).selected = true; - theme_select.addEventListener("change", async e => { - const codename = e.target.value; - await fetch('/api/users/<%= user.id %>', { - method: 'PATCH', - body: JSON.stringify({ - theme: { - codename - } - }), - headers: { - "Content-Type": "application/json" - } - }); - const theme = await fetch("/api/themes/" + codename).then(res => res.json()); - const txt = "Theme changed to:\n" + - "Name: " + theme.name + "\n" + - "Description: " + theme.description + "\n" + - "Author: " + theme.author + "\n"; - alert(txt); - location.reload(); - }); - </script> - <% } %> - - <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;"> - <a href="https://github.com/Akif9748/" style="color: #ffbf00;">Akif</a><br><a href="#" style="color:#ffbf00;">Tokmak</a> - </div> - </div> -</div> \ No newline at end of file diff --git a/public/themes/common/navbar.ejs b/public/themes/common/navbar.ejs deleted file mode 100644 index b5f8dc5..0000000 --- a/public/themes/common/navbar.ejs +++ /dev/null @@ -1,45 +0,0 @@ - -<% if (user?.admin){ %> -<div class="admin-bar"> - <a href="/admin" class="admin-bar">Click here to reach admin panel</a> -</div> -<% } %> - -<div class="header"> - <a class="logo" href="/"><%= dataset.forum_name.toUpperCase() %> <span>FORUM</span></a> - <div class="buttons"> - - <% if (user){ %> - <a href="<%=user.getLink() %>" class="btn-outline-primary"> - <div class="box-username"><%= user.name %> - <div class="avatar"><img src="<%=user.avatar %>"></div> - </div> - </a> - <a id="logout" href="/login" class="btn-primary">Logout</a> - <% } else { %> - - <a id="login" href="/login" class="btn-primary">Login</a> - <a href="/register" class="btn-outline-primary">Register</a> - <script> - document.getElementById("login").href += "?redirect=" + location.pathname; - </script> - <% } %> - - </div> -</div> -<div class="menu"> - <a href="/threads/create/" class="menu-item">Create Thread</a> - <a href="/categories" class="menu-item">Categories</a> - <a href="/threads" class="menu-item">Threads</a> - <a href="/users" class="menu-item">Users</a> - <a href="/search" class="menu-item">Search</a> - <script> - const menuItems = document.getElementsByClassName("menu-item"); - - for (let i = 0; i < menuItems.length; i++) - if (window.location.pathname.includes(menuItems[i].getAttribute("href"))) { - menuItems[i].classList.add("active-menu"); - break; - } - </script> -</div> \ No newline at end of file diff --git a/public/themes/common/usermenu.ejs b/public/themes/common/usermenu.ejs deleted file mode 100644 index 863fd69..0000000 --- a/public/themes/common/usermenu.ejs +++ /dev/null @@ -1,72 +0,0 @@ -<% if(user?.admin || user?.id === member.id){ %> -<details> - <summary class="btn-outline-primary">User Menu:</summary> - - <% if (!member.discordID && discord && user?.id === member.id) { %> - <a href="<%=discord%>" class="btn-outline-primary">DC auth</a> - <% } else if(member.discordID && user?.id === member.id) { %> - <a class="btn-outline-primary" id="un_discord">Unauth DC!</a> - <% } %> - <a href="/users/<%=member.id%>/avatar" class="btn-outline-primary">Upload avatar</a> - <a class="btn-outline-primary" id="toogle">Edit user!</a> - <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.state !== "DELETED") return - alert("User is deleted!"); - location.reload() - } else if (e.target.id == "undelete") { - const response = await request("/api/users/<%= member.id %>/", "PATCH", { - deleted: false - }); - if (response.state == "DELETED") return; - alert("User is undeleted successfully!"); - location.reload() - } else if (e.target.id == "un_discord") { - const response = await fetch("/auth/discord/", { - method: "DELETE" - }); - alert(await response.text()); - location.reload() - } else if (e.target.id.startsWith("last_")) { - let hideLastSeen = e.target.id.replace("last_", "") == "hide" ? true : false; - const response = await request("/api/users/<%= member.id %>/", "PATCH", { - hideLastSeen - }); - alert(`Last seen is ${!hideLastSeen?"un":""}hided!`); - location.reload() - - } else if (e.target.id == "toogle") - document.getElementById('user-edit').classList.toggle('no-active') - - }); - </script> - - <% if (member.hideLastSeen) {%> - <a id="last_unhide" class="btn-primary">Unhide last seen! </a> - <% } else { %> - <a id="last_hide" class="btn-outline-primary">Hide last seen! </a> - <% } %> - - <% if (member.deleted) {%> - <h1>This user has been deleted!</h1> - <a id="undelete" class="btn-primary">Undelete user! </a> - <% } else if (user?.admin){ %> - <a id="delete" class="btn-outline-primary">Delete user! </a> - <% } %> - <% if (user?.admin) {%> - <h2>IP adresses of the user:</h2> - <select> - <% for(const ip of member.ips) { %> - <option><%= ip %></option> - <% } %> - </select> - - <% } %> -</details> -<% } %> \ No newline at end of file diff --git a/public/themes/default_black/extra/footer.ejs b/public/themes/default_black/extra/footer.ejs deleted file mode 100644 index 622f620..0000000 --- a/public/themes/default_black/extra/footer.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- include(dataset.getFile("common/footer")) %> \ No newline at end of file diff --git a/public/themes/default_black/extra/navbar.ejs b/public/themes/default_black/extra/navbar.ejs deleted file mode 100644 index 46322ec..0000000 --- a/public/themes/default_black/extra/navbar.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- include(dataset.getFile("common/navbar")) %> \ No newline at end of file diff --git a/public/themes/default_black/extra/usermenu.ejs b/public/themes/default_black/extra/usermenu.ejs deleted file mode 100644 index 8b10460..0000000 --- a/public/themes/default_black/extra/usermenu.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- include(dataset.getFile("common/usermenu")) %> \ No newline at end of file diff --git a/public/themes/default_white/extra/footer.ejs b/public/themes/default_white/extra/footer.ejs deleted file mode 100644 index 622f620..0000000 --- a/public/themes/default_white/extra/footer.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- include(dataset.getFile("common/footer")) %> \ No newline at end of file diff --git a/public/themes/default_white/extra/navbar.ejs b/public/themes/default_white/extra/navbar.ejs deleted file mode 100644 index 46322ec..0000000 --- a/public/themes/default_white/extra/navbar.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- include(dataset.getFile("common/navbar")) %> \ No newline at end of file diff --git a/public/themes/default_white/extra/usermenu.ejs b/public/themes/default_white/extra/usermenu.ejs deleted file mode 100644 index 8b10460..0000000 --- a/public/themes/default_white/extra/usermenu.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- include(dataset.getFile("common/usermenu")) %> \ No newline at end of file diff --git a/routes/.js b/routes/.js index 686d8e9..6ad6c8f 100644 --- a/routes/.js +++ b/routes/.js @@ -1,16 +1,16 @@ -const { UserModel, ThreadModel, MessageModel } = require("../models") +const { UserModel, ThreadModel, MessageModel, CategoryModel } = require("../models") const { Router } = require("express"); const app = Router(); app.get("/", async (req, res) => { - const - mem = process.memoryUsage().heapUsed / Math.pow(2, 20), + const categories = await CategoryModel.count(), users = await UserModel.count({ deleted: false }), threads = await ThreadModel.count({ state: "OPEN" }), - messages = await MessageModel.count({ deleted: false }); + messages = await MessageModel.count({ deleted: false }), + newestMember = await UserModel.findOne({ deleted: false }, "name").sort({ time: -1 }); - res.reply("index", { mem, users, threads, messages }); + res.reply("index", { categories, users, threads, messages, newestMember: newestMember.name }); }); diff --git a/public/themes/bootstrap_black/index.js b/themes/bootstrap_black/index.js similarity index 100% rename from public/themes/bootstrap_black/index.js rename to themes/bootstrap_black/index.js diff --git a/public/themes/bootstrap_black/bootstrap-night.min.css b/themes/bootstrap_black/public/bootstrap-night.min.css similarity index 100% rename from public/themes/bootstrap_black/bootstrap-night.min.css rename to themes/bootstrap_black/public/bootstrap-night.min.css diff --git a/public/themes/bootstrap_black/bootstrap.min.js b/themes/bootstrap_black/public/bootstrap.min.js similarity index 100% rename from public/themes/bootstrap_black/bootstrap.min.js rename to themes/bootstrap_black/public/bootstrap.min.js diff --git a/public/themes/bootstrap_black/main.css b/themes/bootstrap_black/public/main.css similarity index 100% rename from public/themes/bootstrap_black/main.css rename to themes/bootstrap_black/public/main.css diff --git a/public/themes/bootstrap_black/extra/footer.ejs b/themes/bootstrap_black/views/extra/footer.ejs similarity index 100% rename from public/themes/bootstrap_black/extra/footer.ejs rename to themes/bootstrap_black/views/extra/footer.ejs diff --git a/public/themes/bootstrap_black/extra/meta.ejs b/themes/bootstrap_black/views/extra/meta.ejs similarity index 100% rename from public/themes/bootstrap_black/extra/meta.ejs rename to themes/bootstrap_black/views/extra/meta.ejs diff --git a/public/themes/bootstrap_black/extra/navbar.ejs b/themes/bootstrap_black/views/extra/navbar.ejs similarity index 100% rename from public/themes/bootstrap_black/extra/navbar.ejs rename to themes/bootstrap_black/views/extra/navbar.ejs diff --git a/public/themes/bootstrap_black/extra/usermenu.ejs b/themes/bootstrap_black/views/extra/usermenu.ejs similarity index 100% rename from public/themes/bootstrap_black/extra/usermenu.ejs rename to themes/bootstrap_black/views/extra/usermenu.ejs diff --git a/themes/bootstrap_black/views/index.ejs b/themes/bootstrap_black/views/index.ejs new file mode 100644 index 0000000..e40446d --- /dev/null +++ b/themes/bootstrap_black/views/index.ejs @@ -0,0 +1,124 @@ +<!DOCTYPE html> +<html lang="en"> + +<%- include("extra/meta", {title: "Welcome to the "+dataset.forum_name+"-forum!" }) %> + + +<body> + <%- include("extra/navbar") %> + + + <div class="container my-3"> + <nav class="breadcrumb"> + <span class="breadcrumb-item active"> + <% if (user) { %> + Welcome, <%= user.name %> + <% } else { %> + Welcome, Guest! <a href="/register">You can press to register.</a> + <% } %> + </span> + </nav> + <div class="row"> + <div class="col-12 col-xl-9"> + <div class="category"> + + <h2 class="h4 text-white bg-danger mb-0 p-4 rounded-top">Forum category</h2> + <table class="table table-striped table-bordered table-responsive"> + <thead class="thead-light"> + <tr> + <th scope="col" class="forum-col">Forum</th> + <th scope="col">Topics</th> + <th scope="col">Posts</th> + <th scope="col" class="last-post-col">Last post</th> + </tr> + </thead> + <tbody> + <tr class="thread"> + <td> + <h3 class="h5 mb-0"><a href="#0" class="text-uppercase">Forum name</a></h3> + <p class="mb-0">Lorem ipsum dolor sit amet, consectetur adipiscing elit. In laoreet pellentesque lorem sed elementum.</p> + </td> + <td> + <div>5</div> + </td> + <td> + <div>18</div> + </td> + <td> + <h4 class="h6 mb-0 font-weight-bold"><a href="#0">Post name</a></h4> + <div>by <a href="#0">Author name</a></div> + <div>05 Apr 2017, 20:07</div> + </td> + </tr> + </tbody> + </table> + </div> + </div> + <div class="col-12 col-xl-3"> + <aside> + <div class="row"> + <div class="col-12 col-sm-6 col-xl-12"> + <div class="card mb-3 mb-sm-0 mb-xl-3"> + <div class="card-body"> + <h2 class="h4 card-title">Members online</h2> + <ul class="list-unstyled mb-0"> + <li><a href="/users">You</a></li> + + </ul> + </div> + <div class="card-footer"> + <dl class="row mb-0"> + <dt class="col-8">Total:</dt> + <dd class="col-4 mb-0">-</dd> + </dl> + <dl class="row mb-0"> + <dt class="col-8">Members:</dt> + <dd class="col-4 mb-0">-</dd> + </dl> + <dl class="row mb-0"> + <dt class="col-8">Guests:</dt> + <dd class="col-4 mb-0">-</dd> + </dl> + </div> + </div> + </div> + <div class="col-12 col-sm-6 col-xl-12"> + <div class="card"> + <div class="card-body"> + + <h2 class="h4 card-title">Forum statistics</h2> + + <dl class="row mb-0"> + <dt class="col-8">Total forums:</dt> + <dd class="col-4 mb-0"><%= categories %></dd> + </dl> + <dl class="row mb-0"> + <dt class="col-8">Total threads:</dt> + <dd class="col-4 mb-0"><%= threads %></dd> + </dl> + <dl class="row mb-0"> + <dt class="col-8">Total messages:</dt> + <dd class="col-4 mb-0"><%= messages %></dd> + </dl> + <dl class="row mb-0"> + <dt class="col-8">Total members:</dt> + <dd class="col-4 mb-0"><%= users %></dd> + </dl> + </div> + <div class="card-footer"> + <div>Newest member:</div> + <div><a href="#0"><%= newestMember %></a></div> + </div> + </div> + </div> + </div> + </aside> + </div> + </div> + </div> + + <%- include("extra/footer") %> + +</body> + +</html> \ No newline at end of file diff --git a/public/themes/common/main.css b/themes/common/public/main.css similarity index 100% rename from public/themes/common/main.css rename to themes/common/public/main.css diff --git a/views/admin.ejs b/themes/common/views/admin.ejs similarity index 88% rename from views/admin.ejs rename to themes/common/views/admin.ejs index 4a247c9..b6d6fc0 100644 --- a/views/admin.ejs +++ b/themes/common/views/admin.ejs @@ -1,11 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Admin Panel!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Admin Panel!" }) %> <body style="text-align: center;"> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <style> table { font-family: arial, sans-serif; @@ -83,7 +84,7 @@ } </script> </div> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/avatar_upload.ejs b/themes/common/views/avatar_upload.ejs similarity index 91% rename from views/avatar_upload.ejs rename to themes/common/views/avatar_upload.ejs index 16c9abe..c41187b 100644 --- a/views/avatar_upload.ejs +++ b/themes/common/views/avatar_upload.ejs @@ -1,7 +1,7 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Avatar Upload Panel!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Avatar Upload Panel!" }) %> <body style="text-align: center;"> <link rel="stylesheet" href="/libs/cropper/cropper.css"> @@ -27,7 +27,8 @@ </style> <script src="/libs/cropper/cropper.js"></script> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <div class="container"> @@ -107,7 +108,7 @@ }); </script> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/categories.ejs b/themes/common/views/categories.ejs similarity index 82% rename from views/categories.ejs rename to themes/common/views/categories.ejs index 9692c88..d1813e5 100644 --- a/views/categories.ejs +++ b/themes/common/views/categories.ejs @@ -1,11 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Thread list!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Category list!" }) %> <body> <link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <div class="threads"> <% categories.forEach(category=>{ %> <a href="<%= category.getLink() %>"> @@ -48,7 +49,7 @@ </div> <% } %> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/config.ejs b/themes/common/views/config.ejs similarity index 73% rename from views/config.ejs rename to themes/common/views/config.ejs index b52ad92..8e74e89 100644 --- a/views/config.ejs +++ b/themes/common/views/config.ejs @@ -1,10 +1,11 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Edit Forum Config!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Edit Forum Config!" }) %> <body style="text-align: center;"> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <h1>Edit forum config</h1> <textarea rows="30" cols="75"><%= config %></textarea> <a onclick="send();" class="btn-primary">Edit config</a> @@ -24,7 +25,7 @@ } </script> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/create_category.ejs b/themes/common/views/create_category.ejs similarity index 81% rename from views/create_category.ejs rename to themes/common/views/create_category.ejs index 0b771f8..b897f48 100644 --- a/views/create_category.ejs +++ b/themes/common/views/create_category.ejs @@ -1,9 +1,10 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Create Category!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Create Category!" }) %> <body> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <link rel="stylesheet" href="/libs/simplemde/simplemde.min.css"> <script src="/libs/simplemde/simplemde.min.js"></script> @@ -41,7 +42,7 @@ }); </script> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/create_thread.ejs b/themes/common/views/create_thread.ejs similarity index 84% rename from views/create_thread.ejs rename to themes/common/views/create_thread.ejs index 2a3f2cf..82a4222 100644 --- a/views/create_thread.ejs +++ b/themes/common/views/create_thread.ejs @@ -1,11 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Create thread!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Create thread!" }) %> <body > - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <link rel="stylesheet" href="/libs/simplemde/simplemde.min.css"> <script src="/libs/simplemde/simplemde.min.js"></script> @@ -53,7 +54,7 @@ }); </script> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/edit_user.ejs b/themes/common/views/edit_user.ejs similarity index 85% rename from views/edit_user.ejs rename to themes/common/views/edit_user.ejs index 64ff508..05d1cab 100644 --- a/views/edit_user.ejs +++ b/themes/common/views/edit_user.ejs @@ -1,11 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: member.name }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: member.name }) %> <body> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <link rel="stylesheet" href="/libs/simplemde/simplemde.min.css"> <script src="/libs/simplemde/simplemde.min.js"></script> @@ -47,7 +48,7 @@ location.reload(); }); </script> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/themes/common/views/error.ejs b/themes/common/views/error.ejs new file mode 100644 index 0000000..99c8e38 --- /dev/null +++ b/themes/common/views/error.ejs @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> + +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: type+" error!" }) %> + + +<body style="text-align: center;"> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + + + <h1 style="color: var(--main);"><%= type %></h1> + <h2 style="color: var(--second);"><%= error %></h2> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> + +</body> + +</html> \ No newline at end of file diff --git a/themes/common/views/extra/footer.ejs b/themes/common/views/extra/footer.ejs new file mode 100644 index 0000000..aadaf4e --- /dev/null +++ b/themes/common/views/extra/footer.ejs @@ -0,0 +1,44 @@ +<div class="footer"> + <% if (user){ %> + <select id="theme_select"> + <% for(const theme of dataset.themes){%> + <option value="<%= theme.codename %>"><%= theme.name %></option> + <% } %> + </select> + <script> + const theme_select = document.getElementById("theme_select"); + theme_select.querySelector(`option[value=<%= user.theme.codename %>]`).selected = true; + theme_select.addEventListener("change", async e => { + const codename = e.target.value; + await fetch('/api/users/<%= user.id %>', { + method: 'PATCH', + body: JSON.stringify({ + theme: { + codename + } + }), + headers: { + "Content-Type": "application/json" + } + }); + const theme = await fetch("/api/themes/" + codename).then(res => res.json()); + const txt = "Theme changed to:\n" + + "Name: " + theme.name + "\n" + + "Description: " + theme.description + "\n" + + "Author: " + theme.author + "\n"; + alert(txt); + location.reload(); + }); + </script> + <% } %> + + <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;"> + <a href="https://github.com/Akif9748/" style="color: #ffbf00;">Akif</a><br><a href="#" style="color:#ffbf00;">Tokmak</a> + </div> + </div> + </div> \ No newline at end of file diff --git a/themes/common/views/extra/navbar.ejs b/themes/common/views/extra/navbar.ejs new file mode 100644 index 0000000..602c135 --- /dev/null +++ b/themes/common/views/extra/navbar.ejs @@ -0,0 +1,44 @@ +<% if (user?.admin){ %> + <div class="admin-bar"> + <a href="/admin" class="admin-bar">Click here to reach admin panel</a> + </div> + <% } %> + + <div class="header"> + <a class="logo" href="/"><%= dataset.forum_name.toUpperCase() %> <span>FORUM</span></a> + <div class="buttons"> + + <% if (user){ %> + <a href="<%=user.getLink() %>" class="btn-outline-primary"> + <div class="box-username"><%= user.name %> + <div class="avatar"><img src="<%=user.avatar %>"></div> + </div> + </a> + <a id="logout" href="/login" class="btn-primary">Logout</a> + <% } else { %> + + <a id="login" href="/login" class="btn-primary">Login</a> + <a href="/register" class="btn-outline-primary">Register</a> + <script> + document.getElementById("login").href += "?redirect=" + location.pathname; + </script> + <% } %> + + </div> + </div> + <div class="menu"> + <a href="/threads/create/" class="menu-item">Create Thread</a> + <a href="/categories" class="menu-item">Categories</a> + <a href="/threads" class="menu-item">Threads</a> + <a href="/users" class="menu-item">Users</a> + <a href="/search" class="menu-item">Search</a> + <script> + const menuItems = document.getElementsByClassName("menu-item"); + + for (let i = 0; i < menuItems.length; i++) + if (window.location.pathname.includes(menuItems[i].getAttribute("href"))) { + menuItems[i].classList.add("active-menu"); + break; + } + </script> + </div> \ No newline at end of file diff --git a/themes/common/views/extra/usermenu.ejs b/themes/common/views/extra/usermenu.ejs new file mode 100644 index 0000000..8b9a7dd --- /dev/null +++ b/themes/common/views/extra/usermenu.ejs @@ -0,0 +1,72 @@ +<% if(user?.admin || user?.id === member.id){ %> + <details> + <summary class="btn-outline-primary">User Menu:</summary> + + <% if (!member.discordID && discord && user?.id === member.id) { %> + <a href="<%=discord%>" class="btn-outline-primary">DC auth</a> + <% } else if(member.discordID && user?.id === member.id) { %> + <a class="btn-outline-primary" id="un_discord">Unauth DC!</a> + <% } %> + <a href="/users/<%=member.id%>/avatar" class="btn-outline-primary">Upload avatar</a> + <a class="btn-outline-primary" id="toogle">Edit user!</a> + <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.state !== "DELETED") return + alert("User is deleted!"); + location.reload() + } else if (e.target.id == "undelete") { + const response = await request("/api/users/<%= member.id %>/", "PATCH", { + deleted: false + }); + if (response.state == "DELETED") return; + alert("User is undeleted successfully!"); + location.reload() + } else if (e.target.id == "un_discord") { + const response = await fetch("/auth/discord/", { + method: "DELETE" + }); + alert(await response.text()); + location.reload() + } else if (e.target.id.startsWith("last_")) { + let hideLastSeen = e.target.id.replace("last_", "") == "hide" ? true : false; + const response = await request("/api/users/<%= member.id %>/", "PATCH", { + hideLastSeen + }); + alert(`Last seen is ${!hideLastSeen?"un":""}hided!`); + location.reload() + + } else if (e.target.id == "toogle") + document.getElementById('user-edit').classList.toggle('no-active') + + }); + </script> + + <% if (member.hideLastSeen) {%> + <a id="last_unhide" class="btn-primary">Unhide last seen! </a> + <% } else { %> + <a id="last_hide" class="btn-outline-primary">Hide last seen! </a> + <% } %> + + <% if (member.deleted) {%> + <h1>This user has been deleted!</h1> + <a id="undelete" class="btn-primary">Undelete user! </a> + <% } else if (user?.admin){ %> + <a id="delete" class="btn-outline-primary">Delete user! </a> + <% } %> + <% if (user?.admin) {%> + <h2>IP adresses of the user:</h2> + <select> + <% for(const ip of member.ips) { %> + <option><%= ip %></option> + <% } %> + </select> + + <% } %> + </details> + <% } %> \ No newline at end of file diff --git a/views/index.ejs b/themes/common/views/index.ejs similarity index 72% rename from views/index.ejs rename to themes/common/views/index.ejs index 19d83b7..23fcef7 100644 --- a/views/index.ejs +++ b/themes/common/views/index.ejs @@ -1,13 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Welcome to the "+dataset.forum_name+"-forum!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Welcome to the "+dataset.forum_name+"-forum!" }) %> <body> - <link rel="stylesheet" href="/css/user.css" /> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> - <%- include("extra/navbar") %> <div class="usercontent"> <% if (user) { %> @@ -44,12 +43,12 @@ </div> <div class="userbox"> - <h2 class="userbox-title">Memory usage:</h2> - <h2 class="userbox-value"><%= mem.toFixed(2); %> MB</h2> + <h2 class="userbox-title">Category count:</h2> + <h2 class="userbox-value"><%= categories %></h2> </div> </div> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/login.ejs b/themes/common/views/login.ejs similarity index 72% rename from views/login.ejs rename to themes/common/views/login.ejs index a666160..9ebb8d2 100644 --- a/views/login.ejs +++ b/themes/common/views/login.ejs @@ -1,11 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Log in!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Log in!" }) %> <body style="text-align: center;"> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <h1 class="title">Login</h1> @@ -20,7 +21,7 @@ <% } %> <a href="/register" class="btn-outline-primary">Register</a> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/messages.ejs b/themes/common/views/messages.ejs similarity index 72% rename from views/messages.ejs rename to themes/common/views/messages.ejs index cf8c7f3..41a5f9e 100644 --- a/views/messages.ejs +++ b/themes/common/views/messages.ejs @@ -1,11 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title:"Message search!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title:"Message search!" }) %> <body style="text-align: center;"> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <div id="messages"> @@ -27,7 +28,7 @@ <% }); %> </div> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/register.ejs b/themes/common/views/register.ejs similarity index 75% rename from views/register.ejs rename to themes/common/views/register.ejs index 326f870..a6855c0 100644 --- a/views/register.ejs +++ b/themes/common/views/register.ejs @@ -1,12 +1,13 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Register!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Register!" }) %> <body style="text-align: center;"> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <h1 class="title">Register</h1> @@ -24,7 +25,7 @@ <a href="/login" class="btn-outline-primary">Login</a> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/search.ejs b/themes/common/views/search.ejs similarity index 78% rename from views/search.ejs rename to themes/common/views/search.ejs index 2ab49ff..b9bcba0 100644 --- a/views/search.ejs +++ b/themes/common/views/search.ejs @@ -1,11 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Search page!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Search page!" }) %> <body style="text-align: center;"> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <h1 class="title">Search</h1> @@ -27,7 +28,7 @@ <input type="submit" value="Search" class="btn-primary" /> </form> </div> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/setup.ejs b/themes/common/views/setup.ejs similarity index 86% rename from views/setup.ejs rename to themes/common/views/setup.ejs index 943cec0..e967a3f 100644 --- a/views/setup.ejs +++ b/themes/common/views/setup.ejs @@ -1,11 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Setup the Akf-forum!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Setup the Akf-forum!" }) %> <body style="text-align: center;"> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <h1 style="color: var(--main);">Setup</h1> <h2 style="color: var(--second);">There is default settings for akf-forum, you not need to edit them, but you can! And, the first registered user will be admin.</h2> diff --git a/views/thread.ejs b/themes/common/views/thread.ejs similarity index 95% rename from views/thread.ejs rename to themes/common/views/thread.ejs index d846a6e..948ee9b 100644 --- a/views/thread.ejs +++ b/themes/common/views/thread.ejs @@ -1,11 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: thread.title }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: thread.title }) %> <body> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js"></script> <link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'> <link rel="stylesheet" href="/libs/simplemde/simplemde.min.css"> @@ -159,7 +160,7 @@ </div> </div> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/threads.ejs b/themes/common/views/threads.ejs similarity index 85% rename from views/threads.ejs rename to themes/common/views/threads.ejs index bd62e2f..5b8380e 100644 --- a/views/threads.ejs +++ b/themes/common/views/threads.ejs @@ -1,11 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "Thread list!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "Thread list!" }) %> <body style="color: var(--anti); text-align: center;"> <link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <h1><%= title || "Threads" %></h1> <h2><%= desp %></h2> <div class="threads"> @@ -54,7 +55,7 @@ </div> <% } %> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/user.ejs b/themes/common/views/user.ejs similarity index 89% rename from views/user.ejs rename to themes/common/views/user.ejs index 668b9e6..bf028e1 100644 --- a/views/user.ejs +++ b/themes/common/views/user.ejs @@ -1,11 +1,12 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: member.name }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: member.name }) %> <body> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <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" /> @@ -72,7 +73,7 @@ color: var(--anti); </a> </div> </div> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/views/users.ejs b/themes/common/views/users.ejs similarity index 82% rename from views/users.ejs rename to themes/common/views/users.ejs index 4fba3a9..7fd7954 100644 --- a/views/users.ejs +++ b/themes/common/views/users.ejs @@ -1,13 +1,14 @@ <!DOCTYPE html> <html lang="en"> -<%- include("extra/meta", {title: "User list!" }) %> +<%- include(dataset.getFile(dataset.theme.codename +"/views/extra/meta"), {title: "User list!" }) %> <body> <link href='https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css' rel='stylesheet'> - <%- include("extra/navbar") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/navbar")) %> + <div class="users"> <% users.filter(member=> !member.deleted || user.admin ).forEach(member => { %> <div style="display:flex;justify-content:center;"> @@ -43,7 +44,7 @@ </div> <% } %> - <%- include("extra/footer") %> + <%- include(dataset.getFile(dataset.theme.codename +"/views/extra/footer")) %> </body> diff --git a/public/themes/default_black/index.js b/themes/default_black/index.js similarity index 100% rename from public/themes/default_black/index.js rename to themes/default_black/index.js diff --git a/public/themes/default_black/main.css b/themes/default_black/public/main.css similarity index 100% rename from public/themes/default_black/main.css rename to themes/default_black/public/main.css diff --git a/themes/default_black/views/extra/footer.ejs b/themes/default_black/views/extra/footer.ejs new file mode 100644 index 0000000..9e93ba4 --- /dev/null +++ b/themes/default_black/views/extra/footer.ejs @@ -0,0 +1 @@ +<%- include(dataset.getFile("common/views/extra/footer")) %> \ No newline at end of file diff --git a/public/themes/default_black/extra/meta.ejs b/themes/default_black/views/extra/meta.ejs similarity index 100% rename from public/themes/default_black/extra/meta.ejs rename to themes/default_black/views/extra/meta.ejs diff --git a/themes/default_black/views/extra/navbar.ejs b/themes/default_black/views/extra/navbar.ejs new file mode 100644 index 0000000..edd8d76 --- /dev/null +++ b/themes/default_black/views/extra/navbar.ejs @@ -0,0 +1 @@ +<%- include(dataset.getFile("common/views/extra/navbar")) %> \ No newline at end of file diff --git a/themes/default_black/views/extra/usermenu.ejs b/themes/default_black/views/extra/usermenu.ejs new file mode 100644 index 0000000..ab0693b --- /dev/null +++ b/themes/default_black/views/extra/usermenu.ejs @@ -0,0 +1 @@ +<%- include(dataset.getFile("common/views/extra/usermenu")) %> \ No newline at end of file diff --git a/public/themes/default_white/index.js b/themes/default_white/index.js similarity index 100% rename from public/themes/default_white/index.js rename to themes/default_white/index.js diff --git a/public/themes/default_white/main.css b/themes/default_white/public/main.css similarity index 100% rename from public/themes/default_white/main.css rename to themes/default_white/public/main.css diff --git a/themes/default_white/views/extra/footer.ejs b/themes/default_white/views/extra/footer.ejs new file mode 100644 index 0000000..9e93ba4 --- /dev/null +++ b/themes/default_white/views/extra/footer.ejs @@ -0,0 +1 @@ +<%- include(dataset.getFile("common/views/extra/footer")) %> \ No newline at end of file diff --git a/public/themes/default_white/extra/meta.ejs b/themes/default_white/views/extra/meta.ejs similarity index 100% rename from public/themes/default_white/extra/meta.ejs rename to themes/default_white/views/extra/meta.ejs diff --git a/themes/default_white/views/extra/navbar.ejs b/themes/default_white/views/extra/navbar.ejs new file mode 100644 index 0000000..edd8d76 --- /dev/null +++ b/themes/default_white/views/extra/navbar.ejs @@ -0,0 +1 @@ +<%- include(dataset.getFile("common/views/extra/navbar")) %> \ No newline at end of file diff --git a/themes/default_white/views/extra/usermenu.ejs b/themes/default_white/views/extra/usermenu.ejs new file mode 100644 index 0000000..ab0693b --- /dev/null +++ b/themes/default_white/views/extra/usermenu.ejs @@ -0,0 +1 @@ +<%- include(dataset.getFile("common/views/extra/usermenu")) %> \ No newline at end of file diff --git a/views/error.ejs b/views/error.ejs deleted file mode 100644 index 815fc1d..0000000 --- a/views/error.ejs +++ /dev/null @@ -1,16 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - -<%- include("extra/meta", {title: type+" error!" }) %> - - -<body style="text-align: center;"> - <%- include("extra/navbar") %> - - <h1 style="color: var(--main);"><%= type %></h1> - <h2 style="color: var(--second);"><%= error %></h2> - <%- include("extra/footer") %> - -</body> - -</html> \ No newline at end of file diff --git a/views/extra/footer.ejs b/views/extra/footer.ejs deleted file mode 100644 index 7fe0c27..0000000 --- a/views/extra/footer.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- include(dataset.getFile(dataset.theme.codename +"/extra/footer")) %> \ No newline at end of file diff --git a/views/extra/meta.ejs b/views/extra/meta.ejs deleted file mode 100644 index a09ded5..0000000 --- a/views/extra/meta.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- include(dataset.getFile(dataset.theme.codename +"/extra/meta")) %> \ No newline at end of file diff --git a/views/extra/navbar.ejs b/views/extra/navbar.ejs deleted file mode 100644 index 8772e03..0000000 --- a/views/extra/navbar.ejs +++ /dev/null @@ -1 +0,0 @@ -<%- include(dataset.getFile(dataset.theme.codename +"/extra/navbar")) %> \ No newline at end of file