Added API. And META informations are fixed

This commit is contained in:
Akif9748 2022-03-20 21:37:47 +03:00
parent 40b5126b9d
commit bde409a702
21 changed files with 317 additions and 45 deletions

73
APIDOCS.md Normal file
View file

@ -0,0 +1,73 @@
# API documentation of Akf-forum
<img src="https://raw.githubusercontent.com/Akif9748/akf-forum/main/public/images/logo.jpg" align="right" width="300px" />
Akf-forum has got an API for other clients etc.
You can find an example in apitest.py.
## Authorization
You need this headers for send request to API:
```jsonc
{
"username": "testUser",
"password": "testPassword"
}
```
## How to request?
### Request type:
`GET /api/action/id`
### "action" types:
- `message`
(for now, only message.)
### "id":
ID for action type.
### Example request:
```GET /api/message/1```
#### Example API Output:
```json
{
"status": 200,
"result":
{
"content": "First message",
"time": 1647178873587,
"deleted": false,
"edited": false,
"react": {},
"id": 1,
"author": {
"name": "ForumcuCocuk",
"avatar": "/images/guest.png",
"time": 1647177723873,
"admin": true,
"id": 1
},
"thread": {
"author": {
"name": "Akif9748",
"avatar": "/images/guest.png",
"time": 1647177705200,
"admin": true,
"id": 0
},
"title": "First Thread",
"messages": [0, 1],
"time": 1647178870047,
"deleted": false,
"id": 0
}
}
}
```

View file

@ -8,14 +8,16 @@ A forum script written in Node.js.
- Write `npm i` to install **dependencies**. - Write `npm i` to install **dependencies**.
- Write `npm restart` for reset database, and `npm start` for run it. - Write `npm restart` for reset database, and `npm start` for run it.
## API
Akf-forum has got an API for other clients etc. You can test api with run apitest.py.
And, you can learn informations about API in `APIDOCS.md`.
## Credits ## Credits
* [Akif9748](https://github.com/Akif9748) - Project owner * [Akif9748](https://github.com/Akif9748) - Project owner, main developer
* [Camroku](https://github.com/Camroku) - Made stylesheets * [Camroku](https://github.com/Camroku) - Made stylesheets
## To Do: ## To Do:
- Admin panel, delete, edit messages. - Better method for params in URL
## Roadmap ## Roadmap
- [x] User - [x] User
@ -26,21 +28,20 @@ A forum script written in Node.js.
- [x] Message count - [x] Message count
- [x] Delete User - [x] Delete User
- [ ] Singature & About me - [ ] Singature & About me
- [ ] Edit user
- [ ] Messages - [ ] Messages
- [x] Send message - [x] Send message
- [x] Delete message - [x] Delete message
- [ ] Edit message - [ ] Edit message
- [x] React message - [x] React message
- [ ] Forums - [ ] Threads
- [ ] Category system - [ ] Edit it!
- [ ] Create category - [ ] Delete it!
- [ ] Delete category
- [ ] Edit category
- [ ] Move category
- [ ] Other - [ ] Other
- [ ] API - [x] API
- [ ] Other client for forum via API - [x] Other client for forum via API
- [ ] Footer... - [ ] Footer of the site
- [ ] Multiple theme support
- [ ] Search - [ ] Search
- [x] New Thread theme, better render for messages - [x] New Thread theme, better render for messages
## Image: ## Image:

81
api/index.js Normal file
View file

@ -0,0 +1,81 @@
const { User, Message, Thread } = require("../classes");
const db = require("quick.db");
class ApiResponse {
constructor(status, result) {
this.status = status;
this.result = result;
}
}
const { request, response } = require("express");
/**
* For intellisense
* @param {request} req
* @param {response} res
*/
module.exports = (req, res) => {
const error = (status, error) =>
res.status(status).json(new ApiResponse(403, { error }))
/**
* AUTH TYPE:
headers:
{
username: "Username for client",
password: "Password of selected username for client"
}
*/
const { username = null, password = null } = req.headers;
if (!username || !password)
return error(403, "Headers are missing")
const user = db.get("secret." + username);
if (!user)
return error(403, "We have not got any user has got this name")
if (user.key !== password)
return error(403, 'Incorrect Password!')
/**
* REQUEST TYPE:
* GET /api/action/id
*
* @example message action:
* GET /api/message/0
*
*/
const { action } = req.params;
switch (action) {
case "message":
const { id = null } = req.params;
if (!id) return error(403, "Missing id in query")
const message = new Message().getId(id);
if (!message || message.deleted) return error(403, "We have not got any message declared as this id.");
res.status(200).json(new ApiResponse(200, message));
break;
default:
return error(403, "Missing/undefined param: action");
}
}

50
apitest.py Normal file
View file

@ -0,0 +1,50 @@
import requests
# Headers for login to Akf-forum
headers = {
"username": "testUser",
"password": "testPassword"
}
r = requests.get("http://localhost:3000/api/message/1/", headers=headers)
print(r.json())
example_response = {
"status": 200,
"result":
{ # content of message
"content": "a",
# author of message
"author": {
"name": "ForumcuCocuk",
"avatar": "/images/guest.png",
"time": 1647177723873,
"admin": True,
"id": 1
},
# UNIX Timestamp of message
"time": 1647178873587,
# thread information of message
"thread": {
"author": {
"name": "ForumcuCocuk",
"avatar": "/images/guest.png",
"time": 1647177723873,
"admin": True,
"id": 1
},
"title": "My",
"messages": [0], # ids of messages
"time": 1647178870047,
"deleted": False,
"id": "0"
},
# Other informations about message
"deleted": False,
"edited": False,
"react": {},
"id": "1"
}
}

View file

@ -1,6 +1,9 @@
const Message = require("./message") const Message = require("./message");
const Thread = require("./thread") const Thread = require("./thread");
const User = require("./user") const User = require("./user");
const React = require("./react") const React = require("./react");
module.exports = { Message, Thread, User, React } /**
* Classes for akf-forum;
*/
module.exports = { Message, Thread, User, React };

View file

@ -17,10 +17,10 @@ module.exports = class Message {
this.react = react; this.react = react;
} }
getId(id = this.id) { getId(id = this.id) {
const message = db.get("messages" ).find(msg => msg.id == id); const message = db.get("messages").find(msg => msg.id == id);
if (!message) return null; if (!message) return null;
this.id = id; this.id = Number(id);
const { content, author, thread = new Thread(), time = new Date().getTime(), deleted = false, edited = false, react = {} } = message; const { content, author, thread = new Thread(), time = new Date().getTime(), deleted = false, edited = false, react = {} } = message;
this.content = content; this.content = content;
this.thread = thread; this.thread = thread;

View file

@ -19,7 +19,7 @@ module.exports = class Thread {
getId(id = this.id) { getId(id = this.id) {
const thread = db.get("threads").find(t => t.id == id); const thread = db.get("threads").find(t => t.id == id);
if (!thread) return null; if (!thread) return null;
this.id = id; this.id = Number(id);
const { title, author, messages = [], time = new Date().getTime(), deleted = false } = thread; const { title, author, messages = [], time = new Date().getTime(), deleted = false } = thread;
this.title = title this.title = title
this.author = author this.author = author

View file

@ -18,6 +18,11 @@ app.set("view engine", "ejs");
//Temp: //Temp:
app.get("/", (req, res) => res.redirect("/index")); app.get("/", (req, res) => res.redirect("/index"));
/**
* API:
*/
app.get("/api/:action/:id", require("./api"));
for (const type of fs.readdirSync("./routes")) for (const type of fs.readdirSync("./routes"))
for (const file of fs.readdirSync("./routes/" + type)) for (const file of fs.readdirSync("./routes/" + type))
app[type](`/${file.replace(".js", "")}*`, require(`./routes/${type}/${file}`)) app[type](`/${file.replace(".js", "")}*`, require(`./routes/${type}/${file}`))
@ -28,3 +33,4 @@ app.post('*', (req, res) => error(res, 404, "We have not got this page."));
const port = process.env.PORT || 3000; const port = process.env.PORT || 3000;
app.listen(port, () => console.log("SERVER ON PORT:", port)); app.listen(port, () => console.log("SERVER ON PORT:", port));

4
package-lock.json generated
View file

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

View file

@ -1,5 +1,5 @@
{ {
"name": "karum", "name": "akf-lang",
"version": "2.1.0", "version": "2.1.0",
"description": "A forum script written in Node.js", "description": "A forum script written in Node.js",
"main": "index.js", "main": "index.js",
@ -9,7 +9,7 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/Akif9748/karum.git" "url": "git+https://github.com/Akif9748/akf-lang.git"
}, },
"author": "Akif9748", "author": "Akif9748",
"contributors": [ "contributors": [
@ -17,9 +17,9 @@
], ],
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"bugs": { "bugs": {
"url": "https://github.com/Akif9748/karum/issues" "url": "https://github.com/Akif9748/akf-lang/issues"
}, },
"homepage": "https://github.com/Akif9748/karum#readme", "homepage": "https://github.com/Akif9748/akf-lang#readme",
"dependencies": { "dependencies": {
"body-parser": "^1.19.2", "body-parser": "^1.19.2",
"ejs": "^3.1.6", "ejs": "^3.1.6",

View file

@ -1,5 +1,4 @@
const { User } = require("../../classes/index"); const { User } = require("../../classes/index");
const { get } = require("quick.db");
const error = require("../../errors/error.js"); const error = require("../../errors/error.js");
module.exports = (req, res) => { module.exports = (req, res) => {

9
routes/get/userEdit.js Normal file
View file

@ -0,0 +1,9 @@
const { User } = require("../../classes/index");
module.exports = (req, res) => {
if (!req.session.loggedin) return res.redirect('/login');
const user = new User().getId(req.session.userid);
res.render("userEdit", { user })
}

View file

@ -1,6 +1,6 @@
const db = require("quick.db"); const db = require("quick.db");
const error = require("../../errors/error.js") const error = require("../../errors/error.js")
const { User, Message } = require("../../classes/index"); const { User } = require("../../classes/index");
module.exports = (req, res) => { module.exports = (req, res) => {
req.session.loggedin = false; req.session.loggedin = false;
@ -12,19 +12,19 @@ module.exports = (req, res) => {
const user = db.get("secret." + username) const user = db.get("secret." + username)
if (user) { if (user) {
// Authenticate the user // Authenticate the user
if (user.key !== password) return error(res, 404, 'Incorrect Password!') if (user.key !== password) return error(res, 403, 'Incorrect Password!')
if (new User().getName(username).deleted) return error(res, 404, 'Incorrect Username and/or Password!') if (new User().getName(username).deleted) return error(res, 403, 'Incorrect Username and/or Password!')
req.session.loggedin = true; req.session.loggedin = true;
req.session.username = username; req.session.username = username;
req.session.userid = user.id; req.session.userid = user.id;
res.redirect('/'); res.redirect('/');
} else } else
error(res, 404, 'Incorrect Username and/or Password!') error(res, 403, 'Incorrect Username and/or Password!')
} else } else
error(res, 404, "You forgot entering some values") error(res, 403, "You forgot entering some values")

View file

@ -5,8 +5,8 @@
<link rel="stylesheet" href="/css/styles.css" /> <link rel="stylesheet" href="/css/styles.css" />
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Main page!</title> <title>Admin panel!</title>
<meta name="description" content="Akf-forum!"> <meta name="description" content="Admin panel of Akf-forum!">
<meta name="author" content="Akif9748"> <meta name="author" content="Akif9748">
<link rel="icon" type="image/x-icon" href="/images/favicon.jpg"> <link rel="icon" type="image/x-icon" href="/images/favicon.jpg">

View file

@ -6,7 +6,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Main page!</title> <title>Main page!</title>
<meta name="description" content="Akf-forum!"> <meta name="description" content="Main page of Akf-forum!">
<meta name="author" content="Akif9748"> <meta name="author" content="Akif9748">
<link rel="icon" type="image/x-icon" href="/images/favicon.jpg"> <link rel="icon" type="image/x-icon" href="/images/favicon.jpg">

View file

@ -6,7 +6,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>LOGIN</title> <title>LOGIN</title>
<meta name="description" content="Akf-forum!"> <meta name="description" content="Login to Akf-forum!">
<meta name="author" content="Akif9748"> <meta name="author" content="Akif9748">
<link rel="icon" type="image/x-icon" href="/images/favicon.jpg"> <link rel="icon" type="image/x-icon" href="/images/favicon.jpg">

View file

@ -6,7 +6,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Open Thread</title> <title>Open Thread</title>
<meta name="description" content="Akf-forum!"> <meta name="description" content="Open thread in Akf-forum!">
<meta name="author" content="Akif9748"> <meta name="author" content="Akif9748">
<link rel="icon" type="image/x-icon" href="/images/favicon.jpg"> <link rel="icon" type="image/x-icon" href="/images/favicon.jpg">

View file

@ -6,7 +6,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Register</title> <title>Register</title>
<meta name="description" content="Akf-forum!"> <meta name="description" content="Register to Akf-forum!">
<link rel="icon" type="image/x-icon" href="/images/favicon.jpg"> <link rel="icon" type="image/x-icon" href="/images/favicon.jpg">
</head> </head>

View file

@ -8,7 +8,7 @@
<title> <title>
<%= thread.title %> <%= thread.title %>
</title> </title>
<meta name="description" content="Akf-forum!"> <meta name="description" content="Thread page of Akf-forum!">
<meta name="author" content="Akif9748"> <meta name="author" content="Akif9748">
<link rel="icon" type="image/x-icon" href="/images/favicon.jpg"> <link rel="icon" type="image/x-icon" href="/images/favicon.jpg">

View file

@ -5,8 +5,8 @@
<link rel="stylesheet" href="/css/styles.css" /> <link rel="stylesheet" href="/css/styles.css" />
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Main page!</title> <title>Threads!</title>
<meta name="description" content="Akf-forum!"> <meta name="description" content="Threads' page of Akf-forum!">
<meta name="author" content="Akif9748"> <meta name="author" content="Akif9748">
<link rel="icon" type="image/x-icon" href="/images/favicon.jpg"> <link rel="icon" type="image/x-icon" href="/images/favicon.jpg">

50
views/userEdit.ejs Normal file
View file

@ -0,0 +1,50 @@
<!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>Edit profile</title>
<meta name="description" content="Akf-forum!">
<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 -->
<hr>
<h1>This page is not ready.</h1>
<hr>
<!--- <form action="/userEdit/<%= %>" method="post">
<input type="text" name="username" placeholder="Username" id="username" required>
<input type="password" name="password" placeholder="Password" id="password" required>
<input type="text" name="avatar" placeholder="Avatar URL" id="avatar">
<input type="submit" value="Register">
</form>
-->
</body>
</html>