mirror of
https://github.com/Akif9748/akf-forum.git
synced 2024-11-22 03:50:41 +03:00
Added discord auth support
This commit is contained in:
parent
e1aeb8fd14
commit
598e48e25d
16 changed files with 115 additions and 12 deletions
|
@ -1 +1,2 @@
|
||||||
MONGO_DB_URL = mongodb://localhost:27017/akf-forum
|
MONGO_DB_URL = mongodb://localhost:27017/akf-forum
|
||||||
|
DISCORD_CLIENT = discord_app_id
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -4,6 +4,9 @@ node_modules/
|
||||||
# Environment variables
|
# Environment variables
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
# Config files
|
||||||
|
config.json
|
||||||
|
|
||||||
# Test files
|
# Test files
|
||||||
test.js
|
test.js
|
||||||
|
|
||||||
|
|
14
README.md
14
README.md
|
@ -4,11 +4,18 @@ A Node.js based forum software.
|
||||||
## Installation
|
## Installation
|
||||||
- Clone or download this repo.
|
- Clone or download this repo.
|
||||||
- Run `npm i` to install **dependencies**.
|
- Run `npm i` to install **dependencies**.
|
||||||
|
- Enter your database credentials in `.env`.
|
||||||
- Run `npm start` for run it.
|
- Run `npm start` for run it.
|
||||||
|
|
||||||
### Extra
|
### Extra
|
||||||
Run `node util/reset` to **reset the database** for duplicate key errors, and run `node util/admin` for give admin perms to first member.
|
Run `node util/reset` to **reset the database** for duplicate key errors, and run `node util/admin` for give admin perms to first member.
|
||||||
Edit `config.json` for default themes of users, and forum name...
|
Edit `config.json` for default themes (`black` or `default`) of users, and forum name, meta description, character limits, discord auth enabler, global ratelimit.
|
||||||
|
|
||||||
|
### DISCORD AUTH:
|
||||||
|
`discord_auth: true` in config.json.
|
||||||
|
Enter application id to `.env`.
|
||||||
|
Create a redirect url in discord developer portal:
|
||||||
|
`https://forum_url.com/discord_auth/hash`
|
||||||
|
|
||||||
## API
|
## API
|
||||||
Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn about API in `APIDOCS.md`.
|
Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn about API in `APIDOCS.md`.
|
||||||
|
@ -35,11 +42,12 @@ Akf-forum has got an API for AJAX (fetch), other clients etc. And, you can learn
|
||||||
| To do | Is done? |
|
| To do | Is done? |
|
||||||
| ----- | -------- |
|
| ----- | -------- |
|
||||||
| Profile Message | ⚪ |
|
| Profile Message | ⚪ |
|
||||||
| Better Auth | ⚪ |
|
| Better Auth for API way | ⚪ |
|
||||||
| mod role, permissions | ⚪ |
|
| mod role, permissions | ⚪ |
|
||||||
| upload other photos, model for it | ⚪ |
|
| upload other photos, model for it | ⚪ |
|
||||||
| categories page is need a update | ⚪ |
|
| categories page is need a update, thread count in category | ⚪ |
|
||||||
| preview for send messages in markdown format | ⚪ |
|
| preview for send messages in markdown format | ⚪ |
|
||||||
|
| DC auth will store code for taking tokens, and create secret model setting | ⚪ |
|
||||||
|
|
||||||
## Major Version History
|
## Major Version History
|
||||||
- V4: Caching
|
- V4: Caching
|
||||||
|
|
|
@ -8,9 +8,11 @@
|
||||||
"names": 25,
|
"names": 25,
|
||||||
"desp": 256
|
"desp": 256
|
||||||
},
|
},
|
||||||
"global_ratelimit":{
|
"global_ratelimit": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"max": 25,
|
"max": 25,
|
||||||
"windowMs": 60000
|
"windowMs": 60000
|
||||||
}
|
},
|
||||||
|
"discord_auth": true
|
||||||
|
|
||||||
}
|
}
|
17
config.json.example
Normal file
17
config.json.example
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"def_theme": "default",
|
||||||
|
"forum_name": "akf",
|
||||||
|
"description": "Akf-forum!",
|
||||||
|
"limits": {
|
||||||
|
"title": 128,
|
||||||
|
"message": 1024,
|
||||||
|
"names": 25,
|
||||||
|
"desp": 256
|
||||||
|
},
|
||||||
|
"global_ratelimit": {
|
||||||
|
"enabled": true,
|
||||||
|
"max": 25,
|
||||||
|
"windowMs": 60000
|
||||||
|
},
|
||||||
|
"discord_auth": false
|
||||||
|
}
|
10
index.js
10
index.js
|
@ -4,7 +4,7 @@ const { urlencoded: BP } = require('body-parser'),
|
||||||
SES = require('express-session');
|
SES = require('express-session');
|
||||||
|
|
||||||
const
|
const
|
||||||
{ def_theme, forum_name, description, limits, global_ratelimit: RLS } = require("./config.json"),
|
{ def_theme, forum_name, description, limits, global_ratelimit: RLS, discord_auth } = require("./config.json"),
|
||||||
{ UserModel, BanModel } = require("./models"),
|
{ UserModel, BanModel } = require("./models"),
|
||||||
port = process.env.PORT || 3000,
|
port = process.env.PORT || 3000,
|
||||||
mongoose = require("mongoose"),
|
mongoose = require("mongoose"),
|
||||||
|
@ -50,4 +50,10 @@ for (const file of fs.readdirSync("./routes"))
|
||||||
|
|
||||||
app.all("*", (req, res) => res.error(404, "We have not got this page."));
|
app.all("*", (req, res) => res.error(404, "We have not got this page."));
|
||||||
|
|
||||||
app.listen(port, () => console.log(`${forum_name}-forum on port:`, port));
|
const server = app.listen(port, () => console.log(`${forum_name}-forum on port:`, port));
|
||||||
|
|
||||||
|
if (discord_auth) {
|
||||||
|
const { address } = server.address();
|
||||||
|
console.log(`https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT}&redirect_uri=http%3A%2F%2F${address == '::' ? 'localhost:' + port : address}%2Fdiscord_auth%2Fhash&response_type=token&scope=identify`)
|
||||||
|
app.set("discord_auth", `https://discord.com/api/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT}&redirect_uri=http%3A%2F%2F${address == '::' ? 'localhost:' + port : address}%2Fdiscord_auth%2Fhash&response_type=token&scope=identify`);
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ const mongoose = require("mongoose")
|
||||||
const { def_theme } = require("../config.json");
|
const { def_theme } = require("../config.json");
|
||||||
const schema = new mongoose.Schema({
|
const schema = new mongoose.Schema({
|
||||||
id: { type: String, unique: true },
|
id: { type: String, unique: true },
|
||||||
|
discordID: { type: String, unique: true },
|
||||||
name: { type: String, maxlength: 25 },
|
name: { type: String, maxlength: 25 },
|
||||||
avatar: { type: String, default: "/images/avatars/default.jpg" },
|
avatar: { type: String, default: "/images/avatars/default.jpg" },
|
||||||
time: { type: Date, default: Date.now },
|
time: { type: Date, default: Date.now },
|
||||||
|
|
1
package-lock.json
generated
1
package-lock.json
generated
|
@ -18,6 +18,7 @@
|
||||||
"express-session": "^1.17.2",
|
"express-session": "^1.17.2",
|
||||||
"mongoose": "^6.6.1",
|
"mongoose": "^6.6.1",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
"request-ip": "^3.3.0"
|
"request-ip": "^3.3.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
"express-session": "^1.17.2",
|
"express-session": "^1.17.2",
|
||||||
"mongoose": "^6.6.1",
|
"mongoose": "^6.6.1",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
"request-ip": "^3.3.0"
|
"request-ip": "^3.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
58
routes/discord_auth.js
Normal file
58
routes/discord_auth.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
const { Router } = require("express")
|
||||||
|
const { UserModel } = require("../models");
|
||||||
|
const fetch = require("node-fetch");
|
||||||
|
const app = Router();
|
||||||
|
|
||||||
|
app.get("/hash", (req, res) => res.send('<script>location.href=location.href.replace("#","?").replace("discord_auth/hash","discord_auth");</script>'))
|
||||||
|
|
||||||
|
app.get("/", async (req, res) => {
|
||||||
|
const { access_token, token_type } = req.query;
|
||||||
|
if (!access_token) return;
|
||||||
|
try {
|
||||||
|
const discord = await fetch('https://discord.com/api/users/@me', {
|
||||||
|
headers: { authorization: `${token_type} ${access_token}` }
|
||||||
|
}).then(res => res.json());
|
||||||
|
|
||||||
|
const forum = await UserModel.findOne({ discordID: discord.id });
|
||||||
|
|
||||||
|
|
||||||
|
if (req.user) {
|
||||||
|
if (req.user.discordID)
|
||||||
|
return res.error(403, "Your forum account is already linked to a discord account.");
|
||||||
|
|
||||||
|
if (forum)
|
||||||
|
return res.error(403, "This discord account is already linked to a forum account.");
|
||||||
|
|
||||||
|
req.user.discordID = discord.id;
|
||||||
|
await req.user.save();
|
||||||
|
return res.send("Your discord account has been linked to your forum account.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (forum) {
|
||||||
|
req.session.userID = forum.id;
|
||||||
|
return res.redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = discord.username + discord.discriminator;
|
||||||
|
while (await UserModel.findOne({ name }))
|
||||||
|
name += Math.floor(Math.random() * 2);
|
||||||
|
|
||||||
|
const user2 = new UserModel({
|
||||||
|
name, discordID: discord.id,
|
||||||
|
avatar: `https://cdn.discordapp.com/avatars/${discord.id}/${discord.avatar}.png?size=256`
|
||||||
|
});
|
||||||
|
|
||||||
|
await user2.takeId();
|
||||||
|
await user2.save();
|
||||||
|
|
||||||
|
req.session.userID = user2.id;
|
||||||
|
|
||||||
|
res.redirect("/");
|
||||||
|
} catch (error) {
|
||||||
|
res.error(500, "Something went wrong");
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
|
@ -3,7 +3,7 @@ const { Router } = require("express");
|
||||||
const app = Router();
|
const app = Router();
|
||||||
const bcrypt = require("bcrypt");
|
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, discord: req.app.get("discord_auth") }));
|
||||||
|
|
||||||
app.post("/", async (req, res) => {
|
app.post("/", async (req, res) => {
|
||||||
req.session.userID = null;
|
req.session.userID = null;
|
||||||
|
|
|
@ -4,7 +4,7 @@ const bcrypt = require("bcrypt");
|
||||||
const rateLimit = require('express-rate-limit');
|
const rateLimit = require('express-rate-limit');
|
||||||
const app = Router();
|
const app = Router();
|
||||||
|
|
||||||
app.get("/", (req, res) => res.reply("register", { user: null }));
|
app.get("/", (req, res) => res.reply("register", { user: null, discord: req.app.get("discord_auth") }));
|
||||||
|
|
||||||
app.post("/", rateLimit({
|
app.post("/", rateLimit({
|
||||||
windowMs: 24 * 60 * 60_000, max: 5, standardHeaders: true, legacyHeaders: false,
|
windowMs: 24 * 60 * 60_000, max: 5, standardHeaders: true, legacyHeaders: false,
|
||||||
|
|
|
@ -27,7 +27,7 @@ app.get("/:id", async (req, res) => {
|
||||||
|
|
||||||
const message = await MessageModel.count({ authorID: id });
|
const message = await MessageModel.count({ authorID: id });
|
||||||
const thread = await ThreadModel.count({ authorID: id });
|
const thread = await ThreadModel.count({ authorID: id });
|
||||||
res.reply("user", { member, counts: { message, thread } })
|
res.reply("user", { member, counts: { message, thread}, discord: req.app.get("discord_auth") })
|
||||||
}
|
}
|
||||||
else res.error(404, `We don't have any user with id ${id}.`);
|
else res.error(404, `We don't have any user with id ${id}.`);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<link rel="stylesheet" href="/css/login.css" />
|
<link rel="stylesheet" href="/css/login.css" />
|
||||||
|
|
||||||
<%- include("extra/navbar") %>
|
<%- include("extra/navbar") %>
|
||||||
|
<a href="<%=discord%>" class="btn-outline-primary">Login with discord</a>
|
||||||
|
|
||||||
<h1 class="title">Login</h1>
|
<h1 class="title">Login</h1>
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,11 @@
|
||||||
<link rel="stylesheet" href="/css/login.css" />
|
<link rel="stylesheet" href="/css/login.css" />
|
||||||
|
|
||||||
<%- include("extra/navbar") %>
|
<%- include("extra/navbar") %>
|
||||||
|
<a href="<%=discord%>" class="btn-outline-primary">Login with discord</a>
|
||||||
|
|
||||||
<h1 class="title">Register</h1>
|
<h1 class="title">Register</h1>
|
||||||
|
|
||||||
|
|
||||||
<form action="/register" method="post">
|
<form action="/register" method="post">
|
||||||
<input type="text" name="username" maxlength="25" placeholder="Username" class="input" required>
|
<input type="text" name="username" maxlength="25" placeholder="Username" class="input" required>
|
||||||
<input type="password" name="password" maxlength="25" placeholder="Password" class="input" required>
|
<input type="password" name="password" maxlength="25" placeholder="Password" class="input" required>
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
|
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
<% if (!member.discordID && user?.id === member.id) { %>
|
||||||
|
<a href="<%=discord%>" class="btn-outline-primary">Login with discord</a>
|
||||||
|
<% } %>
|
||||||
<% if (user?.admin || user?.id === member.id) { %>
|
<% if (user?.admin || user?.id === member.id) { %>
|
||||||
<a href="/users/<%=member.id%>/avatar" class="btn-outline-primary">Upload avatar</a>
|
<a href="/users/<%=member.id%>/avatar" class="btn-outline-primary">Upload avatar</a>
|
||||||
<link rel="stylesheet" href="/css/modal.css" />
|
<link rel="stylesheet" href="/css/modal.css" />
|
||||||
|
|
Loading…
Reference in a new issue