diff --git a/.env.example b/.env.example index 3305854..8048631 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -MONGO_DB_URL = mongodb://localhost:27017/akf-forum \ No newline at end of file +MONGO_DB_URL = mongodb://localhost:27017/akf-forum +DISCORD_CLIENT = discord_app_id \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0ef53cf..ada5835 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ node_modules/ # Environment variables .env +# Config files +config.json + # Test files test.js diff --git a/README.md b/README.md index b2049e0..2ef19b3 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,18 @@ A Node.js based forum software. ## Installation - Clone or download this repo. - Run `npm i` to install **dependencies**. +- Enter your database credentials in `.env`. - Run `npm start` for run it. ### 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. -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 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? | | ----- | -------- | | Profile Message | ⚪ | -| Better Auth | ⚪ | +| Better Auth for API way | ⚪ | | mod role, permissions | ⚪ | | 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 | ⚪ | +| DC auth will store code for taking tokens, and create secret model setting | ⚪ | ## Major Version History - V4: Caching diff --git a/config.json b/config.json index 7d1d9ea..779f36b 100644 --- a/config.json +++ b/config.json @@ -8,9 +8,11 @@ "names": 25, "desp": 256 }, - "global_ratelimit":{ + "global_ratelimit": { "enabled": true, "max": 25, "windowMs": 60000 - } + }, + "discord_auth": true + } \ No newline at end of file diff --git a/config.json.example b/config.json.example new file mode 100644 index 0000000..9e5eded --- /dev/null +++ b/config.json.example @@ -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 +} \ No newline at end of file diff --git a/index.js b/index.js index c386ab5..e3ff07e 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ const { urlencoded: BP } = require('body-parser'), SES = require('express-session'); 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"), port = process.env.PORT || 3000, 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.listen(port, () => console.log(`${forum_name}-forum on port:`, port)); \ No newline at end of file +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`); +} \ No newline at end of file diff --git a/models/User.js b/models/User.js index 5373ae7..e573646 100644 --- a/models/User.js +++ b/models/User.js @@ -2,6 +2,7 @@ const mongoose = require("mongoose") const { def_theme } = require("../config.json"); const schema = new mongoose.Schema({ id: { type: String, unique: true }, + discordID: { type: String, unique: true }, name: { type: String, maxlength: 25 }, avatar: { type: String, default: "/images/avatars/default.jpg" }, time: { type: Date, default: Date.now }, diff --git a/package-lock.json b/package-lock.json index d1f4661..d63deb6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "express-session": "^1.17.2", "mongoose": "^6.6.1", "multer": "^1.4.5-lts.1", + "node-fetch": "^2.6.7", "request-ip": "^3.3.0" }, "engines": { diff --git a/package.json b/package.json index 1f7a573..a9c3179 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "express-session": "^1.17.2", "mongoose": "^6.6.1", "multer": "^1.4.5-lts.1", + "node-fetch": "^2.6.7", "request-ip": "^3.3.0" } } diff --git a/routes/discord_auth.js b/routes/discord_auth.js new file mode 100644 index 0000000..9f5c2cc --- /dev/null +++ b/routes/discord_auth.js @@ -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('')) + +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; \ No newline at end of file diff --git a/routes/login.js b/routes/login.js index ce8eb19..096a345 100644 --- a/routes/login.js +++ b/routes/login.js @@ -3,7 +3,7 @@ const { Router } = require("express"); const app = Router(); const bcrypt = require("bcrypt"); -app.get("/", (req, res) => res.reply("login", { redirect: req.query.redirect, user: null })); +app.get("/", (req, res) => res.reply("login", { redirect: req.query.redirect, user: null, discord: req.app.get("discord_auth") })); app.post("/", async (req, res) => { req.session.userID = null; diff --git a/routes/register.js b/routes/register.js index ec5c50d..2824094 100644 --- a/routes/register.js +++ b/routes/register.js @@ -4,7 +4,7 @@ const bcrypt = require("bcrypt"); const rateLimit = require('express-rate-limit'); 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({ windowMs: 24 * 60 * 60_000, max: 5, standardHeaders: true, legacyHeaders: false, diff --git a/routes/users.js b/routes/users.js index 19a7dba..840d4b6 100644 --- a/routes/users.js +++ b/routes/users.js @@ -27,7 +27,7 @@ app.get("/:id", async (req, res) => { const message = await MessageModel.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}.`); diff --git a/views/login.ejs b/views/login.ejs index 6d992d2..c291544 100644 --- a/views/login.ejs +++ b/views/login.ejs @@ -7,6 +7,7 @@ <%- include("extra/navbar") %> + Login with discord

Login

diff --git a/views/register.ejs b/views/register.ejs index d5dfe6c..f58ccee 100644 --- a/views/register.ejs +++ b/views/register.ejs @@ -8,9 +8,11 @@ <%- include("extra/navbar") %> + Login with discord

Register

+
diff --git a/views/user.ejs b/views/user.ejs index 1aa1c42..276ad59 100644 --- a/views/user.ejs +++ b/views/user.ejs @@ -13,7 +13,9 @@
- + <% if (!member.discordID && user?.id === member.id) { %> + Login with discord + <% } %> <% if (user?.admin || user?.id === member.id) { %> Upload avatar