This commit is contained in:
Akif Yüce 2022-03-13 16:06:25 +03:00 committed by GitHub
parent bd9dbce169
commit eb197276db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1054 additions and 826 deletions

1348
LICENSE

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +1,44 @@
# akf-forum
<img src="https://raw.githubusercontent.com/Akif9748/akf-forum/main/public/images/logo.jpg" align="right" width="300px" />
A Node.js based forum software.
## Installation
- Clone this repo. Or download it.
- Write `npm i` to install **dependencies**.
- Write `npm restart` for reset database, and `npm start` for run it.
## Credits
* [Akif9748](https://github.com/Akif9748) - Project owner
* [Camroku](https://github.com/Camroku) - Made stylesheets
## To Do:
- Admin panel, delete, edit messages.
## Roadmap
- [ ] User
- [x] Login
- [x] Register
- [x] Logout
- [x] Admin
- [x] Message count
- [ ] Messages
- [x] Send message
- [x] Delete message
- [ ] Edit message
- [x] React message
- [ ] Forums
- [ ] Category system.
- [ ] Create category
- [ ] Delete category
- [ ] Edit category
- [ ] Move category
- [ ] Other
- [ ] API
- [ ] Other client for forum via API
- [ ] Footer...
- [ ] Search
- [x] New Thread theme, better render for messages
## Image:
![image](https://user-images.githubusercontent.com/70021050/155854448-76d3e030-3840-43e7-aa3a-80946a0a4ab5.png)
# akf-forum
<img src="https://raw.githubusercontent.com/Akif9748/akf-lang/main/public/images/logo.jpg" align="right" width="300px" />
A forum script written in Node.js.
## Installation
- Clone this repo. Or download it.
- Write `npm i` to install **dependencies**.
- Write `npm restart` for reset database, and `npm start` for run it.
## Credits
* [Akif9748](https://github.com/Akif9748) - Project owner
* [Camroku](https://github.com/Camroku) - Made stylesheets
## To Do:
- Admin panel, delete, edit messages.
## Roadmap
- [x] User
- [x] Login
- [x] Register
- [x] Logout
- [x] Admin
- [x] Message count
- [ ] Messages
- [x] Send message
- [x] Delete message
- [ ] Edit message
- [x] React message
- [ ] Forums
- [ ] Category system
- [ ] Create category
- [ ] Delete category
- [ ] Edit category
- [ ] Move category
- [ ] Other
- [ ] API
- [ ] Other client for forum via API
- [ ] Footer...
- [ ] Search
- [x] New Thread theme, better render for messages
## Image:

View File

@ -1,5 +1,6 @@
const Message = require("./message")
const Thread = require("./thread")
const User = require("./user")
const React = require("./react")
module.exports = { Message, Thread, User }
module.exports = { Message, Thread, User, React }

View File

@ -1,15 +1,45 @@
const User = require("./user")
const Thread = require("./thread")
const db = require("quick.db");
module.exports = class Message {
constructor(content, author = new User(), time = new Date().getTime()) {
constructor(content, author = new User(), thread = new Thread(), time = new Date().getTime(), deleted = false, edited = false, react = {}) {
this.content = content;
this.author = author;
this.time = time;
this.thread = thread;
this.deleted = deleted;
this.edited = edited;
this.react = react;
}
getId(id = this.id) {
const message = db.get("messages" ).find(msg => msg.id == id);
if (!message) return null;
this.id = id;
const { content, author, thread = new Thread(), time = new Date().getTime(), deleted = false, edited = false, react = {} } = message;
this.content = content;
this.thread = thread;
this.author = author;
this.time = time;
this.deleted = deleted;
this.edited = edited;
this.react = react;
return this
}
takeId() {
this.id = db.get("messages").length || 0;
return this;
}
write(id = this.id) {
db.set("messages." + id, this)
return this;
}
getLink(id = this.id) {
return "/messages/" + id;
}
}

8
classes/react.js vendored Normal file
View File

@ -0,0 +1,8 @@
const User = require("./user")
module.exports = class Message {
constructor(like = true, author = new User()) {
this.like = like;
this.author= author;
}
}

View File

@ -1,15 +1,18 @@
const db = require("quick.db")
const User = require("./user")
module.exports = class Thread {
constructor(title, content, author = new User(), messages = [], time = new Date().getTime()) {
constructor(title, author = new User(), messages = [], time = new Date().getTime(),deleted = false) {
this.content = content;
this.author = author;
this.title = title;
this.messages = messages;
this.time = time;
this.deleted = deleted;
}
@ -17,12 +20,12 @@ module.exports = class Thread {
const thread = db.get("threads."+id);
if (!thread) return null;
this.id = id;
const { content, title, author, messages = [], time = new Date().getTime() } = thread;
this.content = content
const { title, author, messages = [], time = new Date().getTime(), deleted = false } = thread;
this.title = title
this.author = author
this.messages = messages;
this.time = time;
this.deleted = deleted;
return this
}
@ -40,6 +43,7 @@ module.exports = class Thread {
write(id = this.id) {
db.set("threads."+id, this)
return this;
}
getLink(id = this.id) {
return "/threads/" + id;

View File

@ -3,22 +3,23 @@ const db = require("quick.db")
module.exports = class User {
constructor(name = "guest", avatar = "/images/guest.png", time = new Date().getTime()) {
constructor(name = "guest", avatar = "/images/guest.png", time = new Date().getTime(), admin= false) {
this.name = name;
this.avatar = avatar;
this.time = time;
this.admin = admin;
}
getId(id = this.id) {
const user = db.get("users." + id);
if (!user) return null;
this.id = id;
const { name = "guest", avatar = "/images/guest.png", time = new Date().getTime() } = user;
this.id = Number(id);
const { name = "guest", avatar = "/images/guest.png", time = new Date().getTime(), admin = false } = user;
this.name = name;
this.avatar = avatar;
this.time = time;
this.time = time;
this.admin = admin;
return this
}
takeId() {

Binary file not shown.

4
package-lock.json generated
View File

@ -1,11 +1,11 @@
{
"name": "akf-forum",
"name": "karum",
"version": "2.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "akf-forum",
"name": "karum",
"version": "2.0.0",
"license": "GPL-3.0-or-later",
"dependencies": {

View File

@ -1,7 +1,7 @@
{
"name": "akf-forum",
"name": "karum",
"version": "2.0.0",
"description": "A Node.js based forum software",
"description": "A forum script written in Node.js",
"main": "index.js",
"scripts": {
"start": "node index.js",
@ -9,14 +9,17 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/Akif9748/akf-forum.git"
"url": "git+https://github.com/Akif9748/karum.git"
},
"author": "Akif9748",
"contributors": [
"Camroku"
],
"license": "GPL-3.0-or-later",
"bugs": {
"url": "https://github.com/Akif9748/akf-forum/issues"
"url": "https://github.com/Akif9748/karum/issues"
},
"homepage": "https://github.com/Akif9748/akf-forum#readme",
"homepage": "https://github.com/Akif9748/karum#readme",
"dependencies": {
"body-parser": "^1.19.2",
"ejs": "^3.1.6",
@ -24,4 +27,4 @@
"express-session": "^1.17.2",
"quick.db": "^7.1.3"
}
}
}

View File

@ -1,3 +1,27 @@
:root { /* Apprentice Scheme */
/*
* use `var(--col-x)` instead of directly typing the color.
*/
--col-0: #1C1C1C;
--col-1: #AF5F5F;
--col-2: #5F875F;
--col-3: #87875F;
--col-4: #5F87AF;
--col-5: #5F5F87;
--col-6: #5F8787;
--col-7: #6C6C6C;
--col-8: #444444;
--col-9: #FF8700;
--col-10: #87AF87;
--col-11: #FFFFAF;
--col-12: #8FAFD7;
--col-13: #8787AF;
--col-14: #5FAFAF;
--col-15: #FFFFFF;
--col-fg: #BCBCBC;
--col-bg: #262626;
}
img.yuvarlak {
border-radius: 50%;
height: 30px;
@ -14,9 +38,9 @@ img.logo {
textarea {
font-family: monospace;
background-color: #262626;
border: 2px solid #444444;
color: #BCBCBC;
background-color: var(--col-bg);
border: 2px solid var(--col-8);
color: var(--col-fg);
width: auto;
height: auto;
cursor: pointer;
@ -27,9 +51,9 @@ p {
}
hr {
border-color: #444444;
border-color: var(--col-8);
border: 0;
border-top: 1px solid #eee;
border-top: 1px solid var(--col-fg);
margin: 20px 0;
}
@ -41,8 +65,8 @@ h4,
h5,
h6 {
font-family: monospace;
background: #262626;
color: #bcbcbc;
background: var(--col-bg);
color: var(--col-fg);
max-width: 69rem;
margin: auto;
margin-top: 10px;
@ -51,23 +75,23 @@ h6 {
}
a:link {
color: #5f87af;
color: var(--col-4);
}
a:hover {
color: #af5f5f;
color: var(--col-1);
}
a:visited {
color: #8787af;
color: var(--col-13);
}
a:active {
color: #af5f5f;
color: var(--col-1);
}
pre {
background-color: #1c1c1c;
background-color: var(--col-0);
padding: 1em;
border: 0;
}
@ -81,15 +105,15 @@ h5 {
}
hr {
border-color: #444444;
border-color: var(--col-8);
}
button, input {
font-family: monospace;
background-color: #262626;
border: 2px solid #444444;
background-color: var(--col-bg);
border: 2px solid var(--col-8);
color: #BCBCBC;
color: var(--col-fg);
width: auto;
height: 30px;
@ -103,12 +127,12 @@ button.buyuk {
input {
width: 75%;
cursor: pointer;
color: #bcbcbc;
color: var(--col-fg);
}
button:hover {
background-color: #BCBCBC;
color: #262626;
background-color: var(--col-fg);
color: var(--col-bg);
}
.mainpage {

View File

@ -2,4 +2,5 @@ const { set } = require("quick.db");
set("users", new Array());
set("threads", new Array());
set("secret", new Object());
set("messages", new Array()); //SSH, this will add.
set("messages", new Array());

12
routes/get/admin.js Normal file
View File

@ -0,0 +1,12 @@
const { User } = require("../../classes/index");
const { get } = require("quick.db");
const error = require("../../errors/error.js");
module.exports = (req, res) => {
if (!req.session.loggedin) return res.redirect('/login');
const user = new User().getId(req.session.userid)
if (!user.admin) return error(res, 404, "You have not got permissions for view to this page.");
res.render("admin", { user })
}

View File

@ -6,7 +6,7 @@ module.exports = (req, res) => {
const mem = process.memoryUsage().heapUsed / Math.pow(2, 20);
const users = get("users").length;
const threads = get("threads").length;
const messages = get("messages").length;
const user = new User().getId(req.session.userid)
res.render("index", { mem, user, users, threads })
res.render("index", { mem, user, users, threads, messages })
}

13
routes/get/message.js Normal file
View File

@ -0,0 +1,13 @@
const { Message } = require("../../classes/index");
const error = require("../../errors/error.js");
module.exports = (req, res) => {
if (!req.session.loggedin) return res.redirect('/login');
const id = req.url.slice(9);
const message = new Message().getId(id)
if (!message || message.deleted) return error(res, 404, "We have not got any message declared as this id.");
res.redirect("/threads/" + message.thread.id);
}

View File

@ -1,9 +1,9 @@
const { Thread, Message, User } = require("../../classes/index");
const db = require("quick.db");
const error = require("../../errors/error.js")
const error = require("../../errors/error.js");
module.exports = (req,res) =>{
module.exports = (req, res) => {
if (!req.session.loggedin) return res.redirect('/login');
const id = req.url.slice(9);
@ -12,12 +12,14 @@ module.exports = (req,res) =>{
if (!id) {
const threads = db.get("threads").slice(0, 10)
const links = threads.map(thread => "/threads/" + threads.indexOf(thread))
return res.render("threads", { threads, links, user })
return res.render("threads", { threads, links, user})
}
const thread = new Thread().getId(id);
if (thread)
res.render("thread", { thread, user })
else
if (thread) {
const messages = thread.messages.filter(id => !new Message().getId(id).deleted).map(id => new Message().getId(id));
res.render("thread", { thread, messages, user })
} else
error(res, 404, "We have not got this thread.");
}

View File

@ -1,8 +1,8 @@
const { Thread, Message, User } = require("../../classes/index");
const { User } = require("../../classes/index");
const db = require("quick.db");
const error = require("../../errors/error.js")
module.exports = (req,res) =>{
module.exports = (req, res) => {
if (!req.session.loggedin) return res.redirect('/login');
const user = new User().getId(req.session.userid)
@ -20,8 +20,13 @@ module.exports = (req,res) =>{
const member = new User().getId(id);
if (member)
res.render("user", { user, member })
if (member) {
const message = db.get("messages").filter(message => message.author.id === Number(id)).length
const thread = db.get("threads").filter(thread => thread.author.id === Number(id)).length
const counts = { message, thread }
res.render("user", { user, member, counts })
}
else
error(res, 404, "We have not got this user.");

22
routes/post/admin.js Normal file
View File

@ -0,0 +1,22 @@
const { User } = require("../../classes/index");
const error = require("../../errors/error.js");
module.exports = (req, res) => {
if (!req.session.loggedin) return res.redirect('/login');
const user = new User().getId(req.session.userid)
if (!user.admin) return error(res, 404, "You have not got permissions for view to this page.");
const user2 = new User().getId(req.body.userid)
if (!user2) return error(res, 404, "We have not got this user in all of forum. Vesselam.");
else {
user2.admin = true;
user2.write()
}
res.render("admin", { user })
}

View File

@ -1,12 +1,14 @@
const { User, Thread } = require("../../classes/index");
const { User, Thread, Message } = require("../../classes/index");
module.exports = (req, res) => {
if (!req.session.loggedin) return res.redirect('/login');
const user = new User().getId(req.session.userid);
const info = req.body;
const thread = new Thread(info.title, info.content, new User().getId(req.session.userid)).takeId();
thread.write()
const thread = new Thread(info.title, user).takeId().write();
thread.push(new Message(info.content, user, thread).takeId().write().id)
thread.write();
res.redirect('/threads/' + thread.id);
}

View File

@ -2,12 +2,13 @@ const db = require("quick.db");
const error = require("../../errors/error.js")
module.exports = (req, res) => {
req.session.loggedin = false;
req.session.username = null;
req.session.userid = null;
let username = req.body.username;
let password = req.body.password;
if (username && password) {
const user = db.get("secret." + username)
if (user) {
// Authenticate the user
if (user.key !== password) return error(res, 404, 'Incorrect Password!')

View File

@ -0,0 +1,19 @@
const { User, Message } = require("../../classes/index");
const error = require("../../errors/error.js");
module.exports = (req, res) => {
if (!req.session.loggedin) return res.redirect('/login');
const id = req.url.slice(9 + 6)
const message = new Message().getId(id)
if (!message || message.deleted) return error(res, 404, "We have not got any message declared as this id.");
const user = new User().getId(req.session.userid);
if (user.id != message.author.id && !user.admin)
return error(res, 403, "You have not got permission for this.");
message.deleted = true;
message.write();
res.redirect("/threads/" + message.thread.id);
}

23
routes/post/react.js vendored Normal file
View File

@ -0,0 +1,23 @@
const error = require("../../errors/error.js")
const { Message, User } = require("../../classes/index");
module.exports = (req, res) => {
if (!req.session.loggedin) return res.redirect('/login');
const id = req.url.slice(7);
if (!id) error(res, 404, "Id of request is missing");
const info = req.body;
const user = new User().getId(req.session.userid);
const message = new Message().getId(id);
if (message) {
if (!(user.id in message.react))
message.react[user.id] = "like" in info;
else
delete message.react[user.id];
message.write();
res.redirect("/threads/" + message.thread.id);
} else error(res, 404, "We have not got this Message for reacting.");
}

View File

@ -3,7 +3,9 @@ const error = require("../../errors/error.js")
const { User } = require("../../classes/index");
module.exports = (req, res) => {
req.session.loggedin = false;
req.session.username = null;
req.session.userid = null;
let username = req.body.username;
let password = req.body.password;

View File

@ -8,7 +8,8 @@ module.exports = (req, res) => {
const thread = new Thread().getId(id);
if (thread) {
thread.push(new Message(req.body.content, new User().getId(req.session.userid)))
const message = new Message(req.body.content, new User().getId(req.session.userid), thread).takeId().write();
thread.push(message.id)
thread.write();
res.redirect('/threads/' + id);

41
views/admin.ejs Normal file
View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="/css/styles.css" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Main page!</title>
<meta name="description" content="Akf-forum!">
<meta name="author" content="Akif9748">
<link rel="icon" type="image/x-icon" href="/images/favicon.jpg">
</head>
<body>
<!-- Navbar: -->
<a href="/"><img class="logo" src="/images/logo.jpg" alt="AKF-FORUM"></a>
<br> <button class="buyuk" onclick="window.location.href = '/threads'">THREADS</button>
<button class="buyuk" onclick="window.location.href = '/search'">SEARCH</button>
<button class="buyuk" onclick="window.location.href = '/users'">USERS</button>
<button class="buyuk" onclick="window.location.href = '/createThread/'">OPEN THREAD</button>
<h1 style="display:inline; float:right;"><a href=<%=user.getLink() %>> <%= user.name %></a>
<img class="yuvarlak" src=<%=user.avatar %> alt=<%= user.name %>>
</h1>
<hr>
<!-- Navbar end -->
<h1>Welcome to the admin panel of the forum, <%= user.name %>!</h1>
<form action="/admin/" method="POST">
<h2>Write an ID for make admin:</h2>
<input name="userid"></input>
<hr>
<button class = "buyuk" type="submit">Make admin!</button>
</form>
</body>
</html>

View File

@ -29,11 +29,18 @@
<!-- Navbar end -->
<h1>Welcome, <a href=<%=user.getLink() %>> <%= user.name %></a>
<img class="yuvarlak" src=<%=user.avatar %> alt=<%= user.name %>>
</h1>
<br>
<h1>Statics:</h1>
<ul>
<br>
You can press logout here:
<button onclick="window.location.href = '/login/'">LOGOUT</button>
</h1>
<br>
<h1>Statistics:</h1>
<ul>
<li>
<h3>Message count: <b> <%= messages %> </b></h3>
</li>
<li>
<h3>User count: <b> <%= users %> </b></h3>
</li>

View File

@ -18,7 +18,7 @@
<body>
<!-- Navbar: -->
<a href="/"><img class="logo" src="/images/logo.jpg" alt="AKF-FORUM"></a>
<br> <button class="buyuk" onclick="window.location.href = '/threads'">THREADS</button>
<br> <button class="buyuk" onclick="window.location.href = '/threads'">THREADS</button>
<button class="buyuk" onclick="window.location.href = '/search'">SEARCH</button>
<button class="buyuk" onclick="window.location.href = '/users'">USERS</button>
@ -36,35 +36,38 @@
</h1>
<div style="border: 2px solid #444444; padding: 5px;">
<h2>
<img class="yuvarlak" src=<%=thread.author.avatar %> alt=<%= thread.author.name %>>
<a style=" color: #bcbcbc; " href=<%= "/users/"+ thread.author.id %>> <%= thread.author.name %></a>:
</h2>
<p>
<%= thread.content %>
</p>
<h3 style=" text-align:right;">
<%=new Date(thread.time).toLocaleString() %>
</h3>
</div>
<br>
<% thread.messages.forEach(message=>{ %>
<% messages.forEach(message=>{ %>
<div style="border: 2px solid #444444; padding: 5px;">
<div id=<%="message-" + message.id %> style="border: 2px solid #444444; padding: 5px;">
<h2>
<img class="yuvarlak" src=<%=message.author.avatar %> alt=<%= message.author.name %>>
<a style=" color: #bcbcbc; " href=<%= "/users/"+ message.author.id %>> <%= message.author.name %></a>:
<a style=" color: #bcbcbc; " href=<%="/users/" + message.author.id %>> <%= message.author.name %></a>:
</h2>
<h2>
<%= message.content %>
</h2>
<h3 style=" text-align:right;">
<%=new Date(message.time).toLocaleString() %>
</h3>
<form style="text-align:right;display:inline;" action="/messageDelete/<%= message.id %>" method="post">
<button style="display:inline;" class="button" type="submit">DELETE</button>
</form>
<form style="text-align:right;" action="/react/<%= message.id %>" method="POST">
<h3 style="display:inline;">
<%= Object.values(message.react).filter(Boolean).length - Object.values(message.react).filter(x=>!x).length %>
</h3>
<button style="display:inline;" class="button" name="like" type="submit">+🔼</button>
<button style="display:inline;" class="button" name="dislike" type="submit">-🔽</button>
<h3 style="display:inline;">
<%=new Date(message.time).toLocaleString() %>
</h3>
</form>
</div>
<br>

View File

@ -30,33 +30,38 @@
<!-- Navbar end -->
<h1>Avatar:</h1>
<img style="width:256px;height:256px;" src=<%=member.avatar %> alt=<%= member.name %>>
<hr>
<h1>Name:</h1>
<p>
<%= member.name %>
</p>
<hr>
<h1>Created at:</h1>
<p>
<%= new Date(member.time).toLocaleString() %>
</p>
<ul>
<li>
<h1>Avatar:</h1>
<img style="width:256px;height:256px;" src=<%=member.avatar %> alt=<%= member.name %>>
</li>
<li>
<h2>Name: <%= member.name %>
</h2>
</li>
<li>
<h2>Created at:
<%= new Date(member.time).toLocaleString() %>
</h2>
</li>
<li>
<h2>Is admin? <%= member.admin ? "Yes" : "No" %>
</h2>
</li>
<li>
<h2> Message: <%= counts.message %>
</h2>
</li>
<li>
<h2> Thread: <%= counts.thread %>
</h2>
</li>
</ul>
</body>