diff options
author | Madeline <46743919+MaddyUnderStars@users.noreply.github.com> | 2022-08-25 12:55:42 +1000 |
---|---|---|
committer | Madeline <46743919+MaddyUnderStars@users.noreply.github.com> | 2022-08-25 13:00:31 +1000 |
commit | d946547a9233d866e793e1c68f11d98cb2f7d390 (patch) | |
tree | 2ff2677432e2e874f0784b115202f3846e68deb0 /src/api | |
parent | Merge remote-tracking branch 'upstream/staging' into fix/categoryNames (diff) | |
parent | Merge pull request #799 from MaddyUnderStars/feat/captchaVerify (diff) | |
download | server-d946547a9233d866e793e1c68f11d98cb2f7d390.tar.xz |
Merge remote-tracking branch 'upstream/staging' into fix/categoryNames
Also allow voice to skip checks
Diffstat (limited to '')
134 files changed, 967 insertions, 821 deletions
diff --git a/src/api/Server.ts b/src/api/Server.ts index 136f9814..e92335a5 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -1,16 +1,16 @@ +import { Config, getOrInitialiseDatabase, initEvent, registerRoutes } from "@fosscord/util"; +import { NextFunction, Request, Response, Router } from "express"; import { Server, ServerOptions } from "lambert-server"; +import morgan from "morgan"; +import path from "path"; +import { red } from "picocolors"; import { Authentication, CORS } from "./middlewares/"; -import { Config, getOrInitialiseDatabase, initEvent, registerRoutes } from "@fosscord/util"; -import { ErrorHandler } from "./middlewares/ErrorHandler"; import { BodyParser } from "./middlewares/BodyParser"; -import { Router, Request, Response, NextFunction } from "express"; -import path from "path"; +import { ErrorHandler } from "./middlewares/ErrorHandler"; import { initRateLimits } from "./middlewares/RateLimit"; import TestClient from "./middlewares/TestClient"; import { initTranslation } from "./middlewares/Translation"; -import morgan from "morgan"; import { initInstance } from "./util/handlers/Instance"; -import { red } from "picocolors" export interface FosscordServerOptions extends ServerOptions {} @@ -85,8 +85,13 @@ export class FosscordServer extends Server { this.app.use(ErrorHandler); TestClient(this.app); - if (logRequests) console.log(red(`Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!`)); - + if (logRequests) + console.log( + red( + `Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!` + ) + ); + return super.start(); } -} \ No newline at end of file +} diff --git a/src/api/index.ts b/src/api/index.ts index adc7649c..5f97a463 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,3 +1,3 @@ -export * from "./Server"; export * from "./middlewares/"; +export * from "./Server"; export * from "./util/"; diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts index 2d9ccf57..6d063953 100644 --- a/src/api/middlewares/Authentication.ts +++ b/src/api/middlewares/Authentication.ts @@ -1,6 +1,5 @@ +import { checkToken, Config, HTTPError, Rights } from "@fosscord/util"; import { NextFunction, Request, Response } from "express"; -import { HTTPError } from "@fosscord/util"; -import { checkToken, Config, Rights } from "@fosscord/util"; export const NO_AUTHORIZATION_ROUTES = [ // Authentication routes @@ -10,7 +9,7 @@ export const NO_AUTHORIZATION_ROUTES = [ "/auth/mfa/totp", // Routes with a seperate auth system "/webhooks/", - // Public information endpoints + // Public information endpoints "/ping", "/gateway", "/experiments", diff --git a/src/api/middlewares/BodyParser.ts b/src/api/middlewares/BodyParser.ts index 35db3c6f..36d89da7 100644 --- a/src/api/middlewares/BodyParser.ts +++ b/src/api/middlewares/BodyParser.ts @@ -1,6 +1,6 @@ +import { HTTPError } from "@fosscord/util"; import bodyParser, { OptionsJson } from "body-parser"; import { NextFunction, Request, Response } from "express"; -import { HTTPError } from "@fosscord/util"; export function BodyParser(opts?: OptionsJson) { const jsonParser = bodyParser.json(opts); diff --git a/src/api/middlewares/ErrorHandler.ts b/src/api/middlewares/ErrorHandler.ts index 8a046e06..813adc18 100644 --- a/src/api/middlewares/ErrorHandler.ts +++ b/src/api/middlewares/ErrorHandler.ts @@ -1,6 +1,5 @@ +import { ApiError, FieldError, HTTPError } from "@fosscord/util"; import { NextFunction, Request, Response } from "express"; -import { HTTPError } from "@fosscord/util"; -import { ApiError, FieldError } from "@fosscord/util"; const EntityNotFoundErrorRegex = /"(\w+)"/; export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) { diff --git a/src/api/middlewares/RateLimit.ts b/src/api/middlewares/RateLimit.ts index 47180b62..dc93dcef 100644 --- a/src/api/middlewares/RateLimit.ts +++ b/src/api/middlewares/RateLimit.ts @@ -1,6 +1,6 @@ -import { Config, getRights, listenEvent, Rights } from "@fosscord/util"; -import { NextFunction, Request, Response, Router } from "express"; import { getIpAdress } from "@fosscord/api"; +import { Config, getRights, listenEvent } from "@fosscord/util"; +import { NextFunction, Request, Response, Router } from "express"; import { API_PREFIX_TRAILING_SLASH } from "./Authentication"; // Docs: https://discord.com/developers/docs/topics/rate-limits @@ -48,7 +48,7 @@ export default function rateLimit(opts: { // exempt user? if so, immediately short circuit if (req.user_id) { const rights = await getRights(req.user_id); - if (rights.has("BYPASS_RATE_LIMITS")) return; + if (rights.has("BYPASS_RATE_LIMITS")) return next(); } const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, ""); @@ -121,6 +121,7 @@ export default function rateLimit(opts: { export async function initRateLimits(app: Router) { const { routes, global, ip, error, disabled } = Config.get().limits.rate; if (disabled) return; + console.log("Enabling rate limits..."); await listenEvent(EventRateLimit, (event) => { Cache.set(event.channel_id as string, event.data); event.acknowledge?.(); @@ -163,7 +164,7 @@ export async function initRateLimits(app: Router) { app.use("/auth/register", rateLimit({ onlyIp: true, success: true, ...routes.auth.register })); } -async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number; }) { +async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number }) { const id = opts.executor_id + opts.bucket_id; let limit = Cache.get(id); if (!limit) { diff --git a/src/api/middlewares/TestClient.ts b/src/api/middlewares/TestClient.ts index c8ea57f6..2784c8ab 100644 --- a/src/api/middlewares/TestClient.ts +++ b/src/api/middlewares/TestClient.ts @@ -1,17 +1,17 @@ -import express, { Request, Response, Application } from "express"; +import { Config } from "@fosscord/util"; +import express, { Application, Request, Response } from "express"; import fs from "fs"; +import fetch, { Headers, Response as FetchResponse } from "node-fetch"; import path from "path"; -import fetch, { Response as FetchResponse, Headers } from "node-fetch"; -import ProxyAgent from 'proxy-agent'; -import { Config } from "@fosscord/util"; -import { AssetCacheItem } from "../util/entities/AssetCacheItem" import { green } from "picocolors"; +import ProxyAgent from "proxy-agent"; +import { AssetCacheItem } from "../util/entities/AssetCacheItem"; -const AssetsPath = path.join(__dirname, "..", "..", "..", "assets") +const AssetsPath = path.join(__dirname, "..", "..", "..", "assets"); export default function TestClient(app: Application) { const agent = new ProxyAgent(); - + //build client page let html = fs.readFileSync(path.join(AssetsPath, "index.html"), { encoding: "utf8" }); html = applyEnv(html); @@ -22,31 +22,29 @@ export default function TestClient(app: Application) { //load asset cache let newAssetCache: Map<string, AssetCacheItem> = new Map<string, AssetCacheItem>(); let assetCacheDir = path.join(AssetsPath, "cache"); - if(process.env.ASSET_CACHE_DIR) - assetCacheDir = process.env.ASSET_CACHE_DIR + if (process.env.ASSET_CACHE_DIR) assetCacheDir = process.env.ASSET_CACHE_DIR; - console.log(`[TestClient] ${green(`Using asset cache path: ${assetCacheDir}`)}`) - if(!fs.existsSync(assetCacheDir)) { + console.log(`[TestClient] ${green(`Using asset cache path: ${assetCacheDir}`)}`); + if (!fs.existsSync(assetCacheDir)) { fs.mkdirSync(assetCacheDir); } - if(fs.existsSync(path.join(assetCacheDir, "index.json"))) { + if (fs.existsSync(path.join(assetCacheDir, "index.json"))) { let rawdata = fs.readFileSync(path.join(assetCacheDir, "index.json")); newAssetCache = new Map<string, AssetCacheItem>(Object.entries(JSON.parse(rawdata.toString()))); } - app.use("/assets", express.static(path.join(AssetsPath))); + app.use("/assets", express.static(path.join(AssetsPath))); app.get("/assets/:file", async (req: Request, res: Response) => { delete req.headers.host; let response: FetchResponse; let buffer: Buffer; let assetCacheItem: AssetCacheItem = new AssetCacheItem(req.params.file); - if(newAssetCache.has(req.params.file)){ + if (newAssetCache.has(req.params.file)) { assetCacheItem = newAssetCache.get(req.params.file)!; assetCacheItem.Headers.forEach((value: any, name: any) => { res.set(name, value); }); - } - else { + } else { console.log(`[TestClient] Downloading file not yet cached! Asset file: ${req.params.file}`); response = await fetch(`https://discord.com/assets/${req.params.file}`, { agent, @@ -55,7 +53,7 @@ export default function TestClient(app: Application) { ...req.headers } }); - + //set cache info assetCacheItem.Headers = Object.fromEntries(stripHeaders(response.headers)); assetCacheItem.FilePath = path.join(assetCacheDir, req.params.file); @@ -66,7 +64,7 @@ export default function TestClient(app: Application) { //download file fs.writeFileSync(assetCacheItem.FilePath, await response.buffer()); } - + assetCacheItem.Headers.forEach((value: string, name: string) => { res.set(name, value); }); @@ -77,8 +75,8 @@ export default function TestClient(app: Application) { res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24); res.set("content-type", "text/html"); - if(!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.") - + if (!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance."); + res.send(fs.readFileSync(path.join(__dirname, "..", "..", "..", "assets", "developers.html"), { encoding: "utf8" })); }); app.get("*", (req: Request, res: Response) => { @@ -86,23 +84,18 @@ export default function TestClient(app: Application) { res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24); res.set("content-type", "text/html"); - if(req.url.startsWith("/api") || req.url.startsWith("/__development")) return; + if (req.url.startsWith("/api") || req.url.startsWith("/__development")) return; - if(!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.") + if (!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance."); if (req.url.startsWith("/invite")) return res.send(html.replace("9b2b7f0632acd0c5e781", "9f24f709a3de09b67c49")); - + res.send(html); }); - - } function applyEnv(html: string): string { - const CDN_ENDPOINT = (Config.get().cdn.endpointClient || Config.get()?.cdn.endpointPublic || process.env.CDN || "").replace( - /(https?)?(:\/\/?)/g, - "" - ); - const GATEWAY_ENDPOINT = Config.get().gateway.endpointClient || Config.get()?.gateway.endpointPublic || process.env.GATEWAY || ""; + const CDN_ENDPOINT = (Config.get()?.cdn.endpointPublic || process.env.CDN || "").replace(/(https?)?(:\/\/?)/g, ""); + const GATEWAY_ENDPOINT = Config.get()?.gateway.endpointPublic || process.env.GATEWAY || ""; if (CDN_ENDPOINT) { html = html.replace(/CDN_HOST: .+/, `CDN_HOST: \`${CDN_ENDPOINT}\`,`); @@ -117,23 +110,29 @@ function applyPlugins(html: string): string { // plugins let files = fs.readdirSync(path.join(AssetsPath, "plugins")); let plugins = ""; - files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script src='/assets/plugins/${x}'></script>\n`; }); + files.forEach((x) => { + if (x.endsWith(".js")) plugins += `<script src='/assets/plugins/${x}'></script>\n`; + }); return html.replaceAll("<!-- plugin marker -->", plugins); } -function applyInlinePlugins(html: string): string{ +function applyInlinePlugins(html: string): string { // inline plugins let files = fs.readdirSync(path.join(AssetsPath, "inline-plugins")); let plugins = ""; - files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script src='/assets/inline-plugins/${x}'></script>\n\n`; }); + files.forEach((x) => { + if (x.endsWith(".js")) plugins += `<script src='/assets/inline-plugins/${x}'></script>\n\n`; + }); return html.replaceAll("<!-- inline plugin marker -->", plugins); } -function applyPreloadPlugins(html: string): string{ +function applyPreloadPlugins(html: string): string { //preload plugins let files = fs.readdirSync(path.join(AssetsPath, "preload-plugins")); let plugins = ""; - files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script>${fs.readFileSync(path.join(AssetsPath, "preload-plugins", x))}</script>\n`; }); + files.forEach((x) => { + if (x.endsWith(".js")) plugins += `<script>${fs.readFileSync(path.join(AssetsPath, "preload-plugins", x))}</script>\n`; + }); return html.replaceAll("<!-- preload plugin marker -->", plugins); } @@ -147,7 +146,7 @@ function stripHeaders(headers: Headers): Headers { "expect-ct", "access-control-allow-origin", "content-encoding" - ].forEach(headerName => { + ].forEach((headerName) => { headers.delete(headerName); }); return headers; diff --git a/src/api/middlewares/Translation.ts b/src/api/middlewares/Translation.ts index 64b03bf8..8e5e67e6 100644 --- a/src/api/middlewares/Translation.ts +++ b/src/api/middlewares/Translation.ts @@ -1,9 +1,9 @@ +import { Router } from "express"; import fs from "fs"; -import path from "path"; import i18next from "i18next"; import i18nextMiddleware from "i18next-http-middleware"; import i18nextBackend from "i18next-node-fs-backend"; -import { Router } from "express"; +import path from "path"; export async function initTranslation(router: Router) { const languages = fs.readdirSync(path.join(__dirname, "..", "..", "..", "assets", "locales")); diff --git a/src/api/routes/-/healthz.ts b/src/api/routes/-/healthz.ts index f7bcfebf..5dee9e86 100644 --- a/src/api/routes/-/healthz.ts +++ b/src/api/routes/-/healthz.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; import { getConnection } from "typeorm"; const router = Router(); diff --git a/src/api/routes/-/readyz.ts b/src/api/routes/-/readyz.ts index f7bcfebf..5dee9e86 100644 --- a/src/api/routes/-/readyz.ts +++ b/src/api/routes/-/readyz.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; import { getConnection } from "typeorm"; const router = Router(); diff --git a/src/api/routes/applications/#id/bot/index.ts b/src/api/routes/applications/#id/bot/index.ts index 5cae5215..e663059e 100644 --- a/src/api/routes/applications/#id/bot/index.ts +++ b/src/api/routes/applications/#id/bot/index.ts @@ -1,14 +1,14 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; -import { Application, Config, FieldErrors, generateToken, OrmUtils, Snowflake, trimSpecial, User, handleFile } from "@fosscord/util"; +import { Application, Config, FieldErrors, generateToken, handleFile, OrmUtils, trimSpecial, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; import { verifyToken } from "node-2fa"; const router: Router = Router(); router.post("/", route({}), async (req: Request, res: Response) => { - const app = await Application.findOne({where: {id: req.params.id}}); - if(!app) return res.status(404); + const app = await Application.findOne({ where: { id: req.params.id } }); + if (!app) return res.status(404); const username = trimSpecial(app.name); const discriminator = await User.generateDiscriminator(username); if (!discriminator) { @@ -16,8 +16,8 @@ router.post("/", route({}), async (req: Request, res: Response) => { throw FieldErrors({ username: { code: "USERNAME_TOO_MANY_USERS", - message: req?.t("auth:register.USERNAME_TOO_MANY_USERS"), - }, + message: req?.t("auth:register.USERNAME_TOO_MANY_USERS") + } }); } @@ -47,37 +47,37 @@ router.post("/", route({}), async (req: Request, res: Response) => { flags: "0", data: { hash: null, - valid_tokens_since: new Date(), + valid_tokens_since: new Date() }, settings: {}, extended_settings: {}, fingerprints: [], - notes: {}, + notes: {} }); await user.save(); app.bot = user; await app.save(); - res.send().status(204) + res.send().status(204); }); router.post("/reset", route({}), async (req: Request, res: Response) => { - let bot = await User.findOne({where: {id: req.params.id}}); - let owner = await User.findOne({where: {id: req.user_id}}); - if(!bot) return res.status(404); - if(owner?.totp_secret && (!req.body.code || verifyToken(owner.totp_secret, req.body.code))) { + let bot = await User.findOne({ where: { id: req.params.id } }); + let owner = await User.findOne({ where: { id: req.user_id } }); + if (!bot) return res.status(404); + if (owner?.totp_secret && (!req.body.code || verifyToken(owner.totp_secret, req.body.code))) { throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); } bot.data = { hash: undefined, valid_tokens_since: new Date() }; await bot.save(); let token = await generateToken(bot.id); - res.json({token}).status(200); + res.json({ token }).status(200); }); router.patch("/", route({}), async (req: Request, res: Response) => { if (req.body.avatar) req.body.avatar = await handleFile(`/avatars/${req.params.id}`, req.body.avatar as string); - let app = OrmUtils.mergeDeep(await User.findOne({where: {id: req.params.id}}), req.body); + let app = OrmUtils.mergeDeep(await User.findOne({ where: { id: req.params.id } }), req.body); await app.save(); res.json(app).status(200); }); -export default router; \ No newline at end of file +export default router; diff --git a/src/api/routes/applications/#id/entitlements.ts b/src/api/routes/applications/#id/entitlements.ts index cfcfe40f..26054eb0 100644 --- a/src/api/routes/applications/#id/entitlements.ts +++ b/src/api/routes/applications/#id/entitlements.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/applications/#id/index.ts b/src/api/routes/applications/#id/index.ts index 0aced582..398227fd 100644 --- a/src/api/routes/applications/#id/index.ts +++ b/src/api/routes/applications/#id/index.ts @@ -1,22 +1,22 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; -import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util"; +import { Application, OrmUtils } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { - let results = await Application.findOne({where: {id: req.params.id}, relations: ["owner", "bot"] }); + let results = await Application.findOne({ where: { id: req.params.id }, relations: ["owner", "bot"] }); res.json(results).status(200); }); router.patch("/", route({}), async (req: Request, res: Response) => { delete req.body.icon; - let app = OrmUtils.mergeDeep(await Application.findOne({where: {id: req.params.id}, relations: ["owner", "bot"]}), req.body); - if(app.bot) { - app.bot.bio = req.body.description + let app = OrmUtils.mergeDeep(await Application.findOne({ where: { id: req.params.id }, relations: ["owner", "bot"] }), req.body); + if (app.bot) { + app.bot.bio = req.body.description; app.bot?.save(); } - if(req.body.tags) app.tags = req.body.tags; + if (req.body.tags) app.tags = req.body.tags; await app.save(); res.json(app).status(200); }); @@ -26,5 +26,4 @@ router.post("/delete", route({}), async (req: Request, res: Response) => { res.send().status(200); }); - -export default router; \ No newline at end of file +export default router; diff --git a/src/api/routes/applications/#id/skus.ts b/src/api/routes/applications/#id/skus.ts index 5b667f36..df7ad4bb 100644 --- a/src/api/routes/applications/#id/skus.ts +++ b/src/api/routes/applications/#id/skus.ts @@ -1,6 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; -import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -8,4 +7,4 @@ router.get("/", route({}), async (req: Request, res: Response) => { res.json([]).status(200); }); -export default router; \ No newline at end of file +export default router; diff --git a/src/api/routes/applications/detectable.ts b/src/api/routes/applications/detectable.ts index 28ce42da..f012a595 100644 --- a/src/api/routes/applications/detectable.ts +++ b/src/api/routes/applications/detectable.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/applications/index.ts b/src/api/routes/applications/index.ts index 033dcc51..191833f2 100644 --- a/src/api/routes/applications/index.ts +++ b/src/api/routes/applications/index.ts @@ -1,6 +1,6 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; -import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util"; +import { Application, OrmUtils, trimSpecial, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -11,14 +11,14 @@ export interface ApplicationCreateSchema { router.get("/", route({}), async (req: Request, res: Response) => { //TODO - let results = await Application.find({where: {owner: {id: req.user_id}}, relations: ["owner", "bot"] }); + let results = await Application.find({ where: { owner: { id: req.user_id } }, relations: ["owner", "bot"] }); res.json(results).status(200); }); router.post("/", route({}), async (req: Request, res: Response) => { const body = req.body as ApplicationCreateSchema; - const user = await User.findOne({where: {id: req.user_id}}) - if(!user) res.status(420); + const user = await User.findOne({ where: { id: req.user_id } }); + if (!user) res.status(420); let app = OrmUtils.mergeDeep(new Application(), { name: trimSpecial(body.name), description: "", @@ -31,4 +31,4 @@ router.post("/", route({}), async (req: Request, res: Response) => { res.json(app).status(200); }); -export default router; \ No newline at end of file +export default router; diff --git a/src/api/routes/auth/location-metadata.ts b/src/api/routes/auth/location-metadata.ts index f4c2bd16..b8caf579 100644 --- a/src/api/routes/auth/location-metadata.ts +++ b/src/api/routes/auth/location-metadata.ts @@ -1,13 +1,12 @@ -import { Router, Request, Response } from "express"; -import { route } from "@fosscord/api"; -import { getIpAdress, IPAnalysis } from "@fosscord/api"; +import { getIpAdress, IPAnalysis, route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); -router.get("/",route({}), async (req: Request, res: Response) => { - //TODO - //Note: It's most likely related to legal. At the moment Discord hasn't finished this too - const country_code = (await IPAnalysis(getIpAdress(req))).country_code; - res.json({ consent_required: false, country_code: country_code, promotional_email_opt_in: { required: true, pre_checked: false}}); +router.get("/", route({}), async (req: Request, res: Response) => { + //TODO + //Note: It's most likely related to legal. At the moment Discord hasn't finished this too + const country_code = (await IPAnalysis(getIpAdress(req))).country_code; + res.json({ consent_required: false, country_code: country_code, promotional_email_opt_in: { required: true, pre_checked: false } }); }); export default router; diff --git a/src/api/routes/auth/login.ts b/src/api/routes/auth/login.ts index 9fc5924d..68b2656a 100644 --- a/src/api/routes/auth/login.ts +++ b/src/api/routes/auth/login.ts @@ -1,21 +1,29 @@ import { Request, Response, Router } from "express"; -import { route } from "@fosscord/api"; -import bcrypt from "bcrypt"; +import { route, getIpAdress, verifyCaptcha } from "@fosscord/api"; import { Config, User, generateToken, adjustEmail, FieldErrors, LoginSchema } from "@fosscord/util"; import crypto from "crypto"; +let bcrypt: any; +try { + bcrypt = require("bcrypt"); +} catch { + bcrypt = require("bcryptjs"); + console.log("Warning: using bcryptjs because bcrypt is not installed! Performance will be affected."); +} + const router: Router = Router(); export default router; router.post("/", route({ body: "LoginSchema" }), async (req: Request, res: Response) => { const { login, password, captcha_key, undelete } = req.body as LoginSchema; const email = adjustEmail(login); + const ip = getIpAdress(req); const config = Config.get(); if (config.login.requireCaptcha && config.security.captcha.enabled) { + const { sitekey, service } = config.security.captcha; if (!captcha_key) { - const { sitekey, service } = config.security.captcha; return res.status(400).json({ captcha_key: ["captcha-required"], captcha_sitekey: sitekey, @@ -23,7 +31,15 @@ router.post("/", route({ body: "LoginSchema" }), async (req: Request, res: Respo }); } - // TODO: check captcha + const ip = getIpAdress(req); + const verify = await verifyCaptcha(captcha_key, ip); + if (!verify.success) { + return res.status(400).json({ + captcha_key: verify["error-codes"], + captcha_sitekey: sitekey, + captcha_service: service + }) + } } const user = await User.findOneOrFail({ @@ -57,9 +73,9 @@ router.post("/", route({ body: "LoginSchema" }), async (req: Request, res: Respo return res.json({ ticket: ticket, mfa: true, - sms: false, // TODO - token: null, - }) + sms: false, // TODO + token: null + }); } const token = await generateToken(user.id); diff --git a/src/api/routes/auth/mfa/totp.ts b/src/api/routes/auth/mfa/totp.ts index 421dbafa..9938569e 100644 --- a/src/api/routes/auth/mfa/totp.ts +++ b/src/api/routes/auth/mfa/totp.ts @@ -1,8 +1,8 @@ -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; -import { BackupCode, FieldErrors, generateToken, TotpSchema, User } from "@fosscord/util"; -import { verifyToken } from "node-2fa"; +import { BackupCode, generateToken, TotpSchema, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; +import { verifyToken } from "node-2fa"; const router = Router(); router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Response) => { @@ -10,23 +10,17 @@ router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Respon const user = await User.findOneOrFail({ where: { - totp_last_ticket: ticket, + totp_last_ticket: ticket }, - select: [ - "id", - "totp_secret", - "settings", - ], + select: ["id", "totp_secret", "settings"] }); const backup = await BackupCode.findOne({ where: { code: code, expired: false, consumed: false, user: { id: user.id } } }); if (!backup) { const ret = verifyToken(user.totp_secret!, code); - if (!ret || ret.delta != 0) - throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); - } - else { + if (!ret || ret.delta != 0) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); + } else { backup.consumed = true; await backup.save(); } @@ -35,7 +29,7 @@ router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Respon return res.json({ token: await generateToken(user.id), - user_settings: user.settings, + user_settings: user.settings }); }); diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts index 09366a12..d3b5a59c 100644 --- a/src/api/routes/auth/register.ts +++ b/src/api/routes/auth/register.ts @@ -1,7 +1,14 @@ import { Request, Response, Router } from "express"; -import { Config, generateToken, Invite, FieldErrors, User, adjustEmail, trimSpecial, RegisterSchema } from "@fosscord/util"; -import { route, getIpAdress, IPAnalysis, isProxy } from "@fosscord/api"; -import bcrypt from "bcrypt"; +import { Config, generateToken, Invite, FieldErrors, User, adjustEmail, RegisterSchema } from "@fosscord/util"; +import { route, getIpAdress, IPAnalysis, isProxy, verifyCaptcha } from "@fosscord/api"; + +let bcrypt: any; +try { + bcrypt = require("bcrypt"); +} catch { + bcrypt = require("bcryptjs"); + console.log("Warning: using bcryptjs because bcrypt is not installed! Performance will be affected."); +} import { HTTPError } from "@fosscord/util"; const router: Router = Router(); @@ -38,8 +45,8 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re } if (register.requireCaptcha && security.captcha.enabled) { + const { sitekey, service } = security.captcha; if (!body.captcha_key) { - const { sitekey, service } = security.captcha; return res?.status(400).json({ captcha_key: ["captcha-required"], captcha_sitekey: sitekey, @@ -47,7 +54,14 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re }); } - // TODO: check captcha + const verify = await verifyCaptcha(body.captcha_key, ip); + if (!verify.success) { + return res.status(400).json({ + captcha_key: verify["error-codes"], + captcha_sitekey: sitekey, + captcha_service: service + }) + } } if (!register.allowMultipleAccounts) { diff --git a/src/api/routes/channels/#channel_id/followers.ts b/src/api/routes/channels/#channel_id/followers.ts index 641af4f8..c06db61b 100644 --- a/src/api/routes/channels/#channel_id/followers.ts +++ b/src/api/routes/channels/#channel_id/followers.ts @@ -1,4 +1,4 @@ -import { Router, Response, Request } from "express"; +import { Router } from "express"; const router: Router = Router(); // TODO: diff --git a/src/api/routes/channels/#channel_id/index.ts b/src/api/routes/channels/#channel_id/index.ts index bb8b868b..a65cf451 100644 --- a/src/api/routes/channels/#channel_id/index.ts +++ b/src/api/routes/channels/#channel_id/index.ts @@ -1,17 +1,16 @@ +import { route } from "@fosscord/api"; import { Channel, ChannelDeleteEvent, - ChannelPermissionOverwriteType, + ChannelModifySchema, ChannelType, ChannelUpdateEvent, emitEvent, - Recipient, handleFile, - ChannelModifySchema + OrmUtils, + Recipient } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { route } from "@fosscord/api"; -import { OrmUtils } from "@fosscord/util"; const router: Router = Router(); // TODO: delete channel diff --git a/src/api/routes/channels/#channel_id/invites.ts b/src/api/routes/channels/#channel_id/invites.ts index b5c65c0d..3a1d2666 100644 --- a/src/api/routes/channels/#channel_id/invites.ts +++ b/src/api/routes/channels/#channel_id/invites.ts @@ -1,45 +1,45 @@ -import { Router, Request, Response } from "express"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; -import { random } from "@fosscord/api"; -import { Channel, Invite, InviteCreateEvent, emitEvent, User, Guild, PublicInviteRelation } from "@fosscord/util"; +import { Channel, emitEvent, Guild, HTTPError, Invite, InviteCreateEvent, OrmUtils, PublicInviteRelation, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import { isTextChannel } from "./messages"; -import { OrmUtils } from "@fosscord/util"; const router: Router = Router(); -router.post("/", route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT_INVITE", right: "CREATE_INVITES" }), - async (req: Request, res: Response) => { - const { user_id } = req; - const { channel_id } = req.params; - const channel = await Channel.findOneOrFail({ where: { id: channel_id }, select: ["id", "name", "type", "guild_id"] }); - isTextChannel(channel.type); - - if (!channel.guild_id) { - throw new HTTPError("This channel doesn't exist", 404); +router.post( + "/", + route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT_INVITE", right: "CREATE_INVITES" }), + async (req: Request, res: Response) => { + const { user_id } = req; + const { channel_id } = req.params; + const channel = await Channel.findOneOrFail({ where: { id: channel_id }, select: ["id", "name", "type", "guild_id"] }); + isTextChannel(channel.type); + + if (!channel.guild_id) { + throw new HTTPError("This channel doesn't exist", 404); + } + const { guild_id } = channel; + + const expires_at = new Date(req.body.max_age * 1000 + Date.now()); + + const invite = await OrmUtils.mergeDeep(new Invite(), { + temporary: req.body.temporary || true, + max_uses: req.body.max_uses, + max_age: req.body.max_age, + expires_at, + guild_id, + channel_id, + inviter_id: user_id + }).save(); + //TODO: check this, removed toJSON call + const data = JSON.parse(JSON.stringify(invite)); + data.inviter = await User.getPublicUser(req.user_id); + data.guild = await Guild.findOne({ where: { id: guild_id } }); + data.channel = channel; + + await emitEvent({ event: "INVITE_CREATE", data, guild_id } as InviteCreateEvent); + res.status(201).send(data); } - const { guild_id } = channel; - - const expires_at = new Date(req.body.max_age * 1000 + Date.now()); - - const invite = await OrmUtils.mergeDeep(new Invite(),{ - temporary: req.body.temporary || true, - max_uses: req.body.max_uses, - max_age: req.body.max_age, - expires_at, - guild_id, - channel_id, - inviter_id: user_id - }).save(); - //TODO: check this, removed toJSON call - const data = JSON.parse(JSON.stringify(invite)); - data.inviter = await User.getPublicUser(req.user_id); - data.guild = await Guild.findOne({ where: { id: guild_id } }); - data.channel = channel; - - await emitEvent({ event: "INVITE_CREATE", data, guild_id } as InviteCreateEvent); - res.status(201).send(data); -}); +); router.get("/", route({ permission: "MANAGE_CHANNELS" }), async (req: Request, res: Response) => { const { channel_id } = req.params; diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/ack.ts b/src/api/routes/channels/#channel_id/messages/#message_id/ack.ts index 041f4d5e..5ebeed49 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/ack.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/ack.ts @@ -1,7 +1,6 @@ -import { emitEvent, getPermission, MessageAckEvent, ReadState, Snowflake } from "@fosscord/util"; -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; -import { OrmUtils } from "@fosscord/util"; +import { emitEvent, getPermission, MessageAckEvent, OrmUtils, ReadState } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts b/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts index b2cb6763..fbbc65f0 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts b/src/api/routes/channels/#channel_id/messages/#message_id/index.ts index d7e27062..b082e083 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/index.ts @@ -1,25 +1,22 @@ +import { handleMessage, postHandleMessage, route } from "@fosscord/api"; import { Attachment, Channel, - Embed, - DiscordApiErrors, emitEvent, FosscordApiErrors, getPermission, getRights, - Message, + HTTPError, + Message, MessageCreateEvent, + MessageCreateSchema, MessageDeleteEvent, MessageUpdateEvent, Snowflake, - uploadFile, - MessageCreateSchema + uploadFile } from "@fosscord/util"; -import { Router, Response, Request } from "express"; +import { Request, Response, Router } from "express"; import multer from "multer"; -import { route } from "@fosscord/api"; -import { handleMessage, postHandleMessage } from "@fosscord/api"; -import { HTTPError } from "@fosscord/util"; const router = Router(); // TODO: message content/embed string length limit @@ -33,50 +30,53 @@ const messageUpload = multer({ storage: multer.memoryStorage() }); // max upload 50 mb -router.patch("/", route({ body: "MessageCreateSchema", permission: "SEND_MESSAGES", right: "SEND_MESSAGES" }), async (req: Request, res: Response) => { - const { message_id, channel_id } = req.params; - let body = req.body as MessageCreateSchema; +router.patch( + "/", + route({ body: "MessageCreateSchema", permission: "SEND_MESSAGES", right: "SEND_MESSAGES" }), + async (req: Request, res: Response) => { + const { message_id, channel_id } = req.params; + let body = req.body as MessageCreateSchema; - const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] }); + const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] }); - const permissions = await getPermission(req.user_id, undefined, channel_id); - - const rights = await getRights(req.user_id); + const permissions = await getPermission(req.user_id, undefined, channel_id); - if ((req.user_id !== message.author_id)) { - if (!rights.has("MANAGE_MESSAGES")) { - permissions.hasThrow("MANAGE_MESSAGES"); - body = { flags: body.flags }; -// guild admins can only suppress embeds of other messages, no such restriction imposed to instance-wide admins - } - } else rights.hasThrow("SELF_EDIT_MESSAGES"); - - const new_message = await handleMessage({ - ...message, - // TODO: should message_reference be overridable? - // @ts-ignore - message_reference: message.message_reference, - ...body, - author_id: message.author_id, - channel_id, - id: message_id, - edited_timestamp: new Date() - }); - - await Promise.all([ - new_message!.save(), - await emitEvent({ - event: "MESSAGE_UPDATE", + const rights = await getRights(req.user_id); + + if (req.user_id !== message.author_id) { + if (!rights.has("MANAGE_MESSAGES")) { + permissions.hasThrow("MANAGE_MESSAGES"); + body = { flags: body.flags }; + // guild admins can only suppress embeds of other messages, no such restriction imposed to instance-wide admins + } + } else rights.hasThrow("SELF_EDIT_MESSAGES"); + + const new_message = await handleMessage({ + ...message, + // TODO: should message_reference be overridable? + // @ts-ignore + message_reference: message.message_reference, + ...body, + author_id: message.author_id, channel_id, - data: { ...new_message, nonce: undefined } - } as MessageUpdateEvent) - ]); + id: message_id, + edited_timestamp: new Date() + }); - postHandleMessage(message); + await Promise.all([ + new_message!.save(), + await emitEvent({ + event: "MESSAGE_UPDATE", + channel_id, + data: { ...new_message, nonce: undefined } + } as MessageUpdateEvent) + ]); - return res.json(message); -}); + postHandleMessage(message); + return res.json(message); + } +); // Backfill message with specific timestamp router.put( @@ -94,7 +94,7 @@ router.put( const { channel_id, message_id } = req.params; let body = req.body as MessageCreateSchema; const attachments: Attachment[] = []; - + const rights = await getRights(req.user_id); rights.hasThrow("SEND_MESSAGES"); @@ -103,13 +103,13 @@ router.put( throw new HTTPError("Message IDs must be positive integers", 400); } - const snowflake = Snowflake.deconstruct(message_id) + const snowflake = Snowflake.deconstruct(message_id); if (Date.now() < snowflake.timestamp) { // message is in the future throw FosscordApiErrors.CANNOT_BACKFILL_TO_THE_FUTURE; } - const exists = await Message.findOne({ where: { id: message_id, channel_id: channel_id }}); + const exists = await Message.findOne({ where: { id: message_id, channel_id: channel_id } }); if (exists) { throw FosscordApiErrors.CANNOT_REPLACE_BY_BACKFILL; } @@ -136,19 +136,19 @@ router.put( channel_id, attachments, edited_timestamp: undefined, - timestamp: new Date(snowflake.timestamp), + timestamp: new Date(snowflake.timestamp) }); //Fix for the client bug - delete message.member - + delete message.member; + await Promise.all([ message.save(), emitEvent({ event: "MESSAGE_CREATE", channel_id: channel_id, data: message } as MessageCreateEvent), channel.save() ]); - postHandleMessage(message).catch((e) => { }); // no await as it shouldnt block the message send function and silently catch error + postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error return res.json(message); } @@ -160,7 +160,7 @@ router.get("/", route({ permission: "VIEW_CHANNEL" }), async (req: Request, res: const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] }); const permissions = await getPermission(req.user_id, undefined, channel_id); - + if (message.author_id !== req.user_id) permissions.hasThrow("READ_MESSAGE_HISTORY"); return res.json(message); @@ -171,10 +171,10 @@ router.delete("/", route({}), async (req: Request, res: Response) => { const channel = await Channel.findOneOrFail({ where: { id: channel_id } }); const message = await Message.findOneOrFail({ where: { id: message_id } }); - + const rights = await getRights(req.user_id); - if ((message.author_id !== req.user_id)) { + if (message.author_id !== req.user_id) { if (!rights.has("MANAGE_MESSAGES")) { const permission = await getPermission(req.user_id, channel.guild_id, channel_id); permission.hasThrow("MANAGE_MESSAGES"); diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts index d0ab35bb..44de5c45 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts @@ -1,8 +1,10 @@ +import { route } from "@fosscord/api"; import { Channel, emitEvent, Emoji, getPermission, + HTTPError, Member, Message, MessageReactionAddEvent, @@ -13,9 +15,7 @@ import { PublicUserProjection, User } from "@fosscord/util"; -import { route } from "@fosscord/api"; -import { Router, Response, Request } from "express"; -import { HTTPError } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import { In } from "typeorm"; const router = Router(); @@ -101,48 +101,52 @@ router.get("/:emoji", route({ permission: "VIEW_CHANNEL" }), async (req: Request res.json(users); }); -router.put("/:emoji/:user_id", route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }), async (req: Request, res: Response) => { - const { message_id, channel_id, user_id } = req.params; - if (user_id !== "@me") throw new HTTPError("Invalid user"); - const emoji = getEmoji(req.params.emoji); - - const channel = await Channel.findOneOrFail({ where: { id: channel_id } }); - const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } }); - const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); - - if (!already_added) req.permission!.hasThrow("ADD_REACTIONS"); - - if (emoji.id) { - const external_emoji = await Emoji.findOneOrFail({ where: { id: emoji.id } }); - if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS"); - emoji.animated = external_emoji.animated; - emoji.name = external_emoji.name; - } +router.put( + "/:emoji/:user_id", + route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }), + async (req: Request, res: Response) => { + const { message_id, channel_id, user_id } = req.params; + if (user_id !== "@me") throw new HTTPError("Invalid user"); + const emoji = getEmoji(req.params.emoji); + + const channel = await Channel.findOneOrFail({ where: { id: channel_id } }); + const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } }); + const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); + + if (!already_added) req.permission!.hasThrow("ADD_REACTIONS"); + + if (emoji.id) { + const external_emoji = await Emoji.findOneOrFail({ where: { id: emoji.id } }); + if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS"); + emoji.animated = external_emoji.animated; + emoji.name = external_emoji.name; + } - if (already_added) { - if (already_added.user_ids.includes(req.user_id)) return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error - already_added.count++; - } else message.reactions.push({ count: 1, emoji, user_ids: [req.user_id] }); + if (already_added) { + if (already_added.user_ids.includes(req.user_id)) return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error + already_added.count++; + } else message.reactions.push({ count: 1, emoji, user_ids: [req.user_id] }); - await message.save(); + await message.save(); - const member = channel.guild_id && (await Member.findOneOrFail({ where: { id: req.user_id } })); + const member = channel.guild_id && (await Member.findOneOrFail({ where: { id: req.user_id } })); - await emitEvent({ - event: "MESSAGE_REACTION_ADD", - channel_id, - data: { - user_id: req.user_id, + await emitEvent({ + event: "MESSAGE_REACTION_ADD", channel_id, - message_id, - guild_id: channel.guild_id, - emoji, - member - } - } as MessageReactionAddEvent); + data: { + user_id: req.user_id, + channel_id, + message_id, + guild_id: channel.guild_id, + emoji, + member + } + } as MessageReactionAddEvent); - res.sendStatus(204); -}); + res.sendStatus(204); + } +); router.delete("/:emoji/:user_id", route({}), async (req: Request, res: Response) => { let { message_id, channel_id, user_id } = req.params; diff --git a/src/api/routes/channels/#channel_id/messages/bulk-delete.ts b/src/api/routes/channels/#channel_id/messages/bulk-delete.ts index af44b522..561a40c0 100644 --- a/src/api/routes/channels/#channel_id/messages/bulk-delete.ts +++ b/src/api/routes/channels/#channel_id/messages/bulk-delete.ts @@ -1,7 +1,6 @@ -import { Router, Response, Request } from "express"; -import { Channel, Config, emitEvent, getPermission, getRights, MessageDeleteBulkEvent, Message } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Channel, Config, emitEvent, getPermission, getRights, HTTPError, Message, MessageDeleteBulkEvent } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import { In } from "typeorm"; const router: Router = Router(); @@ -13,7 +12,7 @@ export default router; // https://discord.com/developers/docs/resources/channel#bulk-delete-messages router.post("/", route({ body: "BulkDeleteSchema" }), async (req: Request, res: Response) => { const { channel_id } = req.params; - const channel = await Channel.findOneOrFail({where:{ id: channel_id} }); + const channel = await Channel.findOneOrFail({ where: { id: channel_id } }); if (!channel.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400); const rights = await getRights(req.user_id); diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index 9ab0d97d..5fdcb6f9 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts @@ -1,4 +1,4 @@ -import { Router, Response, Request } from "express"; +import { handleMessage, postHandleMessage, route } from "@fosscord/api"; import { Attachment, Channel, @@ -7,16 +7,15 @@ import { DmChannelDTO, emitEvent, getPermission, - getRights, + HTTPError, + Member, Message, MessageCreateEvent, + MessageCreateSchema, Snowflake, - uploadFile, - Member, - MessageCreateSchema + uploadFile } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; -import { handleMessage, postHandleMessage, route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; import multer from "multer"; import { FindManyOptions, LessThan, MoreThan } from "typeorm"; import { URL } from "url"; @@ -69,23 +68,20 @@ router.get("/", async (req: Request, res: Response) => { permissions.hasThrow("VIEW_CHANNEL"); if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]); - let query: FindManyOptions<Message> & { where: { id?: any; }; } = { + let query: FindManyOptions<Message> & { where: { id?: any } } = { order: { id: "DESC" }, take: limit, where: { channel_id }, relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"] }; - if (after) { if (after > new Snowflake()) return res.status(422); query.where.id = MoreThan(after); - } - else if (before) { + } else if (before) { if (before < req.params.channel_id) return res.status(422); query.where.id = LessThan(before); - } - else if (around) { + } else if (around) { query.where.id = [ MoreThan((BigInt(around) - BigInt(halfLimit)).toString()), LessThan((BigInt(around) + BigInt(halfLimit)).toString()) @@ -110,15 +106,14 @@ router.get("/", async (req: Request, res: Response) => { const uri = y.proxy_url.startsWith("http") ? y.proxy_url : `https://example.org${y.proxy_url}`; y.proxy_url = `${endpoint == null ? "" : endpoint}${new URL(uri).pathname}`; }); - + /** Some clients ( discord.js ) only check if a property exists within the response, which causes erorrs when, say, the `application` property is `null`. **/ - + for (let curr in x) { - if (x[curr] === null) - delete x[curr]; + if (x[curr] === null) delete x[curr]; } return x; @@ -130,7 +125,7 @@ router.get("/", async (req: Request, res: Response) => { const messageUpload = multer({ limits: { fileSize: 1024 * 1024 * 100, - fields: 10, + fields: 10 // files: 1 }, storage: multer.memoryStorage() @@ -162,16 +157,15 @@ router.post( const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients", "recipients.user"] }); if (!channel.isWritable()) { - throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400) + throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400); } - const files = req.files as Express.Multer.File[] ?? []; + const files = (req.files as Express.Multer.File[]) ?? []; for (let currFile of files) { try { const file: any = await uploadFile(`/attachments/${channel.id}`, currFile); attachments.push({ ...file, proxy_url: file.url }); - } - catch (error) { + } catch (error) { return res.status(400).json(error); } } @@ -212,11 +206,11 @@ router.post( }) ); } - - //Defining member fields + + //Defining member fields var member = await Member.findOneOrFail({ where: { id: req.user_id }, relations: ["roles"] }); // TODO: This doesn't work either - // member.roles = member.roles.filter((role) => { + // member.roles = member.roles.filter((role) => { // return role.id !== role.guild_id; // }).map((role) => { // return role.id; @@ -225,7 +219,7 @@ router.post( // TODO: Figure this out // delete message.member.last_message_id; // delete message.member.index; - + await Promise.all([ message.save(), emitEvent({ event: "MESSAGE_CREATE", channel_id: channel_id, data: message } as MessageCreateEvent), @@ -233,9 +227,8 @@ router.post( channel.save() ]); - postHandleMessage(message).catch((e) => { }); // no await as it shouldnt block the message send function and silently catch error + postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error return res.json(message); } ); - diff --git a/src/api/routes/channels/#channel_id/permissions.ts b/src/api/routes/channels/#channel_id/permissions.ts index 34052fe5..bd462ea6 100644 --- a/src/api/routes/channels/#channel_id/permissions.ts +++ b/src/api/routes/channels/#channel_id/permissions.ts @@ -1,17 +1,15 @@ +import { route } from "@fosscord/api"; import { Channel, ChannelPermissionOverwrite, ChannelPermissionOverwriteSchema, - ChannelPermissionOverwriteType, ChannelUpdateEvent, emitEvent, - getPermission, + HTTPError, Member, Role } from "@fosscord/util"; -import { Router, Response, Request } from "express"; -import { HTTPError } from "@fosscord/util"; -import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -22,7 +20,7 @@ router.put( const { channel_id, overwrite_id } = req.params; const body = req.body as ChannelPermissionOverwriteSchema; - let channel = await Channel.findOneOrFail({ where: {id: channel_id} }); + let channel = await Channel.findOneOrFail({ where: { id: channel_id } }); if (!channel.guild_id) throw new HTTPError("Channel not found", 404); if (body.type === 0) { diff --git a/src/api/routes/channels/#channel_id/pins.ts b/src/api/routes/channels/#channel_id/pins.ts index 003638c5..5c28feac 100644 --- a/src/api/routes/channels/#channel_id/pins.ts +++ b/src/api/routes/channels/#channel_id/pins.ts @@ -1,16 +1,6 @@ -import { - Channel, - ChannelPinsUpdateEvent, - Config, - emitEvent, - getPermission, - Message, - MessageUpdateEvent, - DiscordApiErrors -} from "@fosscord/util"; -import { Router, Request, Response } from "express"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Channel, ChannelPinsUpdateEvent, Config, DiscordApiErrors, emitEvent, Message, MessageUpdateEvent } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/channels/#channel_id/purge.ts b/src/api/routes/channels/#channel_id/purge.ts index 1ef6e1d7..aebdb832 100644 --- a/src/api/routes/channels/#channel_id/purge.ts +++ b/src/api/routes/channels/#channel_id/purge.ts @@ -1,10 +1,18 @@ -import { HTTPError, PurgeSchema } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { + Channel, + Config, + emitEvent, + getPermission, + getRights, + HTTPError, + Message, + MessageDeleteBulkEvent, + PurgeSchema +} from "@fosscord/util"; +import { Request, Response, Router } from "express"; +import { Between, FindManyOptions, In, Not } from "typeorm"; import { isTextChannel } from "./messages"; -import { FindManyOptions, Between, Not } from "typeorm"; -import { Channel, Config, emitEvent, getPermission, getRights, Message, MessageDeleteBulkEvent } from "@fosscord/util"; -import { Router, Response, Request } from "express"; -import { In } from "typeorm"; const router: Router = Router(); @@ -13,7 +21,12 @@ export default router; /** TODO: apply the delete bit by bit to prevent client and database stress **/ -router.post("/",route({ /*body: "PurgeSchema",*/ }), async (req: Request, res: Response) => { +router.post( + "/", + route({ + /*body: "PurgeSchema",*/ + }), + async (req: Request, res: Response) => { const { channel_id } = req.params; const channel = await Channel.findOneOrFail({ where: { id: channel_id } }); diff --git a/src/api/routes/channels/#channel_id/recipients.ts b/src/api/routes/channels/#channel_id/recipients.ts index 069212e2..276a0eda 100644 --- a/src/api/routes/channels/#channel_id/recipients.ts +++ b/src/api/routes/channels/#channel_id/recipients.ts @@ -1,4 +1,4 @@ -import { Request, Response, Router } from "express"; +import { route } from "@fosscord/api"; import { Channel, ChannelRecipientAddEvent, @@ -6,12 +6,12 @@ import { DiscordApiErrors, DmChannelDTO, emitEvent, + OrmUtils, PublicUserProjection, Recipient, User } from "@fosscord/util"; -import { route } from "@fosscord/api"; -import { OrmUtils } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/channels/#channel_id/typing.ts b/src/api/routes/channels/#channel_id/typing.ts index 99460f6e..26d0fcfa 100644 --- a/src/api/routes/channels/#channel_id/typing.ts +++ b/src/api/routes/channels/#channel_id/typing.ts @@ -1,6 +1,6 @@ -import { Channel, emitEvent, Member, TypingStartEvent } from "@fosscord/util"; import { route } from "@fosscord/api"; -import { Router, Request, Response } from "express"; +import { Channel, emitEvent, Member, TypingStartEvent } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/channels/#channel_id/webhooks.ts b/src/api/routes/channels/#channel_id/webhooks.ts index b11c8eb9..38dcb869 100644 --- a/src/api/routes/channels/#channel_id/webhooks.ts +++ b/src/api/routes/channels/#channel_id/webhooks.ts @@ -1,9 +1,7 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; -import { Channel, Config, getPermission, trimSpecial, Webhook } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; +import { Channel, Config, DiscordApiErrors, HTTPError, trimSpecial, Webhook } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import { isTextChannel } from "./messages/index"; -import { DiscordApiErrors } from "@fosscord/util"; const router: Router = Router(); //TODO: implement webhooks diff --git a/src/api/routes/discoverable-guilds.ts b/src/api/routes/discoverable-guilds.ts index 35ecf28c..2bf49287 100644 --- a/src/api/routes/discoverable-guilds.ts +++ b/src/api/routes/discoverable-guilds.ts @@ -1,8 +1,8 @@ -import { Guild, Config } from "@fosscord/util"; +import { Config, Guild } from "@fosscord/util"; -import { Router, Request, Response } from "express"; -import { route } from ".."; +import { Request, Response, Router } from "express"; import { Like } from "typeorm"; +import { route } from ".."; const router = Router(); diff --git a/src/api/routes/discovery.ts b/src/api/routes/discovery.ts index 30c418c6..7b9edd48 100644 --- a/src/api/routes/discovery.ts +++ b/src/api/routes/discovery.ts @@ -1,5 +1,5 @@ import { Categories } from "@fosscord/util"; -import { Router, Response, Request } from "express"; +import { Request, Response, Router } from "express"; import { route } from ".."; const router = Router(); @@ -10,7 +10,7 @@ router.get("/categories", route({}), async (req: Request, res: Response) => { const { locale, primary_only } = req.query; - const out = primary_only ? await Categories.find() : await Categories.find({ where: {is_primary: true} }); + const out = primary_only ? await Categories.find() : await Categories.find({ where: { is_primary: true } }); res.send(out); }); diff --git a/src/api/routes/downloads.ts b/src/api/routes/downloads.ts index 44530353..c86c1fb0 100644 --- a/src/api/routes/downloads.ts +++ b/src/api/routes/downloads.ts @@ -1,6 +1,6 @@ -import { Router, Response, Request } from "express"; +import { Config, Release } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import { route } from ".."; -import { Release, Config } from "@fosscord/util"; const router = Router(); @@ -10,7 +10,7 @@ router.get("/:branch", route({}), async (req: Request, res: Response) => { const { platform } = req.query; //TODO - if(!platform || !["linux", "osx", "win"].includes(platform.toString())) return res.status(404) + if (!platform || !["linux", "osx", "win"].includes(platform.toString())) return res.status(404); const release = await Release.findOneOrFail({ where: { name: client.releases.upstreamVersion } }); diff --git a/src/api/routes/experiments.ts b/src/api/routes/experiments.ts index fcbd9271..0355c631 100644 --- a/src/api/routes/experiments.ts +++ b/src/api/routes/experiments.ts @@ -1,11 +1,11 @@ -import { Router, Response, Request } from "express"; +import { Request, Response, Router } from "express"; import { route } from ".."; const router = Router(); router.get("/", route({}), (req: Request, res: Response) => { // TODO: - res.send({ fingerprint: "", assignments: [], guild_experiments:[] }); + res.send({ fingerprint: "", assignments: [], guild_experiments: [] }); }); export default router; diff --git a/src/api/routes/gateway/bot.ts b/src/api/routes/gateway/bot.ts index f1dbb9df..0e44f6b2 100644 --- a/src/api/routes/gateway/bot.ts +++ b/src/api/routes/gateway/bot.ts @@ -1,6 +1,6 @@ -import { Config } from "@fosscord/util"; -import { Router, Response, Request } from "express"; import { route, RouteOptions } from "@fosscord/api"; +import { Config } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/gateway/index.ts b/src/api/routes/gateway/index.ts index 9bad7478..47037573 100644 --- a/src/api/routes/gateway/index.ts +++ b/src/api/routes/gateway/index.ts @@ -1,6 +1,6 @@ -import { Config } from "@fosscord/util"; -import { Router, Response, Request } from "express"; import { route, RouteOptions } from "@fosscord/api"; +import { Config } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/gifs/search.ts b/src/api/routes/gifs/search.ts index 1099dc4a..8b5e984a 100644 --- a/src/api/routes/gifs/search.ts +++ b/src/api/routes/gifs/search.ts @@ -1,7 +1,7 @@ -import { Router, Response, Request } from "express"; -import fetch from "node-fetch"; -import ProxyAgent from 'proxy-agent'; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; +import fetch from "node-fetch"; +import ProxyAgent from "proxy-agent"; import { getGifApiKey, parseGifResult } from "./trending"; const router = Router(); @@ -11,7 +11,7 @@ router.get("/", route({}), async (req: Request, res: Response) => { const { q, media_format, locale } = req.query; const apiKey = getGifApiKey(); - + const agent = new ProxyAgent(); const response = await fetch(`https://g.tenor.com/v1/search?q=${q}&media_format=${media_format}&locale=${locale}&key=${apiKey}`, { @@ -20,7 +20,7 @@ router.get("/", route({}), async (req: Request, res: Response) => { headers: { "Content-Type": "application/json" } }); - const { results } = await response.json() as any; + const { results } = (await response.json()) as any; res.json(results.map(parseGifResult)).status(200); }); diff --git a/src/api/routes/gifs/trending-gifs.ts b/src/api/routes/gifs/trending-gifs.ts index 2b28d9d2..65a9600e 100644 --- a/src/api/routes/gifs/trending-gifs.ts +++ b/src/api/routes/gifs/trending-gifs.ts @@ -1,7 +1,7 @@ -import { Router, Response, Request } from "express"; -import fetch from "node-fetch"; -import ProxyAgent from 'proxy-agent'; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; +import fetch from "node-fetch"; +import ProxyAgent from "proxy-agent"; import { getGifApiKey, parseGifResult } from "./trending"; const router = Router(); @@ -11,7 +11,7 @@ router.get("/", route({}), async (req: Request, res: Response) => { const { media_format, locale } = req.query; const apiKey = getGifApiKey(); - + const agent = new ProxyAgent(); const response = await fetch(`https://g.tenor.com/v1/trending?media_format=${media_format}&locale=${locale}&key=${apiKey}`, { @@ -20,7 +20,7 @@ router.get("/", route({}), async (req: Request, res: Response) => { headers: { "Content-Type": "application/json" } }); - const { results } = await response.json() as any; + const { results } = (await response.json()) as any; res.json(results.map(parseGifResult)).status(200); }); diff --git a/src/api/routes/gifs/trending.ts b/src/api/routes/gifs/trending.ts index 61eb76c4..45396ff0 100644 --- a/src/api/routes/gifs/trending.ts +++ b/src/api/routes/gifs/trending.ts @@ -1,9 +1,8 @@ -import { Router, Response, Request } from "express"; -import fetch from "node-fetch"; -import ProxyAgent from 'proxy-agent'; import { route } from "@fosscord/api"; -import { Config } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; +import { Config, HTTPError } from "@fosscord/util"; +import { Request, Response, Router } from "express"; +import fetch from "node-fetch"; +import ProxyAgent from "proxy-agent"; const router = Router(); @@ -34,7 +33,7 @@ router.get("/", route({}), async (req: Request, res: Response) => { const { media_format, locale } = req.query; const apiKey = getGifApiKey(); - + const agent = new ProxyAgent(); const [responseSource, trendGifSource] = await Promise.all([ @@ -50,8 +49,8 @@ router.get("/", route({}), async (req: Request, res: Response) => { }) ]); - const { tags } = await responseSource.json() as any; - const { results } = await trendGifSource.json() as any; + const { tags } = (await responseSource.json()) as any; + const { results } = (await trendGifSource.json()) as any; res.json({ categories: tags.map((x: any) => ({ name: x.searchterm, src: x.image })), diff --git a/src/api/routes/guild-recommendations.ts b/src/api/routes/guild-recommendations.ts index bd0140d6..0248a9c3 100644 --- a/src/api/routes/guild-recommendations.ts +++ b/src/api/routes/guild-recommendations.ts @@ -1,8 +1,8 @@ -import { Guild, Config } from "@fosscord/util"; +import { Config, Guild } from "@fosscord/util"; -import { Router, Request, Response } from "express"; +import { Request, Response, Router } from "express"; +import { Like } from "typeorm"; import { route } from ".."; -import {Like} from "typeorm" const router = Router(); @@ -13,12 +13,12 @@ router.get("/", route({}), async (req: Request, res: Response) => { // TODO: implement this with default typeorm query // const guilds = await Guild.find({ where: { features: "DISCOVERABLE" } }); //, take: Math.abs(Number(limit)) }); - const genLoadId = (size: Number) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join(''); + const genLoadId = (size: Number) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join(""); const guilds = showAllGuilds ? await Guild.find({ take: Math.abs(Number(limit || 24)) }) - : await Guild.find({ where: { features: Like('%DISCOVERABLE%') }, take: Math.abs(Number(limit || 24)) }); - res.send({ recommended_guilds: guilds, load_id: `server_recs/${genLoadId(32)}`}).status(200); + : await Guild.find({ where: { features: Like("%DISCOVERABLE%") }, take: Math.abs(Number(limit || 24)) }); + res.send({ recommended_guilds: guilds, load_id: `server_recs/${genLoadId(32)}` }).status(200); }); export default router; diff --git a/src/api/routes/guilds/#guild_id/audit-logs.ts b/src/api/routes/guilds/#guild_id/audit-logs.ts index b54835fc..05b9982e 100644 --- a/src/api/routes/guilds/#guild_id/audit-logs.ts +++ b/src/api/routes/guilds/#guild_id/audit-logs.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); //TODO: implement audit logs diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts index 3d405344..4600b4cb 100644 --- a/src/api/routes/guilds/#guild_id/bans.ts +++ b/src/api/routes/guilds/#guild_id/bans.ts @@ -1,8 +1,18 @@ -import { Request, Response, Router } from "express"; -import { DiscordApiErrors, emitEvent, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, Guild, Ban, User, Member, BanRegistrySchema, BanModeratorSchema } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; import { getIpAdress, route } from "@fosscord/api"; -import { OrmUtils } from "@fosscord/util"; +import { + Ban, + BanModeratorSchema, + BanRegistrySchema, + DiscordApiErrors, + emitEvent, + GuildBanAddEvent, + GuildBanRemoveEvent, + HTTPError, + Member, + OrmUtils, + User +} from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -44,16 +54,16 @@ router.get("/:user", route({ permission: "BAN_MEMBERS" }), async (req: Request, const { guild_id } = req.params; const user_id = req.params.ban; - let ban = await Ban.findOneOrFail({ where: { guild_id, user_id } }) as BanRegistrySchema; - + let ban = (await Ban.findOneOrFail({ where: { guild_id, user_id } })) as BanRegistrySchema; + if (ban.user_id === ban.executor_id) throw DiscordApiErrors.UNKNOWN_BAN; // pretend self-bans don't exist to prevent victim chasing - + /* Filter secret from registry. */ - + ban = ban as BanModeratorSchema; - delete ban.ip + delete ban.ip; return res.json(ban); }); @@ -62,14 +72,14 @@ router.put("/:user_id", route({ body: "BanCreateSchema", permission: "BAN_MEMBER const { guild_id } = req.params; const banned_user_id = req.params.user_id; - if ( (req.user_id === banned_user_id) && (banned_user_id === req.permission!.cache.guild?.owner_id)) + if (req.user_id === banned_user_id && banned_user_id === req.permission!.cache.guild?.owner_id) throw new HTTPError("You are the guild owner, hence can't ban yourself", 403); - + if (req.permission!.cache.guild?.owner_id === banned_user_id) throw new HTTPError("You can't ban the owner", 400); - + const banned_user = await User.getPublicUser(banned_user_id); - const ban = OrmUtils.mergeDeep(new Ban(),{ + const ban = OrmUtils.mergeDeep(new Ban(), { user_id: banned_user_id, guild_id: guild_id, ip: getIpAdress(req), @@ -93,14 +103,14 @@ router.put("/:user_id", route({ body: "BanCreateSchema", permission: "BAN_MEMBER return res.json(ban); }); -router.put("/@me", route({ body: "BanCreateSchema"}), async (req: Request, res: Response) => { +router.put("/@me", route({ body: "BanCreateSchema" }), async (req: Request, res: Response) => { const { guild_id } = req.params; const banned_user = await User.getPublicUser(req.params.user_id); - if (req.permission!.cache.guild?.owner_id === req.params.user_id) + if (req.permission!.cache.guild?.owner_id === req.params.user_id) throw new HTTPError("You are the guild owner, hence can't ban yourself", 403); - + const ban = OrmUtils.mergeDeep(new Ban(), { user_id: req.params.user_id, guild_id: guild_id, @@ -129,12 +139,12 @@ router.delete("/:user_id", route({ permission: "BAN_MEMBERS" }), async (req: Req const { guild_id, user_id } = req.params; let ban = await Ban.findOneOrFail({ where: { guild_id, user_id } }); - + if (ban.user_id === ban.executor_id) throw DiscordApiErrors.UNKNOWN_BAN; // make self-bans irreversible and hide them from view to avoid victim chasing - + const banned_user = await User.getPublicUser(user_id); - + await Promise.all([ Ban.delete({ user_id: user_id, diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index 8f2d3643..3563eb4c 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -1,7 +1,6 @@ -import { Router, Response, Request } from "express"; -import { Channel, ChannelUpdateEvent, getPermission, emitEvent, ChannelModifySchema, ChannelReorderSchema } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Channel, ChannelModifySchema, ChannelReorderSchema, ChannelUpdateEvent, emitEvent, HTTPError } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { diff --git a/src/api/routes/guilds/#guild_id/delete.ts b/src/api/routes/guilds/#guild_id/delete.ts index e2624651..e6a1a6b2 100644 --- a/src/api/routes/guilds/#guild_id/delete.ts +++ b/src/api/routes/guilds/#guild_id/delete.ts @@ -1,7 +1,6 @@ -import { Channel, emitEvent, GuildDeleteEvent, Guild, Member, Message, Role, Invite, Emoji } from "@fosscord/util"; -import { Router, Request, Response } from "express"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { emitEvent, Guild, GuildDeleteEvent, HTTPError } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/guilds/#guild_id/discovery-requirements.ts b/src/api/routes/guilds/#guild_id/discovery-requirements.ts index ad20633f..c0260fe7 100644 --- a/src/api/routes/guilds/#guild_id/discovery-requirements.ts +++ b/src/api/routes/guilds/#guild_id/discovery-requirements.ts @@ -1,38 +1,36 @@ -import { Guild, Config } from "@fosscord/util"; - -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; - // TODO: - // Load from database - // Admin control, but for now it allows anyone to be discoverable + const { guild_id } = req.params; + // TODO: + // Load from database + // Admin control, but for now it allows anyone to be discoverable res.send({ guild_id: guild_id, safe_environment: true, - healthy: true, - health_score_pending: false, - size: true, - nsfw_properties: {}, - protected: true, - sufficient: true, - sufficient_without_grace_period: true, - valid_rules_channel: true, - retention_healthy: true, - engagement_healthy: true, - age: true, - minimum_age: 0, - health_score: { - avg_nonnew_participators: 0, - avg_nonnew_communicators: 0, - num_intentful_joiners: 0, - perc_ret_w1_intentful: 0 - }, - minimum_size: 0 + healthy: true, + health_score_pending: false, + size: true, + nsfw_properties: {}, + protected: true, + sufficient: true, + sufficient_without_grace_period: true, + valid_rules_channel: true, + retention_healthy: true, + engagement_healthy: true, + age: true, + minimum_age: 0, + health_score: { + avg_nonnew_participators: 0, + avg_nonnew_communicators: 0, + num_intentful_joiners: 0, + perc_ret_w1_intentful: 0 + }, + minimum_size: 0 }); }); diff --git a/src/api/routes/guilds/#guild_id/emojis.ts b/src/api/routes/guilds/#guild_id/emojis.ts index 4bf4bdcd..db5ae325 100644 --- a/src/api/routes/guilds/#guild_id/emojis.ts +++ b/src/api/routes/guilds/#guild_id/emojis.ts @@ -1,7 +1,19 @@ -import { Router, Request, Response } from "express"; -import { Config, DiscordApiErrors, emitEvent, Emoji, EmojiCreateSchema, EmojiModifySchema, GuildEmojisUpdateEvent, handleFile, Member, Snowflake, User } from "@fosscord/util"; import { route } from "@fosscord/api"; -import { OrmUtils } from "@fosscord/util"; +import { + Config, + DiscordApiErrors, + emitEvent, + Emoji, + EmojiCreateSchema, + EmojiModifySchema, + GuildEmojisUpdateEvent, + handleFile, + Member, + OrmUtils, + Snowflake, + User +} from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/guilds/#guild_id/index.ts b/src/api/routes/guilds/#guild_id/index.ts index a9712c71..af889982 100644 --- a/src/api/routes/guilds/#guild_id/index.ts +++ b/src/api/routes/guilds/#guild_id/index.ts @@ -1,8 +1,18 @@ -import { Request, Response, Router } from "express"; -import { DiscordApiErrors, emitEvent, getPermission, getRights, Guild, GuildUpdateEvent, GuildUpdateSchema, handleFile, Member } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; -import { OrmUtils } from "@fosscord/util"; +import { + DiscordApiErrors, + emitEvent, + getPermission, + getRights, + Guild, + GuildUpdateEvent, + GuildUpdateSchema, + handleFile, + HTTPError, + Member, + OrmUtils +} from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); @@ -21,17 +31,16 @@ router.get("/", route({}), async (req: Request, res: Response) => { return res.send(guild); }); -router.patch("/", route({ body: "GuildUpdateSchema"}), async (req: Request, res: Response) => { +router.patch("/", route({ body: "GuildUpdateSchema" }), async (req: Request, res: Response) => { const body = req.body as GuildUpdateSchema; const { guild_id } = req.params; - - + const rights = await getRights(req.user_id); const permission = await getPermission(req.user_id, guild_id); - - if (!rights.has("MANAGE_GUILDS")||!permission.has("MANAGE_GUILD")) + + if (!rights.has("MANAGE_GUILDS") || !permission.has("MANAGE_GUILD")) throw DiscordApiErrors.MISSING_PERMISSIONS.withParams("MANAGE_GUILD"); - + // TODO: guild update check image if (body.icon) body.icon = await handleFile(`/icons/${guild_id}`, body.icon); diff --git a/src/api/routes/guilds/#guild_id/integrations.ts b/src/api/routes/guilds/#guild_id/integrations.ts index 90650111..6a5abec3 100644 --- a/src/api/routes/guilds/#guild_id/integrations.ts +++ b/src/api/routes/guilds/#guild_id/integrations.ts @@ -1,7 +1,5 @@ -import { Router, Response, Request } from "express"; -import { Channel, ChannelUpdateEvent, getPermission, emitEvent } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); //TODO: implement integrations list diff --git a/src/api/routes/guilds/#guild_id/invites.ts b/src/api/routes/guilds/#guild_id/invites.ts index b7534e31..c663df72 100644 --- a/src/api/routes/guilds/#guild_id/invites.ts +++ b/src/api/routes/guilds/#guild_id/invites.ts @@ -1,5 +1,5 @@ -import { getPermission, Invite, PublicInviteRelation } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Invite, PublicInviteRelation } from "@fosscord/util"; import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts index 794369d8..57152f9a 100644 --- a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts +++ b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts @@ -1,8 +1,18 @@ -import { Request, Response, Router } from "express"; -import { Member, getPermission, getRights, Role, GuildMemberUpdateEvent, emitEvent, Sticker, Emoji, Rights, Guild, MemberChangeSchema } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; -import { OrmUtils } from "@fosscord/util"; +import { + emitEvent, + Emoji, + getPermission, + getRights, + Guild, + GuildMemberUpdateEvent, + Member, + MemberChangeSchema, + OrmUtils, + Role, + Sticker +} from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); @@ -46,7 +56,6 @@ router.patch("/", route({ body: "MemberChangeSchema" }), async (req: Request, re }); router.put("/", route({}), async (req: Request, res: Response) => { - // TODO: Lurker mode const rights = await getRights(req.user_id); @@ -56,7 +65,7 @@ router.put("/", route({}), async (req: Request, res: Response) => { member_id = req.user_id; rights.hasThrow("JOIN_GUILDS"); } else { - // TODO: join others by controller + // TODO: join others by controller } let guild = await Guild.findOneOrFail({ diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts b/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts index a6c71333..26411f97 100644 --- a/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts +++ b/src/api/routes/guilds/#guild_id/members/#member_id/nick.ts @@ -1,5 +1,5 @@ -import { getPermission, Member, PermissionResolvable } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { getPermission, Member, PermissionResolvable } from "@fosscord/util"; import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts b/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts index 8f5ca7ba..0aa7a4dc 100644 --- a/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts +++ b/src/api/routes/guilds/#guild_id/members/#member_id/roles/#role_id/index.ts @@ -1,5 +1,5 @@ -import { getPermission, Member } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Member } from "@fosscord/util"; import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/guilds/#guild_id/members/index.ts b/src/api/routes/guilds/#guild_id/members/index.ts index 2ed28bda..08164626 100644 --- a/src/api/routes/guilds/#guild_id/members/index.ts +++ b/src/api/routes/guilds/#guild_id/members/index.ts @@ -1,8 +1,7 @@ -import { Request, Response, Router } from "express"; -import { Guild, Member, PublicMemberProjection } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { HTTPError, Member, PublicMemberProjection } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import { MoreThan } from "typeorm"; -import { HTTPError } from "@fosscord/util"; const router = Router(); diff --git a/src/api/routes/guilds/#guild_id/premium.ts b/src/api/routes/guilds/#guild_id/premium.ts index 75361ac6..b7716378 100644 --- a/src/api/routes/guilds/#guild_id/premium.ts +++ b/src/api/routes/guilds/#guild_id/premium.ts @@ -1,5 +1,5 @@ -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); router.get("/subscriptions", route({}), async (req: Request, res: Response) => { diff --git a/src/api/routes/guilds/#guild_id/prune.ts b/src/api/routes/guilds/#guild_id/prune.ts index 673f022f..3645721c 100644 --- a/src/api/routes/guilds/#guild_id/prune.ts +++ b/src/api/routes/guilds/#guild_id/prune.ts @@ -1,7 +1,7 @@ -import { Router, Request, Response } from "express"; -import { Guild, Member, Snowflake } from "@fosscord/util"; -import { LessThan, IsNull } from "typeorm"; import { route } from "@fosscord/api"; +import { Guild, Member, Snowflake } from "@fosscord/util"; +import { Request, Response, Router } from "express"; +import { IsNull, LessThan } from "typeorm"; const router = Router(); //Returns all inactive members, respecting role hierarchy diff --git a/src/api/routes/guilds/#guild_id/regions.ts b/src/api/routes/guilds/#guild_id/regions.ts index 308d5ee5..aa57ec65 100644 --- a/src/api/routes/guilds/#guild_id/regions.ts +++ b/src/api/routes/guilds/#guild_id/regions.ts @@ -1,7 +1,6 @@ -import { Config, Guild, Member } from "@fosscord/util"; +import { getIpAdress, getVoiceRegions, route } from "@fosscord/api"; +import { Guild } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { getVoiceRegions, route } from "@fosscord/api"; -import { getIpAdress } from "@fosscord/api"; const router = Router(); diff --git a/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts b/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts index d4422a9c..7f9dbc6f 100644 --- a/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts +++ b/src/api/routes/guilds/#guild_id/roles/#role_id/index.ts @@ -1,8 +1,16 @@ -import { Router, Request, Response } from "express"; -import { Role, Member, GuildRoleUpdateEvent, GuildRoleDeleteEvent, emitEvent, handleFile, RoleModifySchema } from "@fosscord/util"; import { route } from "@fosscord/api"; -import { HTTPError } from "@fosscord/util"; -import { OrmUtils } from "@fosscord/util"; +import { + emitEvent, + GuildRoleDeleteEvent, + GuildRoleUpdateEvent, + handleFile, + HTTPError, + Member, + OrmUtils, + Role, + RoleModifySchema +} from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/guilds/#guild_id/roles/index.ts b/src/api/routes/guilds/#guild_id/roles/index.ts index 17f0b5e9..9791f7a9 100644 --- a/src/api/routes/guilds/#guild_id/roles/index.ts +++ b/src/api/routes/guilds/#guild_id/roles/index.ts @@ -1,21 +1,18 @@ -import { Request, Response, Router } from "express"; +import { route } from "@fosscord/api"; import { - Role, + Config, + DiscordApiErrors, + emitEvent, getPermission, - Member, GuildRoleCreateEvent, GuildRoleUpdateEvent, - GuildRoleDeleteEvent, - emitEvent, - Config, - DiscordApiErrors, - handleFile, + Member, + OrmUtils, + Role, RoleModifySchema, RolePositionUpdateSchema } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; -import { route } from "@fosscord/api"; -import { OrmUtils } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -38,7 +35,7 @@ router.post("/", route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" }) if (role_count > maxRoles) throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles); - let role: Role = OrmUtils.mergeDeep(new Role(),{ + let role: Role = OrmUtils.mergeDeep(new Role(), { // values before ...body are default and can be overriden position: 0, hoist: false, diff --git a/src/api/routes/guilds/#guild_id/stickers.ts b/src/api/routes/guilds/#guild_id/stickers.ts index 71c9dfcd..15741780 100644 --- a/src/api/routes/guilds/#guild_id/stickers.ts +++ b/src/api/routes/guilds/#guild_id/stickers.ts @@ -1,20 +1,19 @@ +import { route } from "@fosscord/api"; import { emitEvent, GuildStickersUpdateEvent, - handleFile, + HTTPError, Member, ModifyGuildStickerSchema, + OrmUtils, Snowflake, Sticker, StickerFormatType, StickerType, uploadFile } from "@fosscord/util"; -import { Router, Request, Response } from "express"; -import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; import multer from "multer"; -import { HTTPError } from "@fosscord/util"; -import { OrmUtils } from "@fosscord/util"; const router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { diff --git a/src/api/routes/guilds/#guild_id/templates.ts b/src/api/routes/guilds/#guild_id/templates.ts index 9c79692d..448ee033 100644 --- a/src/api/routes/guilds/#guild_id/templates.ts +++ b/src/api/routes/guilds/#guild_id/templates.ts @@ -1,9 +1,6 @@ +import { generateCode, route } from "@fosscord/api"; +import { Guild, HTTPError, OrmUtils, Template } from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { Guild, Template } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; -import { route } from "@fosscord/api"; -import { generateCode } from "@fosscord/api"; -import { OrmUtils } from "@fosscord/util"; const router: Router = Router(); @@ -75,7 +72,12 @@ router.patch("/:code", route({ body: "TemplateModifySchema", permission: "MANAGE const { code, guild_id } = req.params; const { name, description } = req.body; - const template = await OrmUtils.mergeDeep(new Template(), { code, name: name, description: description, source_guild_id: guild_id }).save(); + const template = await OrmUtils.mergeDeep(new Template(), { + code, + name: name, + description: description, + source_guild_id: guild_id + }).save(); res.json(template); }); diff --git a/src/api/routes/guilds/#guild_id/vanity-url.ts b/src/api/routes/guilds/#guild_id/vanity-url.ts index ff92ce8d..bf2db134 100644 --- a/src/api/routes/guilds/#guild_id/vanity-url.ts +++ b/src/api/routes/guilds/#guild_id/vanity-url.ts @@ -1,8 +1,6 @@ -import { Channel, ChannelType, getPermission, Guild, Invite, trimSpecial, VanityUrlSchema } from "@fosscord/util"; -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; -import { HTTPError } from "@fosscord/util"; -import { OrmUtils } from "@fosscord/util"; +import { Channel, ChannelType, Guild, HTTPError, Invite, OrmUtils, VanityUrlSchema } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts b/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts index 28a9e8c1..797d348e 100644 --- a/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts +++ b/src/api/routes/guilds/#guild_id/voice-states/#user_id/index.ts @@ -1,7 +1,16 @@ -import { Channel, ChannelType, DiscordApiErrors, emitEvent, getPermission, VoiceState, VoiceStateUpdateEvent, VoiceStateUpdateSchema } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { + Channel, + ChannelType, + DiscordApiErrors, + emitEvent, + getPermission, + OrmUtils, + VoiceState, + VoiceStateUpdateEvent, + VoiceStateUpdateSchema +} from "@fosscord/util"; import { Request, Response, Router } from "express"; -import { OrmUtils } from "@fosscord/util"; const router = Router(); router.patch("/", route({ body: "VoiceStateUpdateSchema" }), async (req: Request, res: Response) => { diff --git a/src/api/routes/guilds/#guild_id/webhooks.ts b/src/api/routes/guilds/#guild_id/webhooks.ts index c8c1eb5c..80e6a59a 100644 --- a/src/api/routes/guilds/#guild_id/webhooks.ts +++ b/src/api/routes/guilds/#guild_id/webhooks.ts @@ -1,7 +1,5 @@ -import { Router, Response, Request } from "express"; -import { Channel, ChannelUpdateEvent, getPermission, emitEvent } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); //TODO: implement webhooks diff --git a/src/api/routes/guilds/#guild_id/welcome_screen.ts b/src/api/routes/guilds/#guild_id/welcome_screen.ts index d08300ba..85c22a19 100644 --- a/src/api/routes/guilds/#guild_id/welcome_screen.ts +++ b/src/api/routes/guilds/#guild_id/welcome_screen.ts @@ -1,7 +1,6 @@ -import { Request, Response, Router } from "express"; -import { Guild, getPermission, Snowflake, Member, GuildUpdateWelcomeScreenSchema } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Guild, GuildUpdateWelcomeScreenSchema, HTTPError, Member } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/guilds/#guild_id/widget.json.ts b/src/api/routes/guilds/#guild_id/widget.json.ts index 37739418..368fe46e 100644 --- a/src/api/routes/guilds/#guild_id/widget.json.ts +++ b/src/api/routes/guilds/#guild_id/widget.json.ts @@ -1,8 +1,6 @@ -import { Request, Response, Router } from "express"; -import { Config, Permissions, Guild, Invite, Channel, Member } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; import { random, route } from "@fosscord/api"; -import { OrmUtils } from "@fosscord/util"; +import { Channel, Guild, HTTPError, Invite, Member, OrmUtils, Permissions } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/guilds/#guild_id/widget.png.ts b/src/api/routes/guilds/#guild_id/widget.png.ts index a61d938d..1c4ef29b 100644 --- a/src/api/routes/guilds/#guild_id/widget.png.ts +++ b/src/api/routes/guilds/#guild_id/widget.png.ts @@ -1,10 +1,19 @@ -import { Request, Response, Router } from "express"; -import { Guild } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Guild, HTTPError } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import fs from "fs"; import path from "path"; +// Setup canvas +let createCanvas: any, loadImage: any; +try { + createCanvas = require("canvas").createCanvas; + loadImage = require("canvas").loadImage; +} catch { + console.log("Canvas not found, disabling widgets!"); +} +const sizeOf = require("image-size"); + const router: Router = Router(); // TODO: use svg templates instead of node-canvas for improved performance and to change it easily @@ -12,6 +21,7 @@ const router: Router = Router(); // https://discord.com/developers/docs/resources/guild#get-guild-widget-image // TODO: Cache the response router.get("/", route({}), async (req: Request, res: Response) => { + if (!createCanvas) return res.status(404); const { guild_id } = req.params; const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); @@ -28,11 +38,6 @@ router.get("/", route({}), async (req: Request, res: Response) => { throw new HTTPError("Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').", 400); } - // Setup canvas - const { createCanvas } = require("canvas"); - const { loadImage } = require("canvas"); - const sizeOf = require("image-size"); - // TODO: Widget style templates need Fosscord branding const source = path.join(__dirname, "..", "..", "..", "..", "..", "assets", "widget", `${style}.png`); if (!fs.existsSync(source)) { diff --git a/src/api/routes/guilds/#guild_id/widget.ts b/src/api/routes/guilds/#guild_id/widget.ts index dbb4cc0c..d2369dd1 100644 --- a/src/api/routes/guilds/#guild_id/widget.ts +++ b/src/api/routes/guilds/#guild_id/widget.ts @@ -1,6 +1,6 @@ -import { Request, Response, Router } from "express"; -import { Guild, WidgetModifySchema } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Guild, WidgetModifySchema } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/guilds/index.ts b/src/api/routes/guilds/index.ts index e4d66192..6946e2f7 100644 --- a/src/api/routes/guilds/index.ts +++ b/src/api/routes/guilds/index.ts @@ -1,6 +1,6 @@ -import { Router, Request, Response } from "express"; -import { Role, Guild, Snowflake, Config, getRights, Member, Channel, DiscordApiErrors, handleFile, GuildCreateSchema } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Config, DiscordApiErrors, getRights, Guild, GuildCreateSchema, Member } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -12,7 +12,7 @@ router.post("/", route({ body: "GuildCreateSchema", right: "CREATE_GUILDS" }), a const { maxGuilds } = Config.get().limits.user; const guild_count = await Member.count({ where: { id: req.user_id } }); const rights = await getRights(req.user_id); - if ((guild_count >= maxGuilds)&&!rights.has("MANAGE_GUILDS")) { + if (guild_count >= maxGuilds && !rights.has("MANAGE_GUILDS")) { throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds); } diff --git a/src/api/routes/guilds/templates/index.ts b/src/api/routes/guilds/templates/index.ts index 3a0de9e8..467186a3 100644 --- a/src/api/routes/guilds/templates/index.ts +++ b/src/api/routes/guilds/templates/index.ts @@ -1,6 +1,6 @@ -import { Request, Response, Router } from "express"; -import { Template, Guild, Role, Snowflake, Config, User, Member, DiscordApiErrors, OrmUtils, GuildTemplateCreateSchema } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Config, DiscordApiErrors, Guild, GuildTemplateCreateSchema, Member, OrmUtils, Role, Snowflake, Template } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import fetch from "node-fetch"; const router: Router = Router(); @@ -9,9 +9,10 @@ router.get("/:code", route({}), async (req: Request, res: Response) => { if (!enabled) res.json({ code: 403, message: "Template creation & usage is disabled on this instance." }).sendStatus(403); const { code } = req.params; - + if (code.startsWith("discord:")) { - if (!allowDiscordTemplates) return res.json({ code: 403, message: "Discord templates cannot be used on this instance." }).sendStatus(403); + if (!allowDiscordTemplates) + return res.json({ code: 403, message: "Discord templates cannot be used on this instance." }).sendStatus(403); const discordTemplateID = code.split("discord:", 2)[1]; const discordTemplateData = await fetch(`https://discord.com/api/v9/guilds/templates/${discordTemplateID}`, { @@ -22,7 +23,7 @@ router.get("/:code", route({}), async (req: Request, res: Response) => { } if (code.startsWith("external:")) { - if (!allowRaws) return res.json({ code: 403, message: "Importing raws is disabled on this instance." }).sendStatus(403); + if (!allowRaws) return res.json({ code: 403, message: "Importing raws is disabled on this instance." }).sendStatus(403); return res.json(code.split("external:", 2)[1]); } @@ -57,18 +58,20 @@ router.post("/:code", route({ body: "GuildTemplateCreateSchema" }), async (req: id: guild_id, owner_id: req.user_id }).save(), - (OrmUtils.mergeDeep(new Role(), { - id: guild_id, - guild_id: guild_id, - color: 0, - hoist: false, - managed: true, - mentionable: true, - name: "@everyone", - permissions: BigInt("2251804225"), - position: 0, - tags: null - }) as Role).save() + ( + OrmUtils.mergeDeep(new Role(), { + id: guild_id, + guild_id: guild_id, + color: 0, + hoist: false, + managed: true, + mentionable: true, + name: "@everyone", + permissions: BigInt("2251804225"), + position: 0, + tags: null + }) as Role + ).save() ]); await Member.addToGuild(req.user_id, guild_id); diff --git a/src/api/routes/invites/index.ts b/src/api/routes/invites/index.ts index 1b434505..73c9324c 100644 --- a/src/api/routes/invites/index.ts +++ b/src/api/routes/invites/index.ts @@ -1,7 +1,6 @@ -import { Router, Request, Response } from "express"; -import { emitEvent, getPermission, Guild, Invite, InviteDeleteEvent, User, PublicInviteRelation } from "@fosscord/util"; import { route } from "@fosscord/api"; -import { HTTPError } from "@fosscord/util"; +import { emitEvent, getPermission, Guild, HTTPError, Invite, InviteDeleteEvent, PublicInviteRelation, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -13,15 +12,16 @@ router.get("/:code", route({}), async (req: Request, res: Response) => { res.status(200).send(invite); }); -router.post("/:code", route({right: "USE_MASS_INVITES"}), async (req: Request, res: Response) => { +router.post("/:code", route({ right: "USE_MASS_INVITES" }), async (req: Request, res: Response) => { const { code } = req.params; - const { guild_id } = await Invite.findOneOrFail({ where: { code } }) - const { features } = await Guild.findOneOrFail({ where: { id: guild_id} }); + const { guild_id } = await Invite.findOneOrFail({ where: { code } }); + const { features } = await Guild.findOneOrFail({ where: { id: guild_id } }); const { public_flags } = await User.findOneOrFail({ where: { id: req.user_id } }); - - if(features.includes("INTERNAL_EMPLOYEE_ONLY") && (public_flags & 1) !== 1) throw new HTTPError("Only intended for the staff of this server.", 401); - if(features.includes("INVITES_CLOSED")) throw new HTTPError("Sorry, this guild has joins closed.", 403); - + + if (features.includes("INTERNAL_EMPLOYEE_ONLY") && (public_flags & 1) !== 1) + throw new HTTPError("Only intended for the staff of this server.", 401); + if (features.includes("INVITES_CLOSED")) throw new HTTPError("Sorry, this guild has joins closed.", 403); + const invite = await Invite.joinGuild(req.user_id, code); res.json(invite); diff --git a/src/api/routes/oauth2/tokens.ts b/src/api/routes/oauth2/tokens.ts index bd284221..831dc7af 100644 --- a/src/api/routes/oauth2/tokens.ts +++ b/src/api/routes/oauth2/tokens.ts @@ -1,5 +1,5 @@ -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { diff --git a/src/api/routes/outbound-promotions.ts b/src/api/routes/outbound-promotions.ts index 411e95bf..8e407184 100644 --- a/src/api/routes/outbound-promotions.ts +++ b/src/api/routes/outbound-promotions.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/partners/#guild_id/requirements.ts b/src/api/routes/partners/#guild_id/requirements.ts index 545c5c78..c0260fe7 100644 --- a/src/api/routes/partners/#guild_id/requirements.ts +++ b/src/api/routes/partners/#guild_id/requirements.ts @@ -1,39 +1,36 @@ - -import { Guild, Config } from "@fosscord/util"; - -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { - const { guild_id } = req.params; - // TODO: - // Load from database - // Admin control, but for now it allows anyone to be discoverable + const { guild_id } = req.params; + // TODO: + // Load from database + // Admin control, but for now it allows anyone to be discoverable res.send({ guild_id: guild_id, safe_environment: true, - healthy: true, - health_score_pending: false, - size: true, - nsfw_properties: {}, - protected: true, - sufficient: true, - sufficient_without_grace_period: true, - valid_rules_channel: true, - retention_healthy: true, - engagement_healthy: true, - age: true, - minimum_age: 0, - health_score: { - avg_nonnew_participators: 0, - avg_nonnew_communicators: 0, - num_intentful_joiners: 0, - perc_ret_w1_intentful: 0 - }, - minimum_size: 0 + healthy: true, + health_score_pending: false, + size: true, + nsfw_properties: {}, + protected: true, + sufficient: true, + sufficient_without_grace_period: true, + valid_rules_channel: true, + retention_healthy: true, + engagement_healthy: true, + age: true, + minimum_age: 0, + health_score: { + avg_nonnew_participators: 0, + avg_nonnew_communicators: 0, + num_intentful_joiners: 0, + perc_ret_w1_intentful: 0 + }, + minimum_size: 0 }); }); diff --git a/src/api/routes/ping.ts b/src/api/routes/ping.ts index 3c1da2c3..5f1b0174 100644 --- a/src/api/routes/ping.ts +++ b/src/api/routes/ping.ts @@ -1,6 +1,6 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; import { Config } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); @@ -18,8 +18,8 @@ router.get("/", route({}), (req: Request, res: Response) => { correspondenceUserID: general.correspondenceUserID, frontPage: general.frontPage, - tosPage: general.tosPage, - }, + tosPage: general.tosPage + } }); }); diff --git a/src/api/routes/policies/instance/domains.ts b/src/api/routes/policies/instance/domains.ts index 20cd07ba..fed0a627 100644 --- a/src/api/routes/policies/instance/domains.ts +++ b/src/api/routes/policies/instance/domains.ts @@ -1,16 +1,15 @@ -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; import { Config } from "@fosscord/util"; -import { config } from "dotenv" +import { Request, Response, Router } from "express"; const router = Router(); -router.get("/",route({}), async (req: Request, res: Response) => { - const { cdn, gateway } = Config.get(); - - const IdentityForm = { - cdn: cdn.endpointPublic || process.env.CDN || "http://localhost:3001", - gateway: gateway.endpointPublic || process.env.GATEWAY || "ws://localhost:3002" - }; +router.get("/", route({}), async (req: Request, res: Response) => { + const { cdn, gateway } = Config.get(); + + const IdentityForm = { + cdn: cdn.endpointPublic || process.env.CDN || "http://localhost:3001", + gateway: gateway.endpointPublic || process.env.GATEWAY || "ws://localhost:3002" + }; res.json(IdentityForm); }); diff --git a/src/api/routes/policies/instance/index.ts b/src/api/routes/policies/instance/index.ts index e3da014f..a8ffd285 100644 --- a/src/api/routes/policies/instance/index.ts +++ b/src/api/routes/policies/instance/index.ts @@ -1,10 +1,9 @@ -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; import { Config } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); - -router.get("/",route({}), async (req: Request, res: Response) => { +router.get("/", route({}), async (req: Request, res: Response) => { const { general } = Config.get(); res.json(general); }); diff --git a/src/api/routes/policies/instance/limits.ts b/src/api/routes/policies/instance/limits.ts index 7de1476b..0d42fc7b 100644 --- a/src/api/routes/policies/instance/limits.ts +++ b/src/api/routes/policies/instance/limits.ts @@ -1,9 +1,9 @@ -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; import { Config } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); -router.get("/",route({}), async (req: Request, res: Response) => { +router.get("/", route({}), async (req: Request, res: Response) => { const { limits } = Config.get(); res.json(limits); }); diff --git a/src/api/routes/scheduled-maintenances/upcoming_json.ts b/src/api/routes/scheduled-maintenances/upcoming_json.ts index 83092e44..ec4ddc7c 100644 --- a/src/api/routes/scheduled-maintenances/upcoming_json.ts +++ b/src/api/routes/scheduled-maintenances/upcoming_json.ts @@ -1,12 +1,12 @@ -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); -router.get("/scheduled-maintenances/upcoming.json",route({}), async (req: Request, res: Response) => { +router.get("/scheduled-maintenances/upcoming.json", route({}), async (req: Request, res: Response) => { res.json({ - "page": {}, - "scheduled_maintenances": {} - }); + page: {}, + scheduled_maintenances: {} + }); }); export default router; diff --git a/src/api/routes/science.ts b/src/api/routes/science.ts index 8556a3ad..cb01e576 100644 --- a/src/api/routes/science.ts +++ b/src/api/routes/science.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/stage-instances.ts b/src/api/routes/stage-instances.ts index 411e95bf..8e407184 100644 --- a/src/api/routes/stage-instances.ts +++ b/src/api/routes/stage-instances.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/sticker-packs/index.ts b/src/api/routes/sticker-packs/index.ts index e6560d12..dddc7f70 100644 --- a/src/api/routes/sticker-packs/index.ts +++ b/src/api/routes/sticker-packs/index.ts @@ -1,6 +1,6 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; import { StickerPack } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/stickers/#sticker_id/index.ts b/src/api/routes/stickers/#sticker_id/index.ts index b484a7a1..16eb2059 100644 --- a/src/api/routes/stickers/#sticker_id/index.ts +++ b/src/api/routes/stickers/#sticker_id/index.ts @@ -1,6 +1,6 @@ -import { Sticker } from "@fosscord/util"; -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; +import { Sticker } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { diff --git a/src/api/routes/stop.ts b/src/api/routes/stop.ts index 7f8b78ba..fb77b4f3 100644 --- a/src/api/routes/stop.ts +++ b/src/api/routes/stop.ts @@ -1,22 +1,21 @@ -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; import { User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); router.post("/", route({}), async (req: Request, res: Response) => { //EXPERIMENTAL: have an "OPERATOR" platform permission implemented for this API route const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["rights"] }); - if((Number(user.rights) << Number(0))%Number(2)==Number(1)) { + if ((Number(user.rights) << Number(0)) % Number(2) == Number(1)) { console.log("user that POSTed to the API was ALLOWED"); console.log(user.rights); - res.sendStatus(200) - process.kill(process.pid, 'SIGTERM') - } - else { + res.sendStatus(200); + process.kill(process.pid, "SIGTERM"); + } else { console.log("operation failed"); console.log(user.rights); - res.sendStatus(403) + res.sendStatus(403); } }); diff --git a/src/api/routes/store/published-listings/applications.ts b/src/api/routes/store/published-listings/applications.ts index 060a4c3d..3d0f7998 100644 --- a/src/api/routes/store/published-listings/applications.ts +++ b/src/api/routes/store/published-listings/applications.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/store/published-listings/applications/#id/subscription-plans.ts b/src/api/routes/store/published-listings/applications/#id/subscription-plans.ts index 54151ae5..86fce75d 100644 --- a/src/api/routes/store/published-listings/applications/#id/subscription-plans.ts +++ b/src/api/routes/store/published-listings/applications/#id/subscription-plans.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/store/published-listings/skus.ts b/src/api/routes/store/published-listings/skus.ts index 060a4c3d..3d0f7998 100644 --- a/src/api/routes/store/published-listings/skus.ts +++ b/src/api/routes/store/published-listings/skus.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/store/published-listings/skus/#sku_id/subscription-plans.ts b/src/api/routes/store/published-listings/skus/#sku_id/subscription-plans.ts index e7f44ded..fdd775b7 100644 --- a/src/api/routes/store/published-listings/skus/#sku_id/subscription-plans.ts +++ b/src/api/routes/store/published-listings/skus/#sku_id/subscription-plans.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/teams.ts b/src/api/routes/teams.ts index 7ce3abcb..9aa1c10e 100644 --- a/src/api/routes/teams.ts +++ b/src/api/routes/teams.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/track.ts b/src/api/routes/track.ts index 8556a3ad..cb01e576 100644 --- a/src/api/routes/track.ts +++ b/src/api/routes/track.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/updates.ts b/src/api/routes/updates.ts index a24e94c1..6019371e 100644 --- a/src/api/routes/updates.ts +++ b/src/api/routes/updates.ts @@ -1,20 +1,20 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; import { Config, Release } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); router.get("/", route({}), async (req: Request, res: Response) => { const { client } = Config.get(); - const release = await Release.findOneOrFail({ where: { name: client.releases.upstreamVersion } }) + const release = await Release.findOneOrFail({ where: { name: client.releases.upstreamVersion } }); res.json({ - name: release.name, - pub_date: release.pub_date, - url: release.url, - notes: release.notes - }); + name: release.name, + pub_date: release.pub_date, + url: release.url, + notes: release.notes + }); }); export default router; diff --git a/src/api/routes/users/#id/index.ts b/src/api/routes/users/#id/index.ts index bdb1060f..e33e5695 100644 --- a/src/api/routes/users/#id/index.ts +++ b/src/api/routes/users/#id/index.ts @@ -1,6 +1,6 @@ -import { Router, Request, Response } from "express"; -import { User } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts index 7a995a8c..27717c79 100644 --- a/src/api/routes/users/#id/profile.ts +++ b/src/api/routes/users/#id/profile.ts @@ -1,6 +1,6 @@ -import { Router, Request, Response } from "express"; -import { PublicConnectedAccount, PublicUser, User, UserPublic, Member } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Member, PublicConnectedAccount, User, UserPublic } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -17,22 +17,22 @@ router.get("/", route({ test: { response: { body: "UserProfileResponse" } } }), let mutual_guilds: object[] = []; let premium_guild_since; - const requested_member = await Member.find( { where: { id: req.params.id, } }) - const self_member = await Member.find( { where: { id: req.user_id, } }) + const requested_member = await Member.find({ where: { id: req.params.id } }); + const self_member = await Member.find({ where: { id: req.user_id } }); - for(const rmem of requested_member) { - if(rmem.premium_since) { - if(premium_guild_since){ - if(premium_guild_since > rmem.premium_since) { + for (const rmem of requested_member) { + if (rmem.premium_since) { + if (premium_guild_since) { + if (premium_guild_since > rmem.premium_since) { premium_guild_since = rmem.premium_since; } } else { premium_guild_since = rmem.premium_since; } } - for(const smem of self_member) { + for (const smem of self_member) { if (smem.guild_id === rmem.guild_id) { - mutual_guilds.push({id: rmem.guild_id, nick: rmem.nick}) + mutual_guilds.push({ id: rmem.guild_id, nick: rmem.nick }); } } } diff --git a/src/api/routes/users/#id/relationships.ts b/src/api/routes/users/#id/relationships.ts index 61655c25..9b7e3402 100644 --- a/src/api/routes/users/#id/relationships.ts +++ b/src/api/routes/users/#id/relationships.ts @@ -1,41 +1,46 @@ -import { Router, Request, Response } from "express"; -import { User } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); export interface UserRelationsResponse { object: { - id?: string, - username?: string, - avatar?: string, - discriminator?: string, - public_flags?: number - } + id?: string; + username?: string; + avatar?: string; + discriminator?: string; + public_flags?: number; + }; } - router.get("/", route({ test: { response: { body: "UserRelationsResponse" } } }), async (req: Request, res: Response) => { let mutual_relations: object[] = []; - const requested_relations = await User.findOneOrFail({ + const requested_relations = await User.findOneOrFail({ where: { id: req.params.id }, relations: ["relationships"] }); - const self_relations = await User.findOneOrFail({ + const self_relations = await User.findOneOrFail({ where: { id: req.user_id }, relations: ["relationships"] }); - - for(const rmem of requested_relations.relationships) { - for(const smem of self_relations.relationships) - if (rmem.to_id === smem.to_id && rmem.type === 1 && rmem.to_id !== req.user_id) { - let relation_user = await User.getPublicUser(rmem.to_id) - mutual_relations.push({id: relation_user.id, username: relation_user.username, avatar: relation_user.avatar, discriminator: relation_user.discriminator, public_flags: relation_user.public_flags}) - } + for (const rmem of requested_relations.relationships) { + for (const smem of self_relations.relationships) + if (rmem.to_id === smem.to_id && rmem.type === 1 && rmem.to_id !== req.user_id) { + let relation_user = await User.getPublicUser(rmem.to_id); + + mutual_relations.push({ + id: relation_user.id, + username: relation_user.username, + avatar: relation_user.avatar, + discriminator: relation_user.discriminator, + public_flags: relation_user.public_flags + }); + } } - res.json(mutual_relations) + res.json(mutual_relations); }); export default router; diff --git a/src/api/routes/users/@me/activities/statistics/applications.ts b/src/api/routes/users/@me/activities/statistics/applications.ts index 014df8af..ba359b47 100644 --- a/src/api/routes/users/@me/activities/statistics/applications.ts +++ b/src/api/routes/users/@me/activities/statistics/applications.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/users/@me/affinities/guilds.ts b/src/api/routes/users/@me/affinities/guilds.ts index 8d744744..e733910f 100644 --- a/src/api/routes/users/@me/affinities/guilds.ts +++ b/src/api/routes/users/@me/affinities/guilds.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/users/@me/affinities/users.ts b/src/api/routes/users/@me/affinities/users.ts index 6d4e4991..758bedc3 100644 --- a/src/api/routes/users/@me/affinities/users.ts +++ b/src/api/routes/users/@me/affinities/users.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/users/@me/applications/#app_id/entitlements.ts b/src/api/routes/users/@me/applications/#app_id/entitlements.ts index 411e95bf..8e407184 100644 --- a/src/api/routes/users/@me/applications/#app_id/entitlements.ts +++ b/src/api/routes/users/@me/applications/#app_id/entitlements.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/users/@me/billing/country-code.ts b/src/api/routes/users/@me/billing/country-code.ts index 33d40796..72601f42 100644 --- a/src/api/routes/users/@me/billing/country-code.ts +++ b/src/api/routes/users/@me/billing/country-code.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/users/@me/billing/payment-sources.ts b/src/api/routes/users/@me/billing/payment-sources.ts index 014df8af..ba359b47 100644 --- a/src/api/routes/users/@me/billing/payment-sources.ts +++ b/src/api/routes/users/@me/billing/payment-sources.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/users/@me/billing/subscriptions.ts b/src/api/routes/users/@me/billing/subscriptions.ts index 411e95bf..8e407184 100644 --- a/src/api/routes/users/@me/billing/subscriptions.ts +++ b/src/api/routes/users/@me/billing/subscriptions.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/users/@me/channels.ts b/src/api/routes/users/@me/channels.ts index ad483529..c17275ec 100644 --- a/src/api/routes/users/@me/channels.ts +++ b/src/api/routes/users/@me/channels.ts @@ -1,6 +1,6 @@ -import { Request, Response, Router } from "express"; -import { Recipient, DmChannelDTO, Channel, DmChannelCreateSchema } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Channel, DmChannelCreateSchema, DmChannelDTO, Recipient } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/users/@me/connections.ts b/src/api/routes/users/@me/connections.ts index 411e95bf..8e407184 100644 --- a/src/api/routes/users/@me/connections.ts +++ b/src/api/routes/users/@me/connections.ts @@ -1,5 +1,5 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/users/@me/delete.ts b/src/api/routes/users/@me/delete.ts index 1d81c2b9..dfc6131b 100644 --- a/src/api/routes/users/@me/delete.ts +++ b/src/api/routes/users/@me/delete.ts @@ -1,8 +1,14 @@ -import { Router, Request, Response } from "express"; -import { Guild, Member, User } from "@fosscord/util"; import { route } from "@fosscord/api"; -import bcrypt from "bcrypt"; -import { HTTPError } from "@fosscord/util"; +import { HTTPError, Member, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; + +let bcrypt: any; +try { + bcrypt = require("bcrypt"); +} catch { + bcrypt = require("bcryptjs"); + console.log("Warning: using bcryptjs because bcrypt is not installed! Performance will be affected."); +} const router = Router(); diff --git a/src/api/routes/users/@me/devices.ts b/src/api/routes/users/@me/devices.ts index 8556a3ad..cb01e576 100644 --- a/src/api/routes/users/@me/devices.ts +++ b/src/api/routes/users/@me/devices.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/users/@me/disable.ts b/src/api/routes/users/@me/disable.ts index 4aff3774..05976908 100644 --- a/src/api/routes/users/@me/disable.ts +++ b/src/api/routes/users/@me/disable.ts @@ -1,7 +1,14 @@ -import { User } from "@fosscord/util"; -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; -import bcrypt from "bcrypt"; +import { User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; + +let bcrypt: any; +try { + bcrypt = require("bcrypt"); +} catch { + bcrypt = require("bcryptjs"); + console.log("Warning: using bcryptjs because bcrypt is not installed! Performance will be affected."); +} const router = Router(); diff --git a/src/api/routes/users/@me/email-settings.ts b/src/api/routes/users/@me/email-settings.ts index 3114984e..28d0864a 100644 --- a/src/api/routes/users/@me/email-settings.ts +++ b/src/api/routes/users/@me/email-settings.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/users/@me/entitlements.ts b/src/api/routes/users/@me/entitlements.ts index 341e2b4c..7aaa5d7c 100644 --- a/src/api/routes/users/@me/entitlements.ts +++ b/src/api/routes/users/@me/entitlements.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/users/@me/guilds.ts b/src/api/routes/users/@me/guilds.ts index 4d4fccd4..5141aa3d 100644 --- a/src/api/routes/users/@me/guilds.ts +++ b/src/api/routes/users/@me/guilds.ts @@ -1,7 +1,6 @@ -import { Router, Request, Response } from "express"; -import { Guild, Member, User, GuildDeleteEvent, GuildMemberRemoveEvent, emitEvent, Config } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { Config, emitEvent, Guild, GuildDeleteEvent, GuildMemberRemoveEvent, HTTPError, Member, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/routes/users/@me/guilds/premium/subscription-slots.ts b/src/api/routes/users/@me/guilds/premium/subscription-slots.ts index 014df8af..ba359b47 100644 --- a/src/api/routes/users/@me/guilds/premium/subscription-slots.ts +++ b/src/api/routes/users/@me/guilds/premium/subscription-slots.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts index 7d095451..fcb0a9df 100644 --- a/src/api/routes/users/@me/index.ts +++ b/src/api/routes/users/@me/index.ts @@ -1,8 +1,24 @@ -import { Router, Request, Response } from "express"; -import { User, PrivateUserProjection, emitEvent, UserUpdateEvent, handleFile, FieldErrors, UserModifySchema } from "@fosscord/util"; import { route } from "@fosscord/api"; -import bcrypt from "bcrypt"; -import { OrmUtils, generateToken } from "@fosscord/util"; +import { + emitEvent, + FieldErrors, + generateToken, + handleFile, + OrmUtils, + PrivateUserProjection, + User, + UserModifySchema, + UserUpdateEvent +} from "@fosscord/util"; +import { Request, Response, Router } from "express"; + +let bcrypt: any; +try { + bcrypt = require("bcrypt"); +} catch { + bcrypt = require("bcryptjs"); + console.log("Warning: using bcryptjs because bcrypt is not installed! Performance will be affected."); +} const router: Router = Router(); @@ -37,17 +53,17 @@ router.patch("/", route({ body: "UserModifySchema" }), async (req: Request, res: } user.data.hash = await bcrypt.hash(body.new_password, 12); user.data.valid_tokens_since = new Date(); - token = await generateToken(user.id) as string; + token = (await generateToken(user.id)) as string; } - if(body.username){ - let check_username = body?.username?.replace(/\s/g, ''); - if(!check_username) { - throw FieldErrors({ - username: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") } - }); - } - } + if (body.username) { + let check_username = body?.username?.replace(/\s/g, ""); + if (!check_username) { + throw FieldErrors({ + username: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") } + }); + } + } user = OrmUtils.mergeDeep(user, body); await user.save(); @@ -61,7 +77,7 @@ router.patch("/", route({ body: "UserModifySchema" }), async (req: Request, res: user_id: req.user_id, data: user } as UserUpdateEvent); - + res.json({ ...user, token diff --git a/src/api/routes/users/@me/library.ts b/src/api/routes/users/@me/library.ts index 7ac13bae..0aea02a0 100644 --- a/src/api/routes/users/@me/library.ts +++ b/src/api/routes/users/@me/library.ts @@ -1,5 +1,5 @@ -import { Router, Response, Request } from "express"; import { route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/users/@me/mfa/codes.ts b/src/api/routes/users/@me/mfa/codes.ts index 4224a1c0..c62581cc 100644 --- a/src/api/routes/users/@me/mfa/codes.ts +++ b/src/api/routes/users/@me/mfa/codes.ts @@ -1,7 +1,14 @@ -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; import { BackupCode, Config, FieldErrors, generateMfaBackupCodes, MfaCodesSchema, User } from "@fosscord/util"; -import bcrypt from "bcrypt"; +import { Request, Response, Router } from "express"; + +let bcrypt: any; +try { + bcrypt = require("bcrypt"); +} catch { + bcrypt = require("bcryptjs"); + console.log("Warning: using bcryptjs because bcrypt is not installed! Performance will be affected."); +} const router = Router(); @@ -12,25 +19,21 @@ router.post("/", route({ body: "MfaCodesSchema" }), async (req: Request, res: Re const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["data"] }); - if (!await bcrypt.compare(password, user.data.hash || "")) { + if (!(await bcrypt.compare(password, user.data.hash || ""))) { throw FieldErrors({ password: { message: req.t("auth:login.INVALID_PASSWORD"), code: "INVALID_PASSWORD" } }); } var codes: BackupCode[]; if (regenerate && Config.get().security.twoFactor.generateBackupCodes) { - await BackupCode.update( - { user: { id: req.user_id } }, - { expired: true } - ); + await BackupCode.update({ user: { id: req.user_id } }, { expired: true }); codes = generateMfaBackupCodes(req.user_id); - await Promise.all(codes.map(x => x.save())); - } - else { + await Promise.all(codes.map((x) => x.save())); + } else { codes = await BackupCode.find({ where: { user: { - id: req.user_id, + id: req.user_id }, expired: false } @@ -38,8 +41,8 @@ router.post("/", route({ body: "MfaCodesSchema" }), async (req: Request, res: Re } return res.json({ - backup_codes: codes.map(x => ({ ...x, expired: undefined })), - }) + backup_codes: codes.map((x) => ({ ...x, expired: undefined })) + }); }); export default router; diff --git a/src/api/routes/users/@me/mfa/totp/disable.ts b/src/api/routes/users/@me/mfa/totp/disable.ts index 2fe9355c..6bc9a5c7 100644 --- a/src/api/routes/users/@me/mfa/totp/disable.ts +++ b/src/api/routes/users/@me/mfa/totp/disable.ts @@ -1,8 +1,8 @@ -import { Router, Request, Response } from "express"; import { route } from "@fosscord/api"; -import { verifyToken } from 'node-2fa'; +import { BackupCode, generateToken, TotpDisableSchema, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; -import { User, generateToken, BackupCode, TotpDisableSchema } from "@fosscord/util"; +import { verifyToken } from "node-2fa"; const router = Router(); @@ -14,28 +14,27 @@ router.post("/", route({ body: "TotpDisableSchema" }), async (req: Request, res: const backup = await BackupCode.findOne({ where: { code: body.code } }); if (!backup) { const ret = verifyToken(user.totp_secret!, body.code); - if (!ret || ret.delta != 0) - throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); + if (!ret || ret.delta != 0) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); } await User.update( { id: req.user_id }, { mfa_enabled: false, - totp_secret: "", - }, + totp_secret: "" + } ); await BackupCode.update( { user: { id: req.user_id } }, { - expired: true, + expired: true } ); return res.json({ - token: await generateToken(user.id), + token: await generateToken(user.id) }); }); -export default router; \ No newline at end of file +export default router; diff --git a/src/api/routes/users/@me/mfa/totp/enable.ts b/src/api/routes/users/@me/mfa/totp/enable.ts index ac668d1d..f3a73c28 100644 --- a/src/api/routes/users/@me/mfa/totp/enable.ts +++ b/src/api/routes/users/@me/mfa/totp/enable.ts @@ -1,9 +1,16 @@ -import { Router, Request, Response } from "express"; -import { User, generateToken, BackupCode, generateMfaBackupCodes, Config, TotpEnableSchema } from "@fosscord/util"; import { route } from "@fosscord/api"; -import bcrypt from "bcrypt"; +import { BackupCode, Config, generateMfaBackupCodes, generateToken, TotpEnableSchema, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; -import { verifyToken } from 'node-2fa'; +import { verifyToken } from "node-2fa"; + +let bcrypt: any; +try { + bcrypt = require("bcrypt"); +} catch { + bcrypt = require("bcryptjs"); + console.log("Warning: using bcryptjs because bcrypt is not installed! Performance will be affected."); +} const router = Router(); @@ -14,35 +21,29 @@ router.post("/", route({ body: "TotpEnableSchema" }), async (req: Request, res: // TODO: Are guests allowed to enable 2fa? if (user.data.hash) { - if (!await bcrypt.compare(body.password, user.data.hash)) { + if (!(await bcrypt.compare(body.password, user.data.hash))) { throw new HTTPError(req.t("auth:login.INVALID_PASSWORD")); } } - if (!body.secret) - throw new HTTPError(req.t("auth:login.INVALID_TOTP_SECRET"), 60005); + if (!body.secret) throw new HTTPError(req.t("auth:login.INVALID_TOTP_SECRET"), 60005); - if (!body.code) - throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); + if (!body.code) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); - if (verifyToken(body.secret, body.code)?.delta != 0) - throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); + if (verifyToken(body.secret, body.code)?.delta != 0) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); let backup_codes: BackupCode[] = []; if (Config.get().security.twoFactor.generateBackupCodes) { backup_codes = generateMfaBackupCodes(req.user_id); - await Promise.all(backup_codes.map(x => x.save())); + await Promise.all(backup_codes.map((x) => x.save())); } - await User.update( - { id: req.user_id }, - { mfa_enabled: true, totp_secret: body.secret } - ); + await User.update({ id: req.user_id }, { mfa_enabled: true, totp_secret: body.secret }); res.send({ token: await generateToken(user.id), - backup_codes: backup_codes.map(x => ({ ...x, expired: undefined })), + backup_codes: backup_codes.map((x) => ({ ...x, expired: undefined })) }); }); -export default router; \ No newline at end of file +export default router; diff --git a/src/api/routes/users/@me/notes.ts b/src/api/routes/users/@me/notes.ts index f938f088..fc207401 100644 --- a/src/api/routes/users/@me/notes.ts +++ b/src/api/routes/users/@me/notes.ts @@ -1,6 +1,6 @@ -import { Request, Response, Router } from "express"; import { route } from "@fosscord/api"; -import { User, Note, emitEvent, Snowflake } from "@fosscord/util"; +import { emitEvent, Note, Snowflake, User } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -10,38 +10,31 @@ router.get("/:id", route({}), async (req: Request, res: Response) => { const note = await Note.findOneOrFail({ where: { owner: { id: req.user_id }, - target: { id: id }, + target: { id: id } } }); return res.json({ note: note?.content, note_user_id: id, - user_id: req.user_id, + user_id: req.user_id }); }); router.put("/:id", route({}), async (req: Request, res: Response) => { const { id } = req.params; const owner = await User.findOneOrFail({ where: { id: req.user_id } }); - const target = await User.findOneOrFail({ where: { id: id } }); //if noted user does not exist throw + const target = await User.findOneOrFail({ where: { id: id } }); //if noted user does not exist throw const { note } = req.body; if (note && note.length) { // upsert a note if (await Note.findOne({ where: { owner: { id: owner.id }, target: { id: target.id } } })) { - Note.update( - { owner: { id: owner.id }, target: { id: target.id } }, - { owner, target, content: note } - ); + Note.update({ owner: { id: owner.id }, target: { id: target.id } }, { owner, target, content: note }); + } else { + Note.insert({ id: Snowflake.generate(), owner, target, content: note }); } - else { - Note.insert( - { id: Snowflake.generate(), owner, target, content: note } - ); - } - } - else { + } else { await Note.delete({ owner: { id: owner.id }, target: { id: target.id } }); } @@ -51,7 +44,7 @@ router.put("/:id", route({}), async (req: Request, res: Response) => { note: note, id: target.id }, - user_id: owner.id, + user_id: owner.id }); return res.status(204); diff --git a/src/api/routes/users/@me/relationships.ts b/src/api/routes/users/@me/relationships.ts index f7464b99..8267c142 100644 --- a/src/api/routes/users/@me/relationships.ts +++ b/src/api/routes/users/@me/relationships.ts @@ -1,18 +1,18 @@ +import { route } from "@fosscord/api"; import { - RelationshipAddEvent, - User, - PublicUserProjection, - RelationshipType, - RelationshipRemoveEvent, + Config, + DiscordApiErrors, emitEvent, + HTTPError, + OrmUtils, + PublicUserProjection, Relationship, - Config + RelationshipAddEvent, + RelationshipRemoveEvent, + RelationshipType, + User } from "@fosscord/util"; -import { Router, Response, Request } from "express"; -import { HTTPError } from "@fosscord/util"; -import { DiscordApiErrors } from "@fosscord/util"; -import { route } from "@fosscord/api"; -import { OrmUtils } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); @@ -42,7 +42,11 @@ router.put("/:id", route({ body: "RelationshipPutSchema" }), async (req: Request return await updateRelationship( req, res, - await User.findOneOrFail({ where: { id: req.params.id }, relations: ["relationships", "relationships.to"], select: userProjection }), + await User.findOneOrFail({ + where: { id: req.params.id }, + relations: ["relationships", "relationships.to"], + select: userProjection + }), req.body.type ?? RelationshipType.friends ); }); @@ -132,7 +136,9 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ relationship.type = RelationshipType.blocked; await relationship.save(); } else { - relationship = await (OrmUtils.mergeDeep(new Relationship(), { to_id: id, type: RelationshipType.blocked, from_id: req.user_id }) as Relationship).save(); + relationship = await ( + OrmUtils.mergeDeep(new Relationship(), { to_id: id, type: RelationshipType.blocked, from_id: req.user_id }) as Relationship + ).save(); } if (friendRequest && friendRequest.type !== RelationshipType.blocked) { @@ -158,7 +164,12 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ const { maxFriends } = Config.get().limits.user; if (user.relationships.length >= maxFriends) throw DiscordApiErrors.MAXIMUM_FRIENDS.withParams(maxFriends); - let incoming_relationship = OrmUtils.mergeDeep(new Relationship(), { nickname: undefined, type: RelationshipType.incoming, to: user, from: friend }); + let incoming_relationship = OrmUtils.mergeDeep(new Relationship(), { + nickname: undefined, + type: RelationshipType.incoming, + to: user, + from: friend + }); let outgoing_relationship = OrmUtils.mergeDeep(new Relationship(), { nickname: undefined, type: RelationshipType.outgoing, diff --git a/src/api/routes/users/@me/settings.ts b/src/api/routes/users/@me/settings.ts index 7578d36e..e276a22a 100644 --- a/src/api/routes/users/@me/settings.ts +++ b/src/api/routes/users/@me/settings.ts @@ -1,6 +1,6 @@ -import { Router, Response, Request } from "express"; -import { User, UserSettings } from "@fosscord/util"; import { route } from "@fosscord/api"; +import { User, UserSettings } from "@fosscord/util"; +import { Request, Response, Router } from "express"; const router = Router(); diff --git a/src/api/routes/voice/regions.ts b/src/api/routes/voice/regions.ts index 4de304ee..eacdcf11 100644 --- a/src/api/routes/voice/regions.ts +++ b/src/api/routes/voice/regions.ts @@ -1,6 +1,5 @@ -import { Router, Request, Response } from "express"; -import { getIpAdress, route } from "@fosscord/api"; -import { getVoiceRegions } from "@fosscord/api"; +import { getIpAdress, getVoiceRegions, route } from "@fosscord/api"; +import { Request, Response, Router } from "express"; const router: Router = Router(); diff --git a/src/api/start.ts b/src/api/start.ts index 9ba198e7..c407484d 100644 --- a/src/api/start.ts +++ b/src/api/start.ts @@ -1,16 +1,16 @@ process.on("uncaughtException", console.error); process.on("unhandledRejection", console.error); -import { config } from "dotenv"; -config(); -import { FosscordServer } from "./Server"; import cluster from "cluster"; +import { config } from "dotenv"; import os from "os"; +import { FosscordServer } from "./Server"; +config(); let cores = 1; try { cores = Number(process.env.THREADS) || os.cpus().length; } catch { - console.log("[API] Failed to get thread count! Using 1...") + console.log("[API] Failed to get thread count! Using 1..."); } if (cluster.isMaster && process.env.NODE_ENV == "production") { diff --git a/src/api/util/entities/AssetCacheItem.ts b/src/api/util/entities/AssetCacheItem.ts index 160dece6..958d5a61 100644 --- a/src/api/util/entities/AssetCacheItem.ts +++ b/src/api/util/entities/AssetCacheItem.ts @@ -1,3 +1,3 @@ export class AssetCacheItem { constructor(public Key: string, public FilePath: string = "", public Headers: any = null as any) {} -} \ No newline at end of file +} diff --git a/src/api/util/handlers/Instance.ts b/src/api/util/handlers/Instance.ts index 7c337270..e03c9488 100644 --- a/src/api/util/handlers/Instance.ts +++ b/src/api/util/handlers/Instance.ts @@ -1,5 +1,4 @@ import { Config, Guild, Session } from "@fosscord/util"; -import { createQueryBuilder } from "typeorm"; export async function initInstance() { // TODO: clean up database and delete tombstone data @@ -10,7 +9,7 @@ export async function initInstance() { const { autoJoin } = Config.get().guild; if (autoJoin.enabled && !autoJoin.guilds?.length) { - let guild = await Guild.findOne({where: {}, order: {id: "ASC"}}); + let guild = await Guild.findOne({ where: {}, order: { id: "ASC" } }); if (guild) { // @ts-ignore await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } }); diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index ff5ece75..d760d27c 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -1,32 +1,31 @@ import { + Application, + Attachment, Channel, + CHANNEL_MENTION, + Config, Embed, emitEvent, + EVERYONE_MENTION, + getPermission, + getRights, Guild, + HERE_MENTION, + HTTPError, Message, MessageCreateEvent, + MessageCreateSchema, + MessageType, MessageUpdateEvent, - getPermission, - getRights, - CHANNEL_MENTION, - Snowflake, - USER_MENTION, - ROLE_MENTION, + OrmUtils, Role, - EVERYONE_MENTION, - HERE_MENTION, - MessageType, + ROLE_MENTION, User, - Application, - Webhook, - Attachment, - Config, - MessageCreateSchema, + USER_MENTION, + Webhook } from "@fosscord/util"; -import { HTTPError } from "@fosscord/util"; -import fetch from "node-fetch"; import cheerio from "cheerio"; -import { OrmUtils } from "@fosscord/util"; +import fetch from "node-fetch"; const allow_empty = false; // TODO: check webhook, application, system author, stickers @@ -61,21 +60,21 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { }); if (message.content && message.content.length > Config.get().limits.message.maxCharacters) { - throw new HTTPError("Content length over max character limit") + throw new HTTPError("Content length over max character limit"); } if (opts.author_id) { message.author = await User.getPublicUser(opts.author_id); const rights = await getRights(opts.author_id); rights.hasThrow("SEND_MESSAGES"); - } + } if (opts.application_id) { message.application = await Application.findOneOrFail({ where: { id: opts.application_id } }); } if (opts.webhook_id) { message.webhook = await Webhook.findOneOrFail({ where: { id: opts.webhook_id } }); } - + const permission = await getPermission(opts.author_id, channel.guild_id, opts.channel_id); permission.hasThrow("SEND_MESSAGES"); if (permission.cache.member) { @@ -89,8 +88,10 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { if (message.guild_id !== null) { const guild = await Guild.findOneOrFail({ where: { id: channel.guild_id } }); if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) { - if (opts.message_reference.guild_id !== channel.guild_id) throw new HTTPError("You can only reference messages from this guild"); - if (opts.message_reference.channel_id !== opts.channel_id) throw new HTTPError("You can only reference messages from this channel"); + if (opts.message_reference.guild_id !== channel.guild_id) + throw new HTTPError("You can only reference messages from this guild"); + if (opts.message_reference.channel_id !== opts.channel_id) + throw new HTTPError("You can only reference messages from this channel"); } } /** Q: should be checked if the referenced message exists? ANSWER: NO @@ -100,7 +101,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { } // TODO: stickers/activity - if (!allow_empty && (!opts.content && !opts.embeds?.length && !opts.attachments?.length && !opts.sticker_ids?.length)) { + if (!allow_empty && !opts.content && !opts.embeds?.length && !opts.attachments?.length && !opts.sticker_ids?.length) { throw new HTTPError("Empty messages are not allowed", 50006); } @@ -110,7 +111,8 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { let mention_user_ids = [] as string[]; let mention_everyone = false; - if (content) { // TODO: explicit-only mentions + if (content) { + // TODO: explicit-only mentions message.content = content.trim(); for (const [_, mention] of content.matchAll(CHANNEL_MENTION)) { if (!mention_channel_ids.includes(mention)) mention_channel_ids.push(mention); @@ -158,7 +160,7 @@ export async function postHandleMessage(message: Message) { try { const request = await fetch(link, { ...DEFAULT_FETCH_OPTIONS, - size: Config.get().limits.message.maxEmbedDownloadSize, + size: Config.get().limits.message.maxEmbedDownloadSize }); const text = await request.text(); diff --git a/src/api/util/handlers/route.ts b/src/api/util/handlers/route.ts index 71e14955..d43ae103 100644 --- a/src/api/util/handlers/route.ts +++ b/src/api/util/handlers/route.ts @@ -1,8 +1,6 @@ import { DiscordApiErrors, EVENT, - Event, - EventData, FieldErrors, FosscordApiErrors, getPermission, @@ -12,14 +10,14 @@ import { RightResolvable, Rights } from "@fosscord/util"; +import Ajv from "ajv"; +import addFormats from "ajv-formats"; +import { AnyValidateFunction } from "ajv/dist/core"; import { NextFunction, Request, Response } from "express"; import fs from "fs"; import path from "path"; -import Ajv from "ajv"; -import { AnyValidateFunction } from "ajv/dist/core"; -import addFormats from "ajv-formats"; -const SchemaPath = path.join(__dirname, "..", "..", "..","..", "assets", "schemas.json"); +const SchemaPath = path.join(__dirname, "..", "..", "..", "..", "assets", "schemas.json"); const schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" })); export const ajv = new Ajv({ @@ -117,10 +115,10 @@ export function route(opts: RouteOptions) { const valid = validate(normalizeBody(req.body)); if (!valid) { const fields: Record<string, { code?: string; message: string }> = {}; - if(process.env.LOG_INVALID_BODY) { - console.log(`Got invalid request: ${req.method} ${req.originalUrl}`) - console.log(req.body) - validate.errors?.forEach(x => console.log(x.params)) + if (process.env.LOG_INVALID_BODY) { + console.log(`Got invalid request: ${req.method} ${req.originalUrl}`); + console.log(req.body); + validate.errors?.forEach((x) => console.log(x.params)); } validate.errors?.forEach((x) => (fields[x.instancePath.slice(1)] = { code: x.keyword, message: x.message || "" })); throw FieldErrors(fields); diff --git a/src/api/util/index.ts b/src/api/util/index.ts index b3c7559f..31e75325 100644 --- a/src/api/util/index.ts +++ b/src/api/util/index.ts @@ -6,4 +6,5 @@ export * from "./utility/Base64"; export * from "./utility/ipAddress"; export * from "./utility/passwordStrength"; export * from "./utility/RandomInviteID"; -export * from "./utility/String"; \ No newline at end of file +export * from "./utility/String"; +export * from "./utility/captcha"; \ No newline at end of file diff --git a/src/api/util/utility/RandomInviteID.ts b/src/api/util/utility/RandomInviteID.ts index 7ea344e0..feebfd3d 100644 --- a/src/api/util/utility/RandomInviteID.ts +++ b/src/api/util/utility/RandomInviteID.ts @@ -22,11 +22,10 @@ export function snowflakeBasedInvite() { // snowflakes hold ~10.75 characters worth of entropy; // safe to generate a 8-char invite out of them let str = ""; - for (let i=0; i < 10; i++) { - + for (let i = 0; i < 10; i++) { str.concat(chars.charAt(Number(snowflake % base))); snowflake = snowflake / base; } - - return str.substr(3,8).split("").reverse().join(""); + + return str.substr(3, 8).split("").reverse().join(""); } diff --git a/src/api/util/utility/String.ts b/src/api/util/utility/String.ts index 982b7e11..a2e491e4 100644 --- a/src/api/util/utility/String.ts +++ b/src/api/util/utility/String.ts @@ -1,6 +1,6 @@ +import { FieldErrors } from "@fosscord/util"; import { Request } from "express"; import { ntob } from "./Base64"; -import { FieldErrors } from "@fosscord/util"; export function checkLength(str: string, min: number, max: number, key: string, req: Request) { if (str.length < min || str.length > max) { diff --git a/src/api/util/utility/captcha.ts b/src/api/util/utility/captcha.ts new file mode 100644 index 00000000..739647d2 --- /dev/null +++ b/src/api/util/utility/captcha.ts @@ -0,0 +1,46 @@ +import { Config } from "@fosscord/util"; +import fetch from "node-fetch"; + +export interface hcaptchaResponse { + success: boolean; + challenge_ts: string; + hostname: string; + credit: boolean; + "error-codes": string[]; + score: number; // enterprise only + score_reason: string[]; // enterprise only +} + +export interface recaptchaResponse { + success: boolean; + score: number; // between 0 - 1 + action: string; + challenge_ts: string; + hostname: string; + "error-codes"?: string[]; +} + +const verifyEndpoints = { + hcaptcha: "https://hcaptcha.com/siteverify", + recaptcha: "https://www.google.com/recaptcha/api/siteverify", +} + +export async function verifyCaptcha(response: string, ip?: string) { + const { security } = Config.get(); + const { service, secret, sitekey } = security.captcha; + + if (!service) throw new Error("Cannot verify captcha without service"); + + const res = await fetch(verifyEndpoints[service], { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: `response=${encodeURIComponent(response)}` + + `&secret=${encodeURIComponent(secret!)}` + + `&sitekey=${encodeURIComponent(sitekey!)}` + + (ip ? `&remoteip=${encodeURIComponent(ip!)}` : ""), + }); + + return await res.json() as hcaptchaResponse | recaptchaResponse; +} \ No newline at end of file diff --git a/src/api/util/utility/ipAddress.ts b/src/api/util/utility/ipAddress.ts index 8d986b26..c96feb9e 100644 --- a/src/api/util/utility/ipAddress.ts +++ b/src/api/util/utility/ipAddress.ts @@ -78,7 +78,11 @@ export function isProxy(data: typeof exampleData) { export function getIpAdress(req: Request): string { // @ts-ignore - return req.headers[Config.get().security.forwadedFor] || req.socket.remoteAddress; + return ( + req.headers[Config.get().security.forwadedFor as string] || + req.headers[Config.get().security.forwadedFor?.toLowerCase() as string] || + req.socket.remoteAddress + ); } export function distanceBetweenLocations(loc1: any, loc2: any): number { diff --git a/src/api/util/utility/passwordStrength.ts b/src/api/util/utility/passwordStrength.ts index 8eca63b8..ff83d3df 100644 --- a/src/api/util/utility/passwordStrength.ts +++ b/src/api/util/utility/passwordStrength.ts @@ -44,16 +44,16 @@ export function checkPassword(password: string): number { if (password.length == password.count(reNUMBER) || password.length === password.count(reUPPERCASELETTER)) { strength = 0; } - + let entropyMap: { [key: string]: number } = {}; for (let i = 0; i < password.length; i++) { if (entropyMap[password[i]]) entropyMap[password[i]]++; else entropyMap[password[i]] = 1; } - + let entropies = Object.values(entropyMap); - - entropies.map(x => (x / entropyMap.length)); - strength += entropies.reduceRight((a: number, x: number) => a - (x * Math.log2(x))) / Math.log2(password.length); + + entropies.map((x) => x / entropyMap.length); + strength += entropies.reduceRight((a: number, x: number) => a - x * Math.log2(x)) / Math.log2(password.length); return strength; } |