diff --git a/src-slowcord/README.md b/src-slowcord/README.md deleted file mode 100644 index ac42117a..00000000 --- a/src-slowcord/README.md +++ /dev/null @@ -1 +0,0 @@ -Additional resources/services for [Slowcord](https://slowcord.maddy.k.vu/login) diff --git a/src-slowcord/bot/.vscode/launch.json b/src-slowcord/bot/.vscode/launch.json deleted file mode 100644 index c1c765a8..00000000 --- a/src-slowcord/bot/.vscode/launch.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "configurations": [ - { - "name": "Slowcord Bot", - "program": "${workspaceFolder}/build/index.js", - "request": "launch", - "skipFiles": ["/**"], - "type": "node", - "preLaunchTask": "npm: build" - } - ] -} diff --git a/src-slowcord/bot/.vscode/tasks.json b/src-slowcord/bot/.vscode/tasks.json deleted file mode 100644 index be345ecd..00000000 --- a/src-slowcord/bot/.vscode/tasks.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "npm", - "script": "build", - "group": "build", - "problemMatcher": [], - "label": "npm: build", - "detail": "tsc -b" - } - ] -} diff --git a/src-slowcord/bot/package-lock.json b/src-slowcord/bot/package-lock.json deleted file mode 100644 index 7d1c59ed..00000000 Binary files a/src-slowcord/bot/package-lock.json and /dev/null differ diff --git a/src-slowcord/bot/package.json b/src-slowcord/bot/package.json deleted file mode 100644 index 454f84cb..00000000 --- a/src-slowcord/bot/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "bot", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "build": "tsc -b", - "start": "node build/index.js" - }, - "author": "", - "license": "ISC", - "dependencies": { - "fosscord-server": "file:../..", - "fosscord-gopnik": "^1.0.0", - "mysql": "^2.18.1", - "typescript": "^4.7.4" - } -} diff --git a/src-slowcord/bot/src/Bot.ts b/src-slowcord/bot/src/Bot.ts deleted file mode 100644 index cf3ff09f..00000000 --- a/src-slowcord/bot/src/Bot.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Message } from "discord.js"; -import { Client } from "fosscord-gopnik/build/lib"; // huh? oh well. some bugs in my lib Ig - -import { Command, getCommands } from "./commands/index.js"; - -export default class Bot { - client: Client; - commands: { [key: string]: Command } = {}; - - constructor(client: Client) { - this.client = client; - } - - onReady = async () => { - this.commands = await getCommands(); - - console.log(`Logged in as ${this.client.user!.tag}`); - - this.client.user!.setPresence({ - activities: [ - { - name: "EVERYTHING", - type: "WATCHING", - }, - ], - }); - }; - - onMessageCreate = async (msg: Message) => { - const prefix = process.env.PREFIX as string; - if (msg.author.bot) return; - if (!msg.content || msg.content.indexOf(prefix) === -1) return; - - const content = msg.content.slice(prefix.length).split(" "); - const cmd = content.shift(); - if (!cmd) return; - const args = content; - - const command = this.commands[cmd]; - if (!command) return; - - await command.exec({ - user: msg.author, - member: msg.member, - guild: msg.guild, - message: msg, - args: args, - }); - }; -} diff --git a/src-slowcord/bot/src/commands/index.ts b/src-slowcord/bot/src/commands/index.ts deleted file mode 100644 index 0130b2bc..00000000 --- a/src-slowcord/bot/src/commands/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Message, GuildMember, Guild, User } from "discord.js"; -import fs from "fs"; - -export type CommandContext = { - user: User; - guild: Guild | null; - member: GuildMember | null; - message: Message; - args: string[]; -}; - -export type Command = { - name: string; - exec: (ctx: CommandContext) => any; -}; - -const walk = async (path: string) => { - const files = fs.readdirSync(path); - const out = []; - for (var file of files) { - if (fs.statSync(`${path}/${file}`).isDirectory()) continue; - if (file.indexOf("index") !== -1) continue; - if (file.indexOf(".js") !== file.length - 3) continue; - var imported = (await import(`./${file}`)).default; - out.push(imported); - } - return out; -}; - -export const getCommands = async () => { - const map: { [key: string]: Command } = {}; - for (var cmd of await walk("./build/commands")) { - map[cmd.name] = cmd; - } - return map; -}; diff --git a/src-slowcord/bot/src/commands/instance.ts b/src-slowcord/bot/src/commands/instance.ts deleted file mode 100644 index 170d8f76..00000000 --- a/src-slowcord/bot/src/commands/instance.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Command } from "./index.js"; -import { User, Guild, Message } from "@fosscord/util"; - -const cache: { [key: string]: number } = { - users: 0, - guilds: 0, - messages: 0, - lastChecked: 0, -}; - -export default { - name: "instance", - exec: async ({ message }) => { - if ( - Date.now() > - cache.lastChecked + parseInt(process.env.CACHE_TTL as string) - ) { - cache.users = await User.count(); - cache.guilds = await Guild.count(); - cache.messages = await Message.count(); - cache.lastChecked = Date.now(); - } - - return message.reply({ - embeds: [ - { - title: "Instance Stats", - description: - "For more indepth information, check out https://grafana.understars.dev", - footer: { - text: `Last checked: ${Math.floor( - (Date.now() - cache.lastChecked) / (1000 * 60), - )} minutes ago`, - }, - fields: [ - { - inline: true, - name: "Total Users", - value: cache.users.toString(), - }, - { - inline: true, - name: "Total Guilds", - value: cache.guilds.toString(), - }, - { - inline: true, - name: "Total Messages", - value: cache.messages.toString(), - }, - ], - }, - ], - }); - }, -} as Command; diff --git a/src-slowcord/bot/src/index.ts b/src-slowcord/bot/src/index.ts deleted file mode 100644 index fa97313f..00000000 --- a/src-slowcord/bot/src/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import "dotenv/config"; -import Fosscord from "fosscord-gopnik"; -import Bot from "./Bot.js"; // huh? -import { initDatabase } from "fosscord-server/src/util"; - -const client = new Fosscord.Client({ - intents: ["GUILD_MESSAGES"], - - http: { - api: process.env.ENDPOINT_API, - cdn: process.env.ENDPOINT_CDN, - invite: process.env.ENDPOINT_INV, - }, -}); - -const bot = new Bot(client); - -client.on("ready", bot.onReady); -client.on("messageCreate", bot.onMessageCreate); - -(async () => { - await initDatabase(); - await client.login(process.env.TOKEN); -})(); diff --git a/src-slowcord/bot/tsconfig.json b/src-slowcord/bot/tsconfig.json deleted file mode 100644 index f055681a..00000000 --- a/src-slowcord/bot/tsconfig.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Projects */ - // "incremental": true, /* Enable incremental compilation */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "ES6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - "lib": [ - "ES2021" - ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, - // "jsx": "preserve", /* Specify what JSX code is generated. */ - "experimentalDecorators": true /* Enable experimental support for TC39 stage 2 draft decorators. */, - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ - // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - - /* Modules */ - "module": "CommonJS" /* Specify what module code is generated. */, - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ - // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true /* Create source map files for emitted JavaScript files. */, - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./build" /* Specify an output folder for all emitted files. */, - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - - /* Type Checking */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ - // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ - "strictPropertyInitialization": false /* Check for class properties that are declared but not set in the constructor. */, - // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ - // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/src-slowcord/login/.env.template b/src-slowcord/login/.env.template deleted file mode 100644 index 34f6abeb..00000000 --- a/src-slowcord/login/.env.template +++ /dev/null @@ -1,5 +0,0 @@ -DATABASE=../bundle/database.db -PORT=3010 -DISCORD_CLIENT_ID= -DISCORD_SECRET= -DISCORD_REDIRECT= \ No newline at end of file diff --git a/src-slowcord/login/package-lock.json b/src-slowcord/login/package-lock.json deleted file mode 100644 index 72c8c257..00000000 Binary files a/src-slowcord/login/package-lock.json and /dev/null differ diff --git a/src-slowcord/login/package.json b/src-slowcord/login/package.json deleted file mode 100644 index cc10ff7f..00000000 --- a/src-slowcord/login/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "slowcord-login", - "version": "1.0.0", - "description": "Slowcord login service", - "main": "build/index.js", - "types": "src/index.ts", - "scripts": { - "build": "tsc -b", - "start": "node build/index.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/maddyunderstars/fosscord-server.git" - }, - "author": "MaddyUnderStars", - "license": "AGPL-3.0-only", - "bugs": { - "url": "https://github.com/maddyunderstars/fosscord-server/issues" - }, - "homepage": "https://github.com/maddyunderstars/fosscord-server#readme", - "dependencies": { - "fosscord-server": "file:../../", - "cookie-parser": "^1.4.6", - "dotenv": "^16.0.1", - "express": "^4.18.1", - "node-fetch": "^3.2.6", - "sqlite3": "^5.0.8" - }, - "devDependencies": { - "@types/cookie-parser": "^1.4.3", - "@types/express": "^4.17.13", - "@types/node": "^18.0.0" - }, - "type": "module" -} diff --git a/src-slowcord/login/public/css/index.css b/src-slowcord/login/public/css/index.css deleted file mode 100644 index cf294ccb..00000000 --- a/src-slowcord/login/public/css/index.css +++ /dev/null @@ -1,113 +0,0 @@ -/* Can you tell I like flexbox? */ - -html { - --background-primary: rgb(22, 23, 25); - --background-secondary: rgb(15, 16, 18); - --foreground-primary: rgb(200, 200, 200); - --background-login-discord: #5865f2; - - background: url("https://slowcord.maddy.k.vu/assets/background.png"); - background-size: 100% 100%; - background-repeat: no-repeat; - - font-family: "Montserrat", sans-serif; - - color: var(--foreground-primary); -} - -* { - margin: 0; - padding: 0; -} - -.content { - display: flex; - width: 100vw; - height: 100vh; - justify-content: flex-start; - align-items: center; -} - -.login { - height: 100%; - width: 25%; - min-width: 400px; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - - background-color: var(--background-primary); - - padding: 0 50px 0 50px; -} - -.header { - margin: 40px; - width: 100%; - text-align: center; - display: flex; - flex-direction: column; - justify-content: center; -} - -.header-subtext { - text-align: center; -} - -.header-subtext a, -.header-subtext p { - display: inline-block; - margin: 0 10px 0 10px; -} - -a { - color: var(--foreground-primary); -} - -form { - display: flex; - flex-direction: column; - justify-content: center; - width: 100%; - min-height: 50%; -} - -input, -form a { - background-color: var(--background-secondary); - padding: 10px; - margin: 5px 0 5px 0; - outline: none; - border: 1px solid grey; - color: var(--foreground-primary); - text-decoration: none; - cursor: pointer; -} - -form a { - text-align: center; -} - -label { - text-transform: uppercase; - font-size: 0.75rem; - font-weight: bold; -} - -#loginDiscord { - background-color: var(--background-login-discord); -} - -#failure { - width: 100%; - margin-top: 10px; - color: rgb(200, 20, 20); - display: none; -} - -.h-captcha { - display: flex; - justify-content: center; - margin-top: 10px; -} diff --git a/src-slowcord/login/public/js/handler.js b/src-slowcord/login/public/js/handler.js deleted file mode 100644 index 2d7b164b..00000000 --- a/src-slowcord/login/public/js/handler.js +++ /dev/null @@ -1,40 +0,0 @@ -const handleSubmit = async (path, body) => { - const failureMessage = document.getElementById("failure"); - - var response = await fetch(path, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - }); - - const json = await response.json(); - if (json.token) { - window.localStorage.setItem("token", `"${json.token}"`); - window.location.href = "/app"; - return; - } - - if (json.ticket) { - // my terrible solution to 2fa - const twoFactorForm = document.forms["2fa"]; - const loginForm = document.forms["login"]; - - twoFactorForm.style.display = "flex"; - loginForm.style.display = "none"; - - twoFactorForm.ticket.value = json.ticket; - return; - } - - // Very fun error message here lol - const error = json.errors - ? Object.values(json.errors)[0]._errors[0].message - : json.captcha_key - ? "Captcha required" - : json.message; - - failureMessage.innerHTML = error; - failureMessage.style.display = "block"; -}; diff --git a/src-slowcord/login/public/login.html b/src-slowcord/login/public/login.html deleted file mode 100644 index fa367b70..00000000 --- a/src-slowcord/login/public/login.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - Slowcord - - - - - - - - - - -
- -
- - - - diff --git a/src-slowcord/login/public/register.html b/src-slowcord/login/public/register.html deleted file mode 100644 index 30b560a4..00000000 --- a/src-slowcord/login/public/register.html +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - Slowcord - - - - - - - - - - -
- -
- - - - diff --git a/src-slowcord/login/src/index.ts b/src-slowcord/login/src/index.ts deleted file mode 100644 index ced35dcf..00000000 --- a/src-slowcord/login/src/index.ts +++ /dev/null @@ -1,171 +0,0 @@ -import "dotenv/config"; -import express, { Request, Response } from "express"; -import cookieParser from "cookie-parser"; -import { - initDatabase, - generateToken, - User, - Config, - handleFile, -} from "fosscord-server/src/util"; -import path from "path"; -import fetch from "node-fetch"; - -// apparently dirname doesn't exist in modules, nice -/* https://stackoverflow.com/a/62892482 */ -import { fileURLToPath } from "url"; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const app = express(); -app.use(cookieParser()); -const port = process.env.PORT; - -// ip -> unix epoch that requests will be accepted again -const rateLimits: { [ip: string]: number } = {}; -const allowRequestsEveryMs = 0.5 * 1000; // every half second - -const allowedRequestsPerSecond = 50; -let requestsThisSecond = 0; -setInterval(() => { - requestsThisSecond = 0; -}, 1000); - -const toDataURL = async (url: string) => { - const response = await fetch(url); - const blob = await response.blob(); - const buffer = Buffer.from(await blob.text()); - return `data:${blob.type};base64,${buffer.toString("base64")}`; -}; - -class Discord { - static getAccessToken = async (req: Request, res: Response) => { - const { code } = req.query; - - const body = new URLSearchParams( - Object.entries({ - client_id: process.env.DISCORD_CLIENT_ID as string, - client_secret: process.env.DISCORD_SECRET as string, - redirect_uri: process.env.DISCORD_REDIRECT as string, - code: code as string, - grant_type: "authorization_code", - }), - ).toString(); - - const resp = await fetch("https://discord.com/api/oauth2/token", { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - body: body, - }); - - const json = (await resp.json()) as any; - if (json.error) return null; - - return { - access_token: json.access_token, - token_type: json.token_type, - expires_in: json.expires_in, - refresh_token: json.refresh_token, - scope: json.scope, - }; - }; - - static getUserDetails = async (token: string) => { - const resp = await fetch("https://discord.com/api/users/@me", { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - - const json = (await resp.json()) as any; - if (!json.username || !json.email) return null; // eh, deal with bad code later - - return { - id: json.id, - email: json.email, - username: json.username, - avatar_url: json.avatar - ? `https://cdn.discordapp.com/avatars/${json.id}/${json.avatar}?size=2048` - : null, - }; - }; -} - -const handlers: { [key: string]: any } = { - discord: Discord, -}; - -app.get("/oauth/:type", async (req, res) => { - requestsThisSecond++; - if (requestsThisSecond > allowedRequestsPerSecond) - return res.sendStatus(429); - - const ip = - (req.headers["x-forwarded-for"] as string) || - (req.socket.remoteAddress as string); - console.log(`${ip}`); - if (!rateLimits[ip]) { - rateLimits[ip] = Date.now() + allowRequestsEveryMs; - } else if (rateLimits[ip] > Date.now()) { - rateLimits[ip] += allowRequestsEveryMs; - console.log( - `${new Date()} : user ${ip} was timed out for ${ - (rateLimits[ip] - Date.now()) / 1000 - }s`, - ); - return res.sendStatus(429); - } else { - delete rateLimits[ip]; - } - - const { type } = req.params; - const handler = handlers[type]; - if (!type || !handler) return res.sendStatus(400); - - const data = await handler.getAccessToken(req, res); - if (!data) return res.sendStatus(500); - - const details = await handler.getUserDetails(data.access_token); - if (!details) return res.sendStatus(500); - - let user = await User.findOne({ where: { email: details.email } }); - if (!user) { - user = await User.register({ - email: details.email, - username: details.username, - req, - }); - - if (details.avatar_url) { - try { - const avatar = await handleFile( - `/avatars/${user.id}`, - (await toDataURL(details.avatar_url)) as string, - ); - user.avatar = avatar; - await user.save(); - } catch (e) { - console.error(e); - } - } - } - - const token = await generateToken(user.id); - - res.cookie("token", token); - - res.sendFile(path.join(__dirname, "../public/login.html")); -}); - -app.use(express.static("public", { extensions: ["html"] })); - -(async () => { - await initDatabase(); - await Config.init(); - - app.listen(port, () => { - console.log(`Listening on port ${port}`); - }); -})(); diff --git a/src-slowcord/login/tsconfig.json b/src-slowcord/login/tsconfig.json deleted file mode 100644 index 4d96714c..00000000 --- a/src-slowcord/login/tsconfig.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "exclude": ["node_modules"], - "include": ["src/**/*.ts"], - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Projects */ - // "incremental": true, /* Enable incremental compilation */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Language and Environment */ - "target": "ES6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - "lib": [ - "ES2021" - ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, - // "jsx": "preserve", /* Specify what JSX code is generated. */ - "experimentalDecorators": true /* Enable experimental support for TC39 stage 2 draft decorators. */, - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ - // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - /* Modules */ - "module": "ES2020" /* Specify what module code is generated. */, - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ - "types": [ - "node" - ] /* Specify type package names to be included without being referenced in a source file. */, - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ - // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true /* Create source map files for emitted JavaScript files. */, - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./build" /* Specify an output folder for all emitted files. */, - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - /* Type Checking */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ - // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ - "strictPropertyInitialization": false /* Check for class properties that are declared but not set in the constructor. */, - // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ - // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/src-slowcord/nginx/fosscord b/src-slowcord/nginx/fosscord deleted file mode 100644 index f1d53de1..00000000 --- a/src-slowcord/nginx/fosscord +++ /dev/null @@ -1,54 +0,0 @@ -server { - server_name slowcord.understars.dev; - - client_max_body_size 150M; - - add_header Last-Modified $date_gmt; - proxy_set_header Host $host; - proxy_pass_request_headers on; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-Proto https; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Host $remote_addr; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - - location / { - proxy_pass http://127.0.0.1:3001; - } - - location /api { - proxy_pass http://127.0.0.1:3001; - add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; - - # TODO: This is a bad solution. Why does proxy_pass not forward all upstream errors to client? - # Or is it just that the server is not actually responding? - proxy_no_cache 1; - proxy_cache_bypass 1; - proxy_connect_timeout 1; - proxy_send_timeout 1; - proxy_read_timeout 1; - send_timeout 1; - } - - # TODO: Make the login service not suck - location ~ ^/(login|register|oauth/discord|css/index.css|js/handler.js) { - proxy_pass http://127.0.0.1:3010; - } - - listen 443 ssl; - ssl_certificate /etc/letsencrypt/live/slowcord.understars.dev/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/slowcord.understars.dev/privkey.pem; - include /etc/letsencrypt/options-ssl-nginx.conf; - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; -} - -server { - if ($host = slowcord.understars.dev) { - return 301 https://$host$request_uri; - } - - listen 80; - server_name slowcord.understars.dev; - return 404; -} \ No newline at end of file diff --git a/src-slowcord/nginx/voice b/src-slowcord/nginx/voice deleted file mode 100644 index b2b18c40..00000000 --- a/src-slowcord/nginx/voice +++ /dev/null @@ -1,35 +0,0 @@ -server { - server_name voice.slowcord.understars.dev; - - client_max_body_size 50M; - - add_header Last-Modified $date_gmt; - proxy_set_header Host $host; - proxy_pass_request_headers on; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-Proto https; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Host $remote_addr; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - - location / { - proxy_pass http://127.0.0.1:3004; - } - - listen 443 ssl; - ssl_certificate /etc/letsencrypt/live/voice.slowcord.understars.dev/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/voice.slowcord.understars.dev/privkey.pem; - include /etc/letsencrypt/options-ssl-nginx.conf; - ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; -} - -server { - if ($host = voice.slowcord.understars.dev) { - return 301 https://$host$request_uri; - } - - listen 80; - server_name voice.slowcord.understars.dev; - return 404; -} \ No newline at end of file diff --git a/src-slowcord/rules.md b/src-slowcord/rules.md deleted file mode 100644 index 1097060a..00000000 --- a/src-slowcord/rules.md +++ /dev/null @@ -1,22 +0,0 @@ -# Hi, welcome to Slowcord! - -Slowcord is a heavily modded Fosscord instance. You can browse it's source here: https://github.com/MaddyUnderStars/fosscord-server/tree/slowcord - -## Here are some general instance-wide rules: - -- **Harassment, homophobia, transphobia, etc, violence, and hate speech are forbidden.** -- Behaviour that harms the service - be it malicious/intentional or not - is strictly forbidden. This may include API abuse/spam, exploits, etc. -- - If you do discover an exploit/bug, it would be greatly appreciated if you could create an issue in the above repo, or DM @MaddyUnderStars#0000. -- Any content that would be considered illegal in Australia is also forbidden. Additionally, if it is illegal in your own country, it shouldn't be here. -- Bots/selfbots are allowed. If you would like an account to be given bot status, DM @MaddyUnderStars#0000. - -These rules are non-exhaustive, but should give a good idea of what will be enforced. - -Permanent Slowcord guild invite: https://slowcord.understars.dev/invite/slowcord - -### If a message or user breaks these rules, you can report it here: https://forms.gle/sd6RkdM7gRgJLV368 - -#### Lastly ( and not rules ): - -- If you use BetterDiscord or Powercord, and want an easier time accessing Slowcord and other Fosscord instances, check out https://github.com/maddyunderstars/fosscord-bd! -- Also, if you're on Android, you can download the mobile client at https://slowcord.understars.dev/assets/slowcord.apk diff --git a/src-slowcord/status/.env.template b/src-slowcord/status/.env.template deleted file mode 100644 index e15dc56a..00000000 --- a/src-slowcord/status/.env.template +++ /dev/null @@ -1,6 +0,0 @@ -DATABASE= -INSTANCE_API= -INSTANCE_CDN= -INSTANCE_TOKEN= -MEASURE_INTERVAL=1000 -RETENTION_DAYS=30 \ No newline at end of file diff --git a/src-slowcord/status/package-lock.json b/src-slowcord/status/package-lock.json deleted file mode 100644 index a4f449ab..00000000 Binary files a/src-slowcord/status/package-lock.json and /dev/null differ diff --git a/src-slowcord/status/package.json b/src-slowcord/status/package.json deleted file mode 100644 index ccec4f55..00000000 --- a/src-slowcord/status/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "slowcord-status", - "version": "1.0.0", - "description": "Slowcord status service", - "main": "build/index.js", - "scripts": { - "build": "tsc -b", - "start": "node build/index.js", - "start:gateway": "node build/gateway.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/maddyunderstars/fosscord-server.git" - }, - "bugs": { - "url": "https://github.com/maddyunderstars/fosscord-server/issues" - }, - "homepage": "https://github.com/maddyunderstars/fosscord-server#readme", - "author": "MaddyUnderStars", - "license": "AGPL-3.0-only", - "devDependencies": { - "@types/node": "^18.0.6" - }, - "dependencies": { - "dotenv": "^16.0.1", - "fosscord-gopnik": "^1.0.0", - "mysql2": "^2.3.3", - "node-fetch": "^3.2.9" - }, - "type": "module" -} diff --git a/src-slowcord/status/src/gateway.ts b/src-slowcord/status/src/gateway.ts deleted file mode 100644 index bc00c2d4..00000000 --- a/src-slowcord/status/src/gateway.ts +++ /dev/null @@ -1,99 +0,0 @@ -import "dotenv/config"; -import Fosscord from "fosscord-gopnik"; -import Discord from "discord.js"; -import mysql from "mysql2"; -import fetch from "node-fetch"; - -const dbConn = mysql.createConnection(process.env.DATABASE as string); -const executePromise = (sql: string, args: any[]) => - new Promise((resolve, reject) => - dbConn.execute(sql, args, (err, res) => { - if (err) reject(err); - else resolve(res); - }), - ); -const savePerf = async (time: number, name: string, error?: string | Error) => { - if (error && typeof error != "string") error = error.message; - try { - await executePromise( - "INSERT INTO performance (value, endpoint, timestamp, error) VALUES (?, ?, ?, ?)", - [time ?? 0, name, new Date(), error ?? null], - ); - // await executePromise("DELETE FROM performance WHERE DATE(timestamp) < now() - interval ? DAY", [process.env.RETENTION_DAYS]); - } catch (e) { - console.error(e); - } -}; - -var timestamp: number | undefined; - -const doMeasurements = async (channel: Discord.TextChannel) => { - timestamp = Date.now(); - await channel.send("hello this is a special message kthxbye"); - - setTimeout( - doMeasurements, - parseInt(process.env.MEASURE_INTERVAL as string), - channel, - ); -}; - -const instance = { - app: process.env.INSTANCE_WEB_APP as string, - api: process.env.INSTANCE_API as string, - cdn: process.env.INSTANCE_CDN as string, - token: process.env.INSTANCE_TOKEN as string, -}; - -const client = new Fosscord.Client({ - intents: [], - http: { - api: instance.api, - cdn: instance.cdn, - }, -}); - -client.on("ready", async () => { - console.log(`Ready on gateway as ${client.user!.tag}`); - - const channel = await client.channels.fetch("1019955729054267764"); - if (!channel) return; - - doMeasurements(channel as Discord.TextChannel); -}); - -client.on("messageCreate", async (msg: Discord.Message) => { - if (!timestamp) return; - if ( - msg.author.id != "992745947417141682" || - msg.channel.id != "1019955729054267764" || - msg.content != "hello this is a special message kthxbye" - ) - return; - await savePerf(Date.now() - timestamp, "messageCreate", undefined); - timestamp = undefined; - - await fetch( - `${instance.api}/channels/1019955729054267764/messages/${msg.id}`, - { - method: "DELETE", - headers: { - authorization: instance.token, - }, - }, - ); -}); - -client.on("error", (error: any) => { - console.log(`Gateway error`, error); -}); - -client.on("warn", (msg: any) => { - console.log(`Gateway warning:`, msg); -}); - -(async () => { - await new Promise((resolve) => dbConn.connect(resolve)); - console.log("Connected to db"); - await client.login(instance.token); -})(); diff --git a/src-slowcord/status/src/index.ts b/src-slowcord/status/src/index.ts deleted file mode 100644 index 979469b6..00000000 --- a/src-slowcord/status/src/index.ts +++ /dev/null @@ -1,159 +0,0 @@ -import "dotenv/config"; -import https from "https"; -import mysql from "mysql2"; -import fetch from "node-fetch"; - -const dbConn = mysql.createConnection(process.env.DATABASE as string); -const executePromise = (sql: string, args: any[]) => - new Promise((resolve, reject) => - dbConn.execute(sql, args, (err, res) => { - if (err) reject(err); - else resolve(res); - }), - ); - -const instance = { - app: process.env.INSTANCE_WEB_APP as string, - api: process.env.INSTANCE_API as string, - cdn: process.env.INSTANCE_CDN as string, - token: process.env.INSTANCE_TOKEN as string, -}; - -const savePerf = async (time: number, name: string, error?: string | Error) => { - if (error && typeof error != "string") error = error.message; - try { - await executePromise( - "INSERT INTO performance (value, endpoint, timestamp, error) VALUES (?, ?, ?, ?)", - [time ?? 0, name, new Date(), error ?? null], - ); - // await executePromise("DELETE FROM performance WHERE DATE(timestamp) < now() - interval ? DAY", [process.env.RETENTION_DAYS]); - } catch (e) { - console.error(e); - } -}; - -const saveSystemUsage = async ( - load: number, - procUptime: number, - sysUptime: number, - ram: number, - sessions: number, -) => { - try { - await executePromise( - "INSERT INTO monitor (time, cpu, procUp, sysUp, ram, sessions) VALUES (?, ?, ?, ?, ?, ?)", - [new Date(), load, procUptime, sysUptime, ram, sessions], - ); - } catch (e) { - console.error(e); - } -}; - -const makeTimedRequest = (path: string, body?: object): Promise => - new Promise((resolve, reject) => { - const opts = { - hostname: new URL(path).hostname, - port: 443, - path: new URL(path).pathname, - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: instance.token, - }, - timeout: 1000, - }; - - let start: number, end: number; - const req = https.request(opts, (res) => { - if (res.statusCode! < 200 || res.statusCode! > 300) { - return reject(`${res.statusCode} ${res.statusMessage}`); - } - - res.on("data", (data) => {}); - - res.on("end", () => { - end = Date.now(); - resolve(end - start); - }); - }); - - req.on("finish", () => { - if (body) req.write(JSON.stringify(body)); - start = Date.now(); - }); - - req.on("error", (error) => { - reject(error); - }); - - req.end(); - }); - -const measureApi = async (name: string, path: string, body?: object) => { - let error, - time = -1; - try { - time = await makeTimedRequest(path, body); - } catch (e) { - error = e as Error | string; - } - - console.log( - `${name} took ${time}ms ${error ? "with error" : ""}`, - error ?? "", - ); - - await savePerf(time, name, error); -}; - -interface monitorzSchema { - load: number[]; - procUptime: number; - sysUptime: number; - memPercent: number; - sessions: number; -} - -const app = async () => { - await new Promise((resolve) => dbConn.connect(resolve)); - console.log("Connected to db"); - // await client.login(instance.token); - - console.log( - `Monitoring performance for instance at ${ - new URL(instance.api).hostname - }`, - ); - - const doMeasurements = async () => { - await measureApi("ping", `${instance.api}/ping`); - await measureApi("users/@me", `${instance.api}/users/@me`); - await measureApi("login", `${instance.app}/login`); - // await gatewayMeasure("websocketPing"); - - try { - const res = await fetch(`${instance.api}/-/monitorz`, { - headers: { - Authorization: process.env.INSTANCE_TOKEN as string, - }, - }); - const json = (await res.json()) as monitorzSchema; - await saveSystemUsage( - json.load[1], - json.procUptime, - json.sysUptime, - json.memPercent, - json.sessions, - ); - } catch (e) {} - - setTimeout( - doMeasurements, - parseInt(process.env.MEASURE_INTERVAL as string), - ); - }; - - doMeasurements(); -}; - -app(); diff --git a/src-slowcord/status/tsconfig.json b/src-slowcord/status/tsconfig.json deleted file mode 100644 index 4d96714c..00000000 --- a/src-slowcord/status/tsconfig.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "exclude": ["node_modules"], - "include": ["src/**/*.ts"], - "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - /* Projects */ - // "incremental": true, /* Enable incremental compilation */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Language and Environment */ - "target": "ES6" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - "lib": [ - "ES2021" - ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, - // "jsx": "preserve", /* Specify what JSX code is generated. */ - "experimentalDecorators": true /* Enable experimental support for TC39 stage 2 draft decorators. */, - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ - // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - /* Modules */ - "module": "ES2020" /* Specify what module code is generated. */, - // "rootDir": "./", /* Specify the root folder within your source files. */ - "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ - "types": [ - "node" - ] /* Specify type package names to be included without being referenced in a source file. */, - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ - // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true /* Create source map files for emitted JavaScript files. */, - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./build" /* Specify an output folder for all emitted files. */, - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - /* Type Checking */ - "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ - // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ - "strictPropertyInitialization": false /* Check for class properties that are declared but not set in the constructor. */, - // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ - // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts index ecde7fb4..ec2c42e5 100644 --- a/src/api/middlewares/Authentication.ts +++ b/src/api/middlewares/Authentication.ts @@ -25,6 +25,8 @@ export const NO_AUTHORIZATION_ROUTES = [ "/track", // Public policy pages "/policies/instance", + // Oauth callback + "/oauth2/callback", // Asset delivery /\/guilds\/\d+\/widget\.(json|png)/, ]; diff --git a/src/api/routes/oauth2/callback.ts b/src/api/routes/oauth2/callback.ts new file mode 100644 index 00000000..3c7fb777 --- /dev/null +++ b/src/api/routes/oauth2/callback.ts @@ -0,0 +1,38 @@ +import { Router, Request, Response } from "express"; +import { route, OauthCallbackHandlers } from "@fosscord/api"; +import { FieldErrors, generateToken, User } from "@fosscord/util"; +const router = Router(); + +router.get("/:type", route({}), async (req: Request, res: Response) => { + const { type } = req.params; + const handler = OauthCallbackHandlers[type]; + if (!handler) throw FieldErrors({ + type: { + code: "BASE_TYPE_CHOICES", + message: `Value must be one of (${Object.keys(OauthCallbackHandlers).join(", ")}).`, + } + }); + + const { code } = req.query; + if (!code || typeof code !== "string") throw FieldErrors({ code: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED"), } }); + const access = await handler.getAccessToken(code); + + const oauthUser = await handler.getUserDetals(access.access_token); + + let user = await User.findOne({ where: { email: oauthUser.email } }); + if (!user) { + user = await User.register({ + email: oauthUser.email, + username: oauthUser.username, + req + }); + + // TODO: upload pfp, banner? + } + + const token = await generateToken(user.id); + + return { token }; +}); + +export default router; \ No newline at end of file diff --git a/src/api/routes/policies/instance/stats.ts b/src/api/routes/policies/instance/stats.ts new file mode 100644 index 00000000..fb8c386a --- /dev/null +++ b/src/api/routes/policies/instance/stats.ts @@ -0,0 +1,21 @@ +import { Router, Request, Response } from "express"; +import { route } from "@fosscord/api"; +import { Attachment, Config, Guild, Message, RateLimit, Session, User } from "@fosscord/util"; +const router = Router(); + +router.get("/", route({}), async (req: Request, res: Response) => { + res.json({ + all_time: { + users: await User.count(), + guilds: await Guild.count(), + messages: await Message.count(), + attachments: await Attachment.count(), + }, + now: { + sessions: await Session.count(), + rate_limits: await RateLimit.count(), + } + }); +}); + +export default router; diff --git a/src/api/util/handlers/Oauth.ts b/src/api/util/handlers/Oauth.ts new file mode 100644 index 00000000..cc662161 --- /dev/null +++ b/src/api/util/handlers/Oauth.ts @@ -0,0 +1,83 @@ +// TODO: Puyo's connections PR would replace this file + +import { Config } from "@fosscord/util"; +import fetch from "node-fetch"; + +export interface OauthAccessToken { + access_token: string; + token_type: string; + expires_in: string; + refresh_token: string; + scope: string; +}; + +export interface OauthUserDetails { + id: string; + email: string; + username: string; + avatar_url: string | null; +} + +interface Connection { + getAccessToken: (code: string) => Promise; + getUserDetals: (token: string) => Promise; +} + +const DiscordConnection: Connection = { + getAccessToken: async (code) => { + const { external } = Config.get(); + const { discord } = external; + + if (!discord.id || !discord.secret || !discord.redirect) + throw new Error("Discord Oauth has not been configured.") + + const body = new URLSearchParams( + Object.entries({ + client_id: discord.id as string, + client_secret: discord.secret as string, + redirect_uri: discord.redirect as string, + code: code as string, + grant_type: "authorization_code", + }) + ).toString(); + + const resp = await fetch("https://discord.com/api/oauth2/token", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: body, + }); + if (resp.status !== 200) throw new Error(`Failed to get access token.`,); + + const json = await resp.json(); + + return json; + }, + + getUserDetals: async (token) => { + const resp = await fetch("https://discord.com/api/users/@me", { + headers: { + Authorization: `Bearer ${token}` + }, + }); + + const json = await resp.json(); + if (!json.username || !json.email) throw new Error("Failed to get user details via oauth"); + + return { + id: json.id, + email: json.email, + username: json.username, + avatar_url: json.avatar + ? `https://cdn.discordapp.com/avatars/${json.id}/${json.avatar}?size=2048` + : null, + }; + } +}; + +const OauthCallbackHandlers: { [key: string]: Connection; } = { + discord: DiscordConnection +}; + +export { OauthCallbackHandlers }; \ No newline at end of file diff --git a/src/api/util/index.ts b/src/api/util/index.ts index ffad0607..49542ceb 100644 --- a/src/api/util/index.ts +++ b/src/api/util/index.ts @@ -7,4 +7,5 @@ export * from "./handlers/route"; export * from "./utility/String"; export * from "./handlers/Voice"; export * from "./utility/captcha"; -export * from "./utility/EmbedHandlers"; \ No newline at end of file +export * from "./utility/EmbedHandlers"; +export * from "./handlers/Oauth"; \ No newline at end of file diff --git a/src/util/entities/Config.ts b/src/util/entities/Config.ts index 5035f552..9b25795d 100644 --- a/src/util/entities/Config.ts +++ b/src/util/entities/Config.ts @@ -211,7 +211,12 @@ export interface ConfigValue { }; external: { twitter: string | null; - } + discord: { + id: string | null; + secret: string | null; + redirect: string | null; + }; + }; } export const DefaultConfigOptions: ConfigValue = { @@ -423,5 +428,10 @@ export const DefaultConfigOptions: ConfigValue = { }, external: { twitter: null, + discord: { + id: null, + secret: null, + redirect: null, + } } }; diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts index 8048250c..5e474d9e 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts @@ -1,19 +1,26 @@ -import { PublicUser, User, UserSettings } from "../entities/User"; -import { Channel } from "../entities/Channel"; -import { Guild } from "../entities/Guild"; -import { Member, PublicMember, UserGuildSettings } from "../entities/Member"; -import { Emoji } from "../entities/Emoji"; -import { Role } from "../entities/Role"; -import { Invite } from "../entities/Invite"; -import { Message, PartialEmoji } from "../entities/Message"; -import { VoiceState } from "../entities/VoiceState"; -import { ApplicationCommand } from "../entities/Application"; -import { Interaction } from "./Interaction"; -import { ConnectedAccount } from "../entities/ConnectedAccount"; -import { Relationship, RelationshipType } from "../entities/Relationship"; -import { Presence } from "./Presence"; -import { Sticker } from ".."; -import { Activity, Status } from "."; +import { + RelationshipType, + ConnectedAccount, + Interaction, + ApplicationCommand, + VoiceState, + Message, + PartialEmoji, + Invite, + Role, + Emoji, + PublicMember, + UserGuildSettings, + Guild, + Channel, + PublicUser, + User, + Sticker, + Activity, + Status, + Presence, + UserSettings, +} from "@fosscord/util"; export interface Event { guild_id?: string; @@ -73,9 +80,9 @@ export interface ReadyEventData { number, null, number, - [[number, { e: number; s: number }[]]], + [[number, { e: number; s: number; }[]]], [number, [[number, [number, number]]]], - { b: number; k: bigint[] }[], + { b: number; k: bigint[]; }[], ][]; guild_join_requests?: any[]; // ? what is this? this is new shard?: [number, number]; @@ -473,7 +480,7 @@ export interface SessionsReplace extends Event { export interface GuildMemberListUpdate extends Event { event: "GUILD_MEMBER_LIST_UPDATE"; data: { - groups: { id: string; count: number }[]; + groups: { id: string; count: number; }[]; guild_id: string; id: string; member_count: number; @@ -481,8 +488,8 @@ export interface GuildMemberListUpdate extends Event { ops: { index: number; item: { - member?: PublicMember & { presence: Presence }; - group?: { id: string; count: number }[]; + member?: PublicMember & { presence: Presence; }; + group?: { id: string; count: number; }[]; }; }[]; };