diff options
author | Emma [it/its]@Rory& <root@rory.gay> | 2023-12-11 01:12:54 +0100 |
---|---|---|
committer | Emma [it/its]@Rory& <root@rory.gay> | 2023-12-11 01:12:54 +0100 |
commit | 0a8ceb9e6349284e75545a01ffad608b020f78e2 (patch) | |
tree | 17a9163f963eddabf9168b0b630096b2f7535b64 /src | |
parent | Prettier: use editorconfig (diff) | |
download | server-dev/emma-refactors.tar.xz |
Actually run prettier dev/emma-refactors
Diffstat (limited to '')
240 files changed, 2153 insertions, 4543 deletions
diff --git a/src/api/Server.ts b/src/api/Server.ts index 472ab1d6..b99c9721 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -41,13 +41,7 @@ import { initRateLimits } from "./middlewares/RateLimit"; import { initTranslation } from "./middlewares/Translation"; import { initInstance } from "./util/handlers/Instance"; -const PUBLIC_ASSETS_FOLDER = path.join( - __dirname, - "..", - "..", - "assets", - "public", -); +const PUBLIC_ASSETS_FOLDER = path.join(__dirname, "..", "..", "assets", "public"); export type SpacebarServerOptions = ServerOptions; @@ -84,16 +78,11 @@ export class SpacebarServer extends Server { this.app.use( morgan("combined", { skip: (req, res) => { - let skip = !( - process.env["LOG_REQUESTS"]?.includes( - res.statusCode.toString(), - ) ?? false - ); - if (process.env["LOG_REQUESTS"]?.charAt(0) == "-") - skip = !skip; + let skip = !(process.env["LOG_REQUESTS"]?.includes(res.statusCode.toString()) ?? false); + if (process.env["LOG_REQUESTS"]?.charAt(0) == "-") skip = !skip; return skip; }, - }), + }) ); } @@ -112,10 +101,7 @@ export class SpacebarServer extends Server { await initRateLimits(api); await initTranslation(api); - this.routes = await registerRoutes( - this, - path.join(__dirname, "routes", "/"), - ); + this.routes = await registerRoutes(this, path.join(__dirname, "routes", "/")); // 404 is not an error in express, so this should not be an error middleware // this is a fine place to put the 404 handler because its after we register the routes @@ -137,9 +123,7 @@ export class SpacebarServer extends Server { app.use("/api/v9", api); app.use("/api", api); // allow unversioned requests - app.get("/", (req, res) => - res.sendFile(path.join(PUBLIC_ASSETS_FOLDER, "index.html")), - ); + app.get("/", (req, res) => res.sendFile(path.join(PUBLIC_ASSETS_FOLDER, "index.html"))); this.app.use(ErrorHandler); @@ -150,8 +134,8 @@ export class SpacebarServer extends Server { if (logRequests) console.log( red( - `Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!`, - ), + `Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!` + ) ); return super.start(); diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts index 9e41b453..8451d69e 100644 --- a/src/api/middlewares/Authentication.ts +++ b/src/api/middlewares/Authentication.ts @@ -71,11 +71,7 @@ declare global { } } -export async function Authentication( - req: Request, - res: Response, - next: NextFunction, -) { +export async function Authentication(req: Request, res: Response, next: NextFunction) { if (req.method === "OPTIONS") return res.sendStatus(204); const url = req.url.replace(API_PREFIX, ""); if (url.startsWith("/invites") && req.method === "GET") return next(); @@ -86,8 +82,7 @@ export async function Authentication( }) ) return next(); - if (!req.headers.authorization) - return next(new HTTPError("Missing Authorization Header", 401)); + if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401)); Sentry.setUser({ id: req.user_id }); diff --git a/src/api/middlewares/BodyParser.ts b/src/api/middlewares/BodyParser.ts index ac8e0432..31d79a51 100644 --- a/src/api/middlewares/BodyParser.ts +++ b/src/api/middlewares/BodyParser.ts @@ -24,8 +24,7 @@ export function BodyParser(opts?: OptionsJson) { const jsonParser = bodyParser.json(opts); return (req: Request, res: Response, next: NextFunction) => { - if (!req.headers["content-type"]) - req.headers["content-type"] = "application/json"; + if (!req.headers["content-type"]) req.headers["content-type"] = "application/json"; jsonParser(req, res, (err) => { if (err) { diff --git a/src/api/middlewares/CORS.ts b/src/api/middlewares/CORS.ts index 3e7452fc..8c9b1627 100644 --- a/src/api/middlewares/CORS.ts +++ b/src/api/middlewares/CORS.ts @@ -25,16 +25,10 @@ export function CORS(req: Request, res: Response, next: NextFunction) { // TODO: use better CSP res.set( "Content-security-policy", - "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';", - ); - res.set( - "Access-Control-Allow-Headers", - req.header("Access-Control-Request-Headers") || "*", - ); - res.set( - "Access-Control-Allow-Methods", - req.header("Access-Control-Request-Methods") || "*", + "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';" ); + res.set("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers") || "*"); + res.set("Access-Control-Allow-Methods", req.header("Access-Control-Request-Methods") || "*"); next(); } diff --git a/src/api/middlewares/ErrorHandler.ts b/src/api/middlewares/ErrorHandler.ts index c417e64f..4d61b745 100644 --- a/src/api/middlewares/ErrorHandler.ts +++ b/src/api/middlewares/ErrorHandler.ts @@ -21,12 +21,7 @@ import { HTTPError } from "lambert-server"; import { ApiError, FieldError } from "@spacebar/util"; const EntityNotFoundErrorRegex = /"(\w+)"/; -export function ErrorHandler( - error: Error & { type?: string }, - req: Request, - res: Response, - next: NextFunction, -) { +export function ErrorHandler(error: Error & { type?: string }, req: Request, res: Response, next: NextFunction) { if (!error) return next(); try { @@ -35,16 +30,13 @@ export function ErrorHandler( let message = error?.toString(); let errors = undefined; - if (error instanceof HTTPError && error.code) - code = httpcode = error.code; + if (error instanceof HTTPError && error.code) code = httpcode = error.code; else if (error instanceof ApiError) { code = error.code; message = error.message; httpcode = error.httpStatus; } else if (error.name === "EntityNotFoundError") { - message = `${ - error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item" - } could not be found`; + message = `${error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item"} could not be found`; code = httpcode = 404; } else if (error instanceof FieldError) { code = Number(error.code); @@ -56,12 +48,7 @@ export function ErrorHandler( code = 50109; message = "The request body contains invalid JSON."; } else { - console.error( - `[Error] ${code} ${req.url}\n`, - errors || error, - "\nbody:", - req.body, - ); + console.error(`[Error] ${code} ${req.url}\n`, errors || error, "\nbody:", req.body); if (req.server?.options?.production) { // don't expose internal errors to the user, instead human errors should be thrown as HTTPError @@ -75,8 +62,6 @@ export function ErrorHandler( res.status(httpcode).json({ code: code, message, errors }); } catch (error) { console.error(`[Internal Server Error] 500`, error); - return res - .status(500) - .json({ code: 500, message: "Internal Server Error" }); + return res.status(500).json({ code: 500, message: "Internal Server Error" }); } } diff --git a/src/api/middlewares/RateLimit.ts b/src/api/middlewares/RateLimit.ts index f5bfbb4f..14811a42 100644 --- a/src/api/middlewares/RateLimit.ts +++ b/src/api/middlewares/RateLimit.ts @@ -65,21 +65,14 @@ export default function rateLimit(opts: { if (rights.has("BYPASS_RATE_LIMITS")) return next(); } - const bucket_id = - opts.bucket || - req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, ""); + const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, ""); let executor_id = getIpAdress(req); if (!opts.onlyIp && req.user_id) executor_id = req.user_id; let max_hits = opts.count; if (opts.bot && req.user_bot) max_hits = opts.bot; - if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) - max_hits = opts.GET; - else if ( - opts.MODIFY && - ["POST", "DELETE", "PATCH", "PUT"].includes(req.method) - ) - max_hits = opts.MODIFY; + if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) max_hits = opts.GET; + else if (opts.MODIFY && ["POST", "DELETE", "PATCH", "PUT"].includes(req.method)) max_hits = opts.MODIFY; const offender = Cache.get(executor_id + bucket_id); @@ -104,18 +97,13 @@ export default function rateLimit(opts: { } res.set("X-RateLimit-Reset", `${reset}`); - res.set( - "X-RateLimit-Reset-After", - `${Math.max(0, Math.ceil(resetAfterSec))}`, - ); + res.set("X-RateLimit-Reset-After", `${Math.max(0, Math.ceil(resetAfterSec))}`); if (offender.blocked) { const global = bucket_id === "global"; // each block violation pushes the expiry one full window further reset += opts.window * 1000; - offender.expires_at = new Date( - offender.expires_at.getTime() + opts.window * 1000, - ); + offender.expires_at = new Date(offender.expires_at.getTime() + opts.window * 1000); resetAfterMs = reset - Date.now(); resetAfterSec = Math.ceil(resetAfterMs / 1000); @@ -129,10 +117,7 @@ export default function rateLimit(opts: { res .status(429) .set("X-RateLimit-Remaining", "0") - .set( - "Retry-After", - `${Math.max(0, Math.ceil(resetAfterSec))}`, - ) + .set("Retry-After", `${Math.max(0, Math.ceil(resetAfterSec))}`) // TODO: error rate limit message translation .send({ message: "You are being rate limited.", @@ -156,11 +141,7 @@ export default function rateLimit(opts: { // check if error and increment error rate limit if (res.statusCode >= 400 && opts.error) { return hitRoute(hitRouteOpts); - } else if ( - res.statusCode >= 200 && - res.statusCode < 300 && - opts.success - ) { + } else if (res.statusCode >= 200 && res.statusCode < 300 && opts.success) { return hitRoute(hitRouteOpts); } }); @@ -198,7 +179,7 @@ export async function initRateLimits(app: Router) { bucket: "global", onlyIp: true, ...ip, - }), + }) ); app.use(rateLimit({ bucket: "global", ...global })); app.use( @@ -207,24 +188,16 @@ export async function initRateLimits(app: Router) { error: true, onlyIp: true, ...error, - }), + }) ); app.use("/guilds/:id", rateLimit(routes.guild)); app.use("/webhooks/:id", rateLimit(routes.webhook)); app.use("/channels/:id", rateLimit(routes.channel)); app.use("/auth/login", rateLimit(routes.auth.login)); - app.use( - "/auth/register", - rateLimit({ onlyIp: true, success: true, ...routes.auth.register }), - ); + 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/Translation.ts b/src/api/middlewares/Translation.ts index f3a4c8df..e089407e 100644 --- a/src/api/middlewares/Translation.ts +++ b/src/api/middlewares/Translation.ts @@ -27,12 +27,8 @@ const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "assets"); export async function initTranslation(router: Router) { const languages = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "locales")); - const namespaces = fs.readdirSync( - path.join(ASSET_FOLDER_PATH, "locales", "en"), - ); - const ns = namespaces - .filter((x) => x.endsWith(".json")) - .map((x) => x.slice(0, x.length - 5)); + const namespaces = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "locales", "en")); + const ns = namespaces.filter((x) => x.endsWith(".json")).map((x) => x.slice(0, x.length - 5)); await i18next .use(i18nextBackend) @@ -43,9 +39,7 @@ export async function initTranslation(router: Router) { fallbackLng: "en", ns, backend: { - loadPath: - path.join(ASSET_FOLDER_PATH, "locales") + - "/{{lng}}/{{ns}}.json", + loadPath: path.join(ASSET_FOLDER_PATH, "locales") + "/{{lng}}/{{ns}}.json", }, load: "all", }); diff --git a/src/api/routes/applications/#id/bot/index.ts b/src/api/routes/applications/#id/bot/index.ts index 3c431e3d..5d58e6ed 100644 --- a/src/api/routes/applications/#id/bot/index.ts +++ b/src/api/routes/applications/#id/bot/index.ts @@ -50,15 +50,14 @@ router.post( relations: ["owner"], }); - if (app.owner.id != req.user_id) - throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; + if (app.owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; const user = await createAppBotUser(app, req); res.send({ token: await generateToken(user.id), }).status(204); - }, + } ); router.post( @@ -77,13 +76,9 @@ router.post( const bot = await User.findOneOrFail({ where: { id: req.params.id } }); const owner = await User.findOneOrFail({ where: { id: req.user_id } }); - if (owner.id != req.user_id) - throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; + if (owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; - if ( - owner.totp_secret && - (!req.body.code || verifyToken(owner.totp_secret, req.body.code)) - ) + 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() }; @@ -93,7 +88,7 @@ router.post( const token = await generateToken(bot.id); res.json({ token }).status(200); - }, + } ); router.patch( @@ -120,14 +115,9 @@ router.patch( if (!app.bot) throw DiscordApiErrors.BOT_ONLY_ENDPOINT; - if (app.owner.id != req.user_id) - throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; + if (app.owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; - if (body.avatar) - body.avatar = await handleFile( - `/avatars/${app.id}`, - body.avatar as string, - ); + if (body.avatar) body.avatar = await handleFile(`/avatars/${app.id}`, body.avatar as string); app.bot.assign(body); @@ -135,7 +125,7 @@ router.patch( await app.save(); res.json(app).status(200); - }, + } ); export default router; diff --git a/src/api/routes/applications/#id/entitlements.ts b/src/api/routes/applications/#id/entitlements.ts index 6388e6b3..4ad0b60f 100644 --- a/src/api/routes/applications/#id/entitlements.ts +++ b/src/api/routes/applications/#id/entitlements.ts @@ -34,7 +34,7 @@ router.get( // TODO: //const { exclude_consumed } = req.query; res.status(200).send([]); - }, + } ); export default router; diff --git a/src/api/routes/applications/#id/index.ts b/src/api/routes/applications/#id/index.ts index c372869a..9b8c2d46 100644 --- a/src/api/routes/applications/#id/index.ts +++ b/src/api/routes/applications/#id/index.ts @@ -17,11 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Application, - ApplicationModifySchema, - DiscordApiErrors, -} from "@spacebar/util"; +import { Application, ApplicationModifySchema, DiscordApiErrors } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; import { verifyToken } from "node-2fa"; @@ -45,11 +41,10 @@ router.get( where: { id: req.params.id }, relations: ["owner", "bot"], }); - if (app.owner.id != req.user_id) - throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; + if (app.owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; return res.json(app); - }, + } ); router.patch( @@ -73,14 +68,9 @@ router.patch( relations: ["owner", "bot"], }); - if (app.owner.id != req.user_id) - throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; + if (app.owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; - if ( - app.owner.totp_secret && - (!req.body.code || - verifyToken(app.owner.totp_secret, req.body.code)) - ) + if (app.owner.totp_secret && (!req.body.code || verifyToken(app.owner.totp_secret, req.body.code))) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); if (app.bot) { @@ -93,7 +83,7 @@ router.patch( await app.save(); return res.json(app); - }, + } ); router.post( @@ -111,20 +101,15 @@ router.post( where: { id: req.params.id }, relations: ["bot", "owner"], }); - if (app.owner.id != req.user_id) - throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; - - if ( - app.owner.totp_secret && - (!req.body.code || - verifyToken(app.owner.totp_secret, req.body.code)) - ) + if (app.owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION; + + if (app.owner.totp_secret && (!req.body.code || verifyToken(app.owner.totp_secret, req.body.code))) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); await Application.delete({ id: app.id }); res.send().status(200); - }, + } ); export default router; diff --git a/src/api/routes/applications/#id/skus.ts b/src/api/routes/applications/#id/skus.ts index dc4fad23..b98bb6c4 100644 --- a/src/api/routes/applications/#id/skus.ts +++ b/src/api/routes/applications/#id/skus.ts @@ -32,7 +32,7 @@ router.get( }), async (req: Request, res: Response) => { res.json([]).status(200); - }, + } ); export default router; diff --git a/src/api/routes/applications/detectable.ts b/src/api/routes/applications/detectable.ts index 5cf9d171..e80d0313 100644 --- a/src/api/routes/applications/detectable.ts +++ b/src/api/routes/applications/detectable.ts @@ -33,7 +33,7 @@ router.get( async (req: Request, res: Response) => { //TODO res.send([]).status(200); - }, + } ); export default router; diff --git a/src/api/routes/applications/index.ts b/src/api/routes/applications/index.ts index 5bba3338..6eeadbc3 100644 --- a/src/api/routes/applications/index.ts +++ b/src/api/routes/applications/index.ts @@ -17,14 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Application, - ApplicationCreateSchema, - Config, - User, - createAppBotUser, - trimSpecial, -} from "@spacebar/util"; +import { Application, ApplicationCreateSchema, Config, User, createAppBotUser, trimSpecial } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -44,7 +37,7 @@ router.get( relations: ["owner", "bot"], }); res.json(results).status(200); - }, + } ); router.post( @@ -77,7 +70,7 @@ router.post( } else await app.save(); res.json(app); - }, + } ); export default router; diff --git a/src/api/routes/auth/forgot.ts b/src/api/routes/auth/forgot.ts index 6fa86021..a17a2936 100644 --- a/src/api/routes/auth/forgot.ts +++ b/src/api/routes/auth/forgot.ts @@ -17,13 +17,7 @@ */ import { getIpAdress, route, verifyCaptcha } from "@spacebar/api"; -import { - Config, - Email, - FieldErrors, - ForgotPasswordSchema, - User, -} from "@spacebar/util"; +import { Config, Email, FieldErrors, ForgotPasswordSchema, User } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; const router = Router(); @@ -47,10 +41,7 @@ router.post( const config = Config.get(); - if ( - config.passwordReset.requireCaptcha && - config.security.captcha.enabled - ) { + if (config.passwordReset.requireCaptcha && config.security.captcha.enabled) { const { sitekey, service } = config.security.captcha; if (!captcha_key) { return res.status(400).json({ @@ -87,8 +78,7 @@ router.post( if (!user.email) throw FieldErrors({ login: { - message: - "This account does not have an email address associated with it.", + message: "This account does not have an email address associated with it.", code: "NO_EMAIL", }, }); @@ -110,12 +100,10 @@ router.post( return res.sendStatus(204); }) .catch((e) => { - console.error( - `Failed to send password reset email to ${user.username}#${user.discriminator}: ${e}`, - ); + console.error(`Failed to send password reset email to ${user.username}#${user.discriminator}: ${e}`); throw new HTTPError("Failed to send password reset email", 500); }); - }, + } ); export default router; diff --git a/src/api/routes/auth/generate-registration-tokens.ts b/src/api/routes/auth/generate-registration-tokens.ts index 80fdaed1..e13e26f1 100644 --- a/src/api/routes/auth/generate-registration-tokens.ts +++ b/src/api/routes/auth/generate-registration-tokens.ts @@ -29,13 +29,11 @@ router.get( query: { count: { type: "number", - description: - "The number of registration tokens to generate. Defaults to 1.", + description: "The number of registration tokens to generate. Defaults to 1.", }, length: { type: "number", - description: - "The length of each registration token. Defaults to 255.", + description: "The length of each registration token. Defaults to 255.", }, }, right: "OPERATOR", @@ -43,18 +41,14 @@ router.get( }), async (req: Request, res: Response) => { const count = req.query.count ? parseInt(req.query.count as string) : 1; - const length = req.query.length - ? parseInt(req.query.length as string) - : 255; + const length = req.query.length ? parseInt(req.query.length as string) : 255; const tokens: ValidRegistrationToken[] = []; for (let i = 0; i < count; i++) { const token = ValidRegistrationToken.create({ token: random(length), - expires_at: - Date.now() + - Config.get().security.defaultRegistrationTokenExpiration, + expires_at: Date.now() + Config.get().security.defaultRegistrationTokenExpiration, }); tokens.push(token); } @@ -67,16 +61,11 @@ router.get( }); const ret = req.query.include_url - ? tokens.map( - (x) => - `${Config.get().general.frontPage}/register?token=${ - x.token - }`, - ) + ? tokens.map((x) => `${Config.get().general.frontPage}/register?token=${x.token}`) : tokens.map((x) => x.token); if (req.query.plain) return res.send(ret.join("\n")); return res.json({ tokens: ret }); - }, + } ); diff --git a/src/api/routes/auth/location-metadata.ts b/src/api/routes/auth/location-metadata.ts index 28293e59..f1625702 100644 --- a/src/api/routes/auth/location-metadata.ts +++ b/src/api/routes/auth/location-metadata.ts @@ -38,7 +38,7 @@ router.get( 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 a2100333..5563597f 100644 --- a/src/api/routes/auth/login.ts +++ b/src/api/routes/auth/login.ts @@ -47,8 +47,7 @@ router.post( }, }), async (req: Request, res: Response) => { - const { login, password, captcha_key, undelete } = - req.body as LoginSchema; + const { login, password, captcha_key, undelete } = req.body as LoginSchema; const config = Config.get(); @@ -101,10 +100,7 @@ router.post( }); // the salt is saved in the password refer to bcrypt docs - const same_password = await bcrypt.compare( - password, - user.data.hash || "", - ); + const same_password = await bcrypt.compare(password, user.data.hash || ""); if (!same_password) { throw FieldErrors({ login: { @@ -123,8 +119,7 @@ router.post( throw FieldErrors({ login: { code: "ACCOUNT_LOGIN_VERIFICATION_EMAIL", - message: - "Email verification is required, please check your email.", + message: "Email verification is required, please check your email.", }, }); } @@ -153,9 +148,7 @@ router.post( const challenge = JSON.stringify({ publicKey: { ...options, - challenge: Buffer.from(options.challenge).toString( - "base64", - ), + challenge: Buffer.from(options.challenge).toString("base64"), allowCredentials: user.security_keys.map((x) => ({ id: x.key_id, type: "public-key", @@ -179,10 +172,8 @@ router.post( if (undelete) { // undelete refers to un'disable' here - if (user.disabled) - await User.update({ id: user.id }, { disabled: false }); - if (user.deleted) - await User.update({ id: user.id }, { deleted: false }); + if (user.disabled) await User.update({ id: user.id }, { disabled: false }); + if (user.deleted) await User.update({ id: user.id }, { deleted: false }); } else { if (user.deleted) return res.status(400).json({ @@ -203,7 +194,7 @@ router.post( // https://user-images.githubusercontent.com/6506416/81051916-dd8c9900-8ec2-11ea-8794-daf12d6f31f0.png res.json({ token, settings: { ...user.settings, index: undefined } }); - }, + } ); /** diff --git a/src/api/routes/auth/logout.ts b/src/api/routes/auth/logout.ts index 94a3e474..9686f5cb 100644 --- a/src/api/routes/auth/logout.ts +++ b/src/api/routes/auth/logout.ts @@ -31,16 +31,12 @@ router.post( }), async (req: Request, res: Response) => { if (req.body.provider != null || req.body.voip_provider != null) { - console.log( - `[LOGOUT]: provider or voip provider not null!`, - req.body, - ); + console.log(`[LOGOUT]: provider or voip provider not null!`, req.body); } else { delete req.body.provider; delete req.body.voip_provider; - if (Object.keys(req.body).length != 0) - console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body); + if (Object.keys(req.body).length != 0) console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body); } res.status(204).send(); - }, + } ); diff --git a/src/api/routes/auth/mfa/totp.ts b/src/api/routes/auth/mfa/totp.ts index 4df408f9..956993b3 100644 --- a/src/api/routes/auth/mfa/totp.ts +++ b/src/api/routes/auth/mfa/totp.ts @@ -59,11 +59,7 @@ router.post( 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, - ); + if (!ret || ret.delta != 0) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); } else { backup.consumed = true; await backup.save(); @@ -75,7 +71,7 @@ router.post( token: await generateToken(user.id), settings: { ...user.settings, index: undefined }, }); - }, + } ); export default router; diff --git a/src/api/routes/auth/mfa/webauthn.ts b/src/api/routes/auth/mfa/webauthn.ts index b58d2944..c1a7ddb9 100644 --- a/src/api/routes/auth/mfa/webauthn.ts +++ b/src/api/routes/auth/mfa/webauthn.ts @@ -17,14 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - generateToken, - SecurityKey, - User, - verifyWebAuthnToken, - WebAuthn, - WebAuthnTotpSchema, -} from "@spacebar/util"; +import { generateToken, SecurityKey, User, verifyWebAuthnToken, WebAuthn, WebAuthnTotpSchema } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { ExpectedAssertionResult } from "fido2-lib"; import { HTTPError } from "lambert-server"; @@ -65,46 +58,33 @@ router.post( }); const ret = await verifyWebAuthnToken(ticket); - if (!ret) - throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); + if (!ret) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); await User.update({ id: user.id }, { totp_last_ticket: "" }); const clientAttestationResponse = JSON.parse(code); - if (!clientAttestationResponse.rawId) - throw new HTTPError("Missing rawId", 400); + if (!clientAttestationResponse.rawId) throw new HTTPError("Missing rawId", 400); - clientAttestationResponse.rawId = toArrayBuffer( - Buffer.from(clientAttestationResponse.rawId, "base64url"), - ); + clientAttestationResponse.rawId = toArrayBuffer(Buffer.from(clientAttestationResponse.rawId, "base64url")); const securityKey = await SecurityKey.findOneOrFail({ where: { - key_id: Buffer.from( - clientAttestationResponse.rawId, - "base64url", - ).toString("base64"), + key_id: Buffer.from(clientAttestationResponse.rawId, "base64url").toString("base64"), }, }); const assertionExpectations: ExpectedAssertionResult = JSON.parse( - Buffer.from( - clientAttestationResponse.response.clientDataJSON, - "base64", - ).toString(), + Buffer.from(clientAttestationResponse.response.clientDataJSON, "base64").toString() ); - const authnResult = await WebAuthn.fido2.assertionResult( - clientAttestationResponse, - { - ...assertionExpectations, - factor: "second", - publicKey: securityKey.public_key, - prevCounter: securityKey.counter, - userHandle: securityKey.key_id, - }, - ); + const authnResult = await WebAuthn.fido2.assertionResult(clientAttestationResponse, { + ...assertionExpectations, + factor: "second", + publicKey: securityKey.public_key, + prevCounter: securityKey.counter, + userHandle: securityKey.key_id, + }); const counter = authnResult.authnrData.get("counter"); @@ -116,7 +96,7 @@ router.post( token: await generateToken(user.id), user_settings: user.settings, }); - }, + } ); export default router; diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts index de1cbd3d..dc72a3ca 100644 --- a/src/api/routes/auth/register.ts +++ b/src/api/routes/auth/register.ts @@ -16,13 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - IPAnalysis, - getIpAdress, - isProxy, - route, - verifyCaptcha, -} from "@spacebar/api"; +import { IPAnalysis, getIpAdress, isProxy, route, verifyCaptcha } from "@spacebar/api"; import { Config, FieldErrors, @@ -65,13 +59,9 @@ router.post( }); await regToken.remove(); regTokenUsed = true; - console.log( - `[REGISTER] Registration token ${token} used for registration!`, - ); + console.log(`[REGISTER] Registration token ${token} used for registration!`); } else { - console.log( - `[REGISTER] Invalid registration token ${token} used for registration by ${ip}!`, - ); + console.log(`[REGISTER] Invalid registration token ${token} used for registration by ${ip}!`); } } @@ -104,11 +94,7 @@ router.post( }); } - if ( - !regTokenUsed && - register.requireCaptcha && - security.captcha.enabled - ) { + if (!regTokenUsed && register.requireCaptcha && security.captcha.enabled) { const { sitekey, service } = security.captcha; if (!body.captcha_key) { return res?.status(400).json({ @@ -139,9 +125,7 @@ router.post( throw FieldErrors({ email: { code: "EMAIL_ALREADY_REGISTERED", - message: req.t( - "auth:register.EMAIL_ALREADY_REGISTERED", - ), + message: req.t("auth:register.EMAIL_ALREADY_REGISTERED"), }, }); } @@ -176,9 +160,7 @@ router.post( throw FieldErrors({ email: { code: "EMAIL_ALREADY_REGISTERED", - message: req.t( - "auth:register.EMAIL_ALREADY_REGISTERED", - ), + message: req.t("auth:register.EMAIL_ALREADY_REGISTERED"), }, }); } @@ -198,14 +180,9 @@ router.post( message: req.t("common:field.BASE_TYPE_REQUIRED"), }, }); - } else if ( - register.dateOfBirth.required && - register.dateOfBirth.minimum - ) { + } else if (register.dateOfBirth.required && register.dateOfBirth.minimum) { const minimum = new Date(); - minimum.setFullYear( - minimum.getFullYear() - register.dateOfBirth.minimum, - ); + minimum.setFullYear(minimum.getFullYear() - register.dateOfBirth.minimum); body.date_of_birth = new Date(body.date_of_birth as Date); // higher is younger @@ -228,10 +205,7 @@ router.post( throw FieldErrors({ password: { code: "PASSWORD_REQUIREMENTS_MIN_LENGTH", - message: req.t( - "auth:register.PASSWORD_REQUIREMENTS_MIN_LENGTH", - { min: min }, - ), + message: req.t("auth:register.PASSWORD_REQUIREMENTS_MIN_LENGTH", { min: min }), }, }); } @@ -249,8 +223,7 @@ router.post( if ( !regTokenUsed && !body.invite && - (register.requireInvite || - (register.guestsRequireInvite && !register.email)) + (register.requireInvite || (register.guestsRequireInvite && !register.email)) ) { // require invite to register -> e.g. for organizations to send invites to their employees throw FieldErrors({ @@ -266,18 +239,14 @@ router.post( limits.absoluteRate.register.enabled && (await User.count({ where: { - created_at: MoreThan( - new Date( - Date.now() - limits.absoluteRate.register.window, - ), - ), + created_at: MoreThan(new Date(Date.now() - limits.absoluteRate.register.window)), }, })) >= limits.absoluteRate.register.limit ) { console.log( - `Global register ratelimit exceeded for ${getIpAdress(req)}, ${ - req.body.username - }, ${req.body.invite || "No invite given"}`, + `Global register ratelimit exceeded for ${getIpAdress(req)}, ${req.body.username}, ${ + req.body.invite || "No invite given" + }` ); throw FieldErrors({ email: { @@ -295,7 +264,7 @@ router.post( } return res.json({ token: await generateToken(user.id) }); - }, + } ); export default router; diff --git a/src/api/routes/auth/reset.ts b/src/api/routes/auth/reset.ts index b3ca1e9e..746363cc 100644 --- a/src/api/routes/auth/reset.ts +++ b/src/api/routes/auth/reset.ts @@ -17,14 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - checkToken, - Email, - FieldErrors, - generateToken, - PasswordResetSchema, - User, -} from "@spacebar/util"; +import { checkToken, Email, FieldErrors, generateToken, PasswordResetSchema, User } from "@spacebar/util"; import bcrypt from "bcrypt"; import { Request, Response, Router } from "express"; @@ -76,7 +69,7 @@ router.post( await Email.sendPasswordChanged(user, user.email!); res.json({ token: await generateToken(user.id) }); - }, + } ); export default router; diff --git a/src/api/routes/auth/verify/index.ts b/src/api/routes/auth/verify/index.ts index 49f74277..49f3398e 100644 --- a/src/api/routes/auth/verify/index.ts +++ b/src/api/routes/auth/verify/index.ts @@ -17,13 +17,7 @@ */ import { getIpAdress, route, verifyCaptcha } from "@spacebar/api"; -import { - checkToken, - Config, - FieldErrors, - generateToken, - User, -} from "@spacebar/util"; +import { checkToken, Config, FieldErrors, generateToken, User } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); @@ -97,7 +91,7 @@ router.post( await User.update({ id: user.id }, { verified: true }); return res.json(await getToken(user)); - }, + } ); export default router; diff --git a/src/api/routes/auth/verify/resend.ts b/src/api/routes/auth/verify/resend.ts index 701f0ea8..ced7c578 100644 --- a/src/api/routes/auth/verify/resend.ts +++ b/src/api/routes/auth/verify/resend.ts @@ -52,12 +52,10 @@ router.post( return res.sendStatus(204); }) .catch((e) => { - console.error( - `Failed to send verification email to ${user.username}#${user.discriminator}: ${e}`, - ); + console.error(`Failed to send verification email to ${user.username}#${user.discriminator}: ${e}`); throw new HTTPError("Failed to send verification email", 500); }); - }, + } ); export default router; diff --git a/src/api/routes/auth/verify/view-backup-codes-challenge.ts b/src/api/routes/auth/verify/view-backup-codes-challenge.ts index 5407de82..59c98f00 100644 --- a/src/api/routes/auth/verify/view-backup-codes-challenge.ts +++ b/src/api/routes/auth/verify/view-backup-codes-challenge.ts @@ -52,7 +52,7 @@ router.post( nonce: "NoncePlaceholder", regenerate_nonce: "RegenNoncePlaceholder", }); - }, + } ); export default router; diff --git a/src/api/routes/channels/#channel_id/index.ts b/src/api/routes/channels/#channel_id/index.ts index 567c7c92..a5fe1ef9 100644 --- a/src/api/routes/channels/#channel_id/index.ts +++ b/src/api/routes/channels/#channel_id/index.ts @@ -52,7 +52,7 @@ router.get( }); return res.send(channel); - }, + } ); router.delete( @@ -101,7 +101,7 @@ router.delete( } res.send(channel); - }, + } ); router.patch( @@ -122,11 +122,7 @@ router.patch( async (req: Request, res: Response) => { const payload = req.body as ChannelModifySchema; const { channel_id } = req.params; - if (payload.icon) - payload.icon = await handleFile( - `/channel-icons/${channel_id}`, - payload.icon, - ); + if (payload.icon) payload.icon = await handleFile(`/channel-icons/${channel_id}`, payload.icon); const channel = await Channel.findOneOrFail({ where: { id: channel_id }, @@ -143,7 +139,7 @@ router.patch( ]); res.send(channel); - }, + } ); export default router; diff --git a/src/api/routes/channels/#channel_id/invites.ts b/src/api/routes/channels/#channel_id/invites.ts index ae32e80d..af0e5cc9 100644 --- a/src/api/routes/channels/#channel_id/invites.ts +++ b/src/api/routes/channels/#channel_id/invites.ts @@ -65,9 +65,7 @@ router.post( const { guild_id } = channel; const expires_at = - body.max_age == 0 || body.max_age == undefined - ? undefined - : new Date(body.max_age * 1000 + Date.now()); + body.max_age == 0 || body.max_age == undefined ? undefined : new Date(body.max_age * 1000 + Date.now()); const invite = await Invite.create({ code: random(), @@ -95,7 +93,7 @@ router.post( } as InviteCreateEvent); res.status(201).send(data); - }, + } ); router.get( @@ -126,7 +124,7 @@ router.get( }); res.status(200).send(invites); - }, + } ); export default router; 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 a6dcae6b..58ca9d26 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 @@ -17,12 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - emitEvent, - getPermission, - MessageAckEvent, - ReadState, -} from "@spacebar/util"; +import { emitEvent, getPermission, MessageAckEvent, ReadState } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); @@ -43,18 +38,13 @@ router.post( async (req: Request, res: Response) => { const { channel_id, message_id } = req.params; - const permission = await getPermission( - req.user_id, - undefined, - channel_id, - ); + const permission = await getPermission(req.user_id, undefined, channel_id); permission.hasThrow("VIEW_CHANNEL"); let read_state = await ReadState.findOne({ where: { user_id: req.user_id, channel_id }, }); - if (!read_state) - read_state = ReadState.create({ user_id: req.user_id, channel_id }); + if (!read_state) read_state = ReadState.create({ user_id: req.user_id, channel_id }); read_state.last_message_id = message_id; await read_state.save(); @@ -70,7 +60,7 @@ router.post( } as MessageAckEvent); res.json({ token: null }); - }, + } ); export default 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 5ca645c0..7529f508 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 @@ -57,7 +57,7 @@ router.post( flags: 1, components: [], }).status(200); - }, + } ); export default 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 c4d2e1e8..82dc5b87 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 @@ -75,11 +75,7 @@ router.patch( relations: ["attachments"], }); - const permissions = await getPermission( - req.user_id, - undefined, - channel_id, - ); + const permissions = await getPermission(req.user_id, undefined, channel_id); const rights = await getRights(req.user_id); @@ -139,7 +135,7 @@ router.patch( // these are not in the Discord.com response mention_channels: new_message.mention_channels, }); - }, + } ); // Backfill message with specific timestamp @@ -196,13 +192,8 @@ router.put( if (req.file) { try { - const file = await uploadFile( - `/attachments/${req.params.channel_id}`, - req.file, - ); - attachments.push( - Attachment.create({ ...file, proxy_url: file.url }), - ); + const file = await uploadFile(`/attachments/${req.params.channel_id}`, req.file); + attachments.push(Attachment.create({ ...file, proxy_url: file.url })); } catch (error) { return res.status(400).json(error); } @@ -241,12 +232,10 @@ router.put( ]); // no await as it shouldnt block the message send function and silently catch error - postHandleMessage(message).catch((e) => - console.error("[Message] post-message handler failed", e), - ); + postHandleMessage(message).catch((e) => console.error("[Message] post-message handler failed", e)); return res.json(message); - }, + } ); router.get( @@ -272,17 +261,12 @@ router.get( relations: ["attachments"], }); - const permissions = await getPermission( - req.user_id, - undefined, - channel_id, - ); + const permissions = await getPermission(req.user_id, undefined, channel_id); - if (message.author_id !== req.user_id) - permissions.hasThrow("READ_MESSAGE_HISTORY"); + if (message.author_id !== req.user_id) permissions.hasThrow("READ_MESSAGE_HISTORY"); return res.json(message); - }, + } ); router.delete( @@ -310,11 +294,7 @@ router.delete( if (message.author_id !== req.user_id) { if (!rights.has("MANAGE_MESSAGES")) { - const permission = await getPermission( - req.user_id, - channel.guild_id, - channel_id, - ); + const permission = await getPermission(req.user_id, channel.guild_id, channel_id); permission.hasThrow("MANAGE_MESSAGES"); } } else rights.hasThrow("SELF_DELETE_MESSAGES"); @@ -332,7 +312,7 @@ router.delete( } as MessageDeleteEvent); res.sendStatus(204); - }, + } ); export default router; 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 c2222b0a..cea8197b 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 @@ -88,7 +88,7 @@ router.delete( } as MessageReactionRemoveAllEvent); res.sendStatus(204); - }, + } ); router.delete( @@ -113,9 +113,7 @@ router.delete( }); const already_added = message.reactions.find( - (x) => - (x.emoji.id === emoji.id && emoji.id) || - x.emoji.name === emoji.name, + (x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name ); if (!already_added) throw new HTTPError("Reaction not found", 404); message.reactions.remove(already_added); @@ -135,7 +133,7 @@ router.delete( ]); res.sendStatus(204); - }, + } ); router.get( @@ -161,9 +159,7 @@ router.get( where: { id: message_id, channel_id }, }); const reaction = message.reactions.find( - (x) => - (x.emoji.id === emoji.id && emoji.id) || - x.emoji.name === emoji.name, + (x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name ); if (!reaction) throw new HTTPError("Reaction not found", 404); @@ -175,7 +171,7 @@ router.get( }); res.json(users); - }, + } ); router.put( @@ -204,9 +200,7 @@ router.put( 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, + (x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name ); if (!already_added) req.permission?.hasThrow("ADD_REACTIONS"); @@ -222,8 +216,7 @@ router.put( } 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 + 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++; already_added.user_ids.push(req.user_id); } else @@ -258,7 +251,7 @@ router.put( } as MessageReactionAddEvent); res.sendStatus(204); - }, + } ); router.delete( @@ -288,30 +281,19 @@ router.delete( if (user_id === "@me") user_id = req.user_id; else { - const permissions = await getPermission( - req.user_id, - undefined, - channel_id, - ); + const permissions = await getPermission(req.user_id, undefined, channel_id); permissions.hasThrow("MANAGE_MESSAGES"); } const already_added = message.reactions.find( - (x) => - (x.emoji.id === emoji.id && emoji.id) || - x.emoji.name === emoji.name, + (x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name ); - if (!already_added || !already_added.user_ids.includes(user_id)) - throw new HTTPError("Reaction not found", 404); + if (!already_added || !already_added.user_ids.includes(user_id)) throw new HTTPError("Reaction not found", 404); already_added.count--; if (already_added.count <= 0) message.reactions.remove(already_added); - else - already_added.user_ids.splice( - already_added.user_ids.indexOf(user_id), - 1, - ); + else already_added.user_ids.splice(already_added.user_ids.indexOf(user_id), 1); await message.save(); @@ -328,7 +310,7 @@ router.delete( } as MessageReactionRemoveEvent); res.sendStatus(204); - }, + } ); export default router; 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 9b607d59..3cd90faf 100644 --- a/src/api/routes/channels/#channel_id/messages/bulk-delete.ts +++ b/src/api/routes/channels/#channel_id/messages/bulk-delete.ts @@ -17,15 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Channel, - Config, - emitEvent, - getPermission, - getRights, - Message, - MessageDeleteBulkEvent, -} from "@spacebar/util"; +import { Channel, Config, emitEvent, getPermission, getRights, Message, MessageDeleteBulkEvent } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; @@ -54,30 +46,22 @@ router.post( const channel = await Channel.findOneOrFail({ where: { id: channel_id }, }); - if (!channel.guild_id) - throw new HTTPError("Can't bulk delete dm channel messages", 400); + if (!channel.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400); const rights = await getRights(req.user_id); rights.hasThrow("SELF_DELETE_MESSAGES"); const superuser = rights.has("MANAGE_MESSAGES"); - const permission = await getPermission( - req.user_id, - channel?.guild_id, - channel_id, - ); + const permission = await getPermission(req.user_id, channel?.guild_id, channel_id); const { maxBulkDelete } = Config.get().limits.message; const { messages } = req.body as { messages: string[] }; - if (messages.length === 0) - throw new HTTPError("You must specify messages to bulk delete"); + if (messages.length === 0) throw new HTTPError("You must specify messages to bulk delete"); if (!superuser) { permission.hasThrow("MANAGE_MESSAGES"); if (messages.length > maxBulkDelete) - throw new HTTPError( - `You cannot delete more than ${maxBulkDelete} messages`, - ); + throw new HTTPError(`You cannot delete more than ${maxBulkDelete} messages`); } await Message.delete(messages); @@ -89,5 +73,5 @@ router.post( } as MessageDeleteBulkEvent); res.sendStatus(204); - }, + } ); diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index a5bfcfd7..c80b5f48 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts @@ -40,13 +40,7 @@ import { import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; import multer from "multer"; -import { - FindManyOptions, - FindOperator, - LessThan, - MoreThan, - MoreThanOrEqual, -} from "typeorm"; +import { FindManyOptions, FindOperator, LessThan, MoreThan, MoreThanOrEqual } from "typeorm"; import { URL } from "url"; const router: Router = Router(); @@ -68,8 +62,7 @@ router.get( }, limit: { type: "number", - description: - "max number of messages to return (1-100). defaults to 50", + description: "max number of messages to return (1-100). defaults to 50", }, }, responses: { @@ -95,14 +88,9 @@ router.get( const before = req.query.before ? `${req.query.before}` : undefined; const after = req.query.after ? `${req.query.after}` : undefined; const limit = Number(req.query.limit) || 50; - if (limit < 1 || limit > 100) - throw new HTTPError("limit must be between 1 and 100", 422); + if (limit < 1 || limit > 100) throw new HTTPError("limit must be between 1 and 100", 422); - const permissions = await getPermission( - req.user_id, - channel.guild_id, - channel_id, - ); + const permissions = await getPermission(req.user_id, channel.guild_id, channel_id); permissions.hasThrow("VIEW_CHANNEL"); if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]); @@ -148,12 +136,10 @@ router.get( } } else { if (after) { - if (BigInt(after) > BigInt(Snowflake.generate())) - return res.status(422); + if (BigInt(after) > BigInt(Snowflake.generate())) return res.status(422); query.where.id = MoreThan(after); } else if (before) { - if (BigInt(before) > BigInt(Snowflake.generate())) - return res.status(422); + if (BigInt(before) > BigInt(Snowflake.generate())) return res.status(422); query.where.id = LessThan(before); } @@ -180,12 +166,8 @@ router.get( }); x.attachments?.forEach((y: Attachment) => { // dynamically set attachment proxy_url in case the endpoint changed - 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 - }`; + 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}`; }); /** @@ -202,7 +184,7 @@ router.get( }); return res.json(ret); - }, + } ); // TODO: config max upload size @@ -258,10 +240,7 @@ router.post( 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); } if (body.nonce) { @@ -283,12 +262,7 @@ router.post( const count = await Message.count({ where: { channel_id, - timestamp: MoreThan( - new Date( - Date.now() - - limits.absoluteRate.sendMessage.window, - ), - ), + timestamp: MoreThan(new Date(Date.now() - limits.absoluteRate.sendMessage.window)), }, }); @@ -305,13 +279,8 @@ router.post( const files = (req.files as Express.Multer.File[]) ?? []; for (const currFile of files) { try { - const file = await uploadFile( - `/attachments/${channel.id}`, - currFile, - ); - attachments.push( - Attachment.create({ ...file, proxy_url: file.url }), - ); + const file = await uploadFile(`/attachments/${channel.id}`, currFile); + attachments.push(Attachment.create({ ...file, proxy_url: file.url })); } catch (error) { return res.status(400).json({ message: error?.toString() }); } @@ -347,14 +316,12 @@ router.post( recipient.save(), emitEvent({ event: "CHANNEL_CREATE", - data: channel_dto.excludedRecipients([ - recipient.user_id, - ]), + data: channel_dto.excludedRecipients([recipient.user_id]), user_id: recipient.user_id, }), ]); } - }) || [], + }) || [] ); } @@ -370,16 +337,13 @@ router.post( // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore - message.member.roles = message.member.roles - .filter((x) => x.id != x.guild_id) - .map((x) => x.id); + message.member.roles = message.member.roles.filter((x) => x.id != x.guild_id).map((x) => x.id); } let read_state = await ReadState.findOne({ where: { user_id: req.user_id, channel_id }, }); - if (!read_state) - read_state = ReadState.create({ user_id: req.user_id, channel_id }); + if (!read_state) read_state = ReadState.create({ user_id: req.user_id, channel_id }); read_state.last_message_id = message.id; await Promise.all([ @@ -391,21 +355,16 @@ router.post( data: message, } as MessageCreateEvent), message.guild_id - ? Member.update( - { id: req.user_id, guild_id: message.guild_id }, - { last_message_id: message.id }, - ) + ? Member.update({ id: req.user_id, guild_id: message.guild_id }, { last_message_id: message.id }) : null, channel.save(), ]); // no await as it shouldnt block the message send function and silently catch error - postHandleMessage(message).catch((e) => - console.error("[Message] post-message handler failed", e), - ); + postHandleMessage(message).catch((e) => console.error("[Message] post-message handler failed", e)); return res.json(message); - }, + } ); export default router; diff --git a/src/api/routes/channels/#channel_id/permissions.ts b/src/api/routes/channels/#channel_id/permissions.ts index d3edb0fa..a9c28c0c 100644 --- a/src/api/routes/channels/#channel_id/permissions.ts +++ b/src/api/routes/channels/#channel_id/permissions.ts @@ -55,15 +55,14 @@ router.put( if (!channel.guild_id) throw new HTTPError("Channel not found", 404); if (body.type === 0) { - if (!(await Role.count({ where: { id: overwrite_id } }))) - throw new HTTPError("role not found", 404); + if (!(await Role.count({ where: { id: overwrite_id } }))) throw new HTTPError("role not found", 404); } else if (body.type === 1) { - if (!(await Member.count({ where: { id: overwrite_id } }))) - throw new HTTPError("user not found", 404); + if (!(await Member.count({ where: { id: overwrite_id } }))) throw new HTTPError("user not found", 404); } else throw new HTTPError("type not supported", 501); - let overwrite: ChannelPermissionOverwrite | undefined = - channel.permission_overwrites?.find((x) => x.id === overwrite_id); + let overwrite: ChannelPermissionOverwrite | undefined = channel.permission_overwrites?.find( + (x) => x.id === overwrite_id + ); if (!overwrite) { overwrite = { id: overwrite_id, @@ -73,14 +72,8 @@ router.put( }; channel.permission_overwrites?.push(overwrite); } - overwrite.allow = String( - (req.permission?.bitfield || 0n) & - (BigInt(body.allow) || BigInt("0")), - ); - overwrite.deny = String( - (req.permission?.bitfield || 0n) & - (BigInt(body.deny) || BigInt("0")), - ); + overwrite.allow = String((req.permission?.bitfield || 0n) & (BigInt(body.allow) || BigInt("0"))); + overwrite.deny = String((req.permission?.bitfield || 0n) & (BigInt(body.deny) || BigInt("0"))); await Promise.all([ channel.save(), @@ -92,7 +85,7 @@ router.put( ]); return res.sendStatus(204); - }, + } ); // TODO: check permission hierarchy @@ -107,9 +100,7 @@ router.delete( }); if (!channel.guild_id) throw new HTTPError("Channel not found", 404); - channel.permission_overwrites = channel.permission_overwrites?.filter( - (x) => x.id === overwrite_id, - ); + channel.permission_overwrites = channel.permission_overwrites?.filter((x) => x.id === overwrite_id); await Promise.all([ channel.save(), @@ -121,7 +112,7 @@ router.delete( ]); return res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/channels/#channel_id/pins.ts b/src/api/routes/channels/#channel_id/pins.ts index 724ebffd..597d735c 100644 --- a/src/api/routes/channels/#channel_id/pins.ts +++ b/src/api/routes/channels/#channel_id/pins.ts @@ -57,8 +57,7 @@ router.put( where: { channel: { id: channel_id }, pinned: true }, }); const { maxPins } = Config.get().limits.channel; - if (pinned_count >= maxPins) - throw DiscordApiErrors.MAXIMUM_PINS.withParams(maxPins); + if (pinned_count >= maxPins) throw DiscordApiErrors.MAXIMUM_PINS.withParams(maxPins); await Promise.all([ Message.update({ id: message_id }, { pinned: true }), @@ -79,7 +78,7 @@ router.put( ]); res.sendStatus(204); - }, + } ); router.delete( @@ -129,7 +128,7 @@ router.delete( ]); res.sendStatus(204); - }, + } ); router.get( @@ -153,7 +152,7 @@ router.get( }); res.send(pins); - }, + } ); export default router; diff --git a/src/api/routes/channels/#channel_id/purge.ts b/src/api/routes/channels/#channel_id/purge.ts index 012fec1c..c473f9aa 100644 --- a/src/api/routes/channels/#channel_id/purge.ts +++ b/src/api/routes/channels/#channel_id/purge.ts @@ -57,17 +57,12 @@ router.post( where: { id: channel_id }, }); - if (!channel.guild_id) - throw new HTTPError("Can't purge dm channels", 400); + if (!channel.guild_id) throw new HTTPError("Can't purge dm channels", 400); isTextChannel(channel.type); const rights = await getRights(req.user_id); if (!rights.has("MANAGE_MESSAGES")) { - const permissions = await getPermission( - req.user_id, - channel.guild_id, - channel_id, - ); + const permissions = await getPermission(req.user_id, channel.guild_id, channel_id); permissions.hasThrow("MANAGE_MESSAGES"); permissions.hasThrow("MANAGE_CHANNELS"); } @@ -84,9 +79,7 @@ router.post( where: { channel_id, id: Between(after, before), // the right way around - author_id: rights.has("SELF_DELETE_MESSAGES") - ? undefined - : Not(req.user_id), + author_id: rights.has("SELF_DELETE_MESSAGES") ? undefined : Not(req.user_id), // if you lack the right of self-deletion, you can't delete your own messages, even in purges }, relations: [ @@ -121,5 +114,5 @@ router.post( } as MessageDeleteBulkEvent); res.sendStatus(204); - }, + } ); diff --git a/src/api/routes/channels/#channel_id/recipients.ts b/src/api/routes/channels/#channel_id/recipients.ts index 569bb5cd..c1377269 100644 --- a/src/api/routes/channels/#channel_id/recipients.ts +++ b/src/api/routes/channels/#channel_id/recipients.ts @@ -48,24 +48,16 @@ router.put( }); if (channel.type !== ChannelType.GROUP_DM) { - const recipients = [ - ...(channel.recipients?.map((r) => r.user_id) || []), - user_id, - ].unique(); + const recipients = [...(channel.recipients?.map((r) => r.user_id) || []), user_id].unique(); - const new_channel = await Channel.createDMChannel( - recipients, - req.user_id, - ); + const new_channel = await Channel.createDMChannel(recipients, req.user_id); return res.status(201).json(new_channel); } else { if (channel.recipients?.map((r) => r.user_id).includes(user_id)) { throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error? } - channel.recipients?.push( - Recipient.create({ channel_id: channel_id, user_id: user_id }), - ); + channel.recipients?.push(Recipient.create({ channel_id: channel_id, user_id: user_id })); await channel.save(); await emitEvent({ @@ -87,7 +79,7 @@ router.put( } as ChannelRecipientAddEvent); return res.sendStatus(204); } - }, + } ); router.delete( @@ -104,12 +96,7 @@ router.delete( where: { id: channel_id }, relations: ["recipients"], }); - if ( - !( - channel.type === ChannelType.GROUP_DM && - (channel.owner_id === req.user_id || user_id === req.user_id) - ) - ) + if (!(channel.type === ChannelType.GROUP_DM && (channel.owner_id === req.user_id || user_id === req.user_id))) throw DiscordApiErrors.MISSING_PERMISSIONS; if (!channel.recipients?.map((r) => r.user_id).includes(user_id)) { @@ -119,7 +106,7 @@ router.delete( await Channel.removeRecipientFromChannel(channel, user_id); return res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/channels/#channel_id/typing.ts b/src/api/routes/channels/#channel_id/typing.ts index 90e78175..4563b689 100644 --- a/src/api/routes/channels/#channel_id/typing.ts +++ b/src/api/routes/channels/#channel_id/typing.ts @@ -64,7 +64,7 @@ router.post( } as TypingStartEvent); res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/channels/#channel_id/webhooks.ts b/src/api/routes/channels/#channel_id/webhooks.ts index d54756a1..864fe834 100644 --- a/src/api/routes/channels/#channel_id/webhooks.ts +++ b/src/api/routes/channels/#channel_id/webhooks.ts @@ -47,7 +47,7 @@ router.get( }), async (req: Request, res: Response) => { res.json([]); - }, + } ); // TODO: use Image Data Type for avatar instead of String @@ -77,8 +77,7 @@ router.post( const webhook_count = await Webhook.count({ where: { channel_id } }); const { maxWebhooks } = Config.get().limits.channel; - if (maxWebhooks && webhook_count > maxWebhooks) - throw DiscordApiErrors.MAXIMUM_WEBHOOKS.withParams(maxWebhooks); + if (maxWebhooks && webhook_count > maxWebhooks) throw DiscordApiErrors.MAXIMUM_WEBHOOKS.withParams(maxWebhooks); let { avatar, name } = req.body as WebhookCreateSchema; name = trimSpecial(name); @@ -105,7 +104,7 @@ router.post( ...hook, user: user, }); - }, + } ); export default router; diff --git a/src/api/routes/connections/#connection_name/authorize.ts b/src/api/routes/connections/#connection_name/authorize.ts index b43f46d7..c3a435cd 100644 --- a/src/api/routes/connections/#connection_name/authorize.ts +++ b/src/api/routes/connections/#connection_name/authorize.ts @@ -30,9 +30,7 @@ router.get("/", route({}), async (req: Request, res: Response) => { provider_id: { code: "BASE_TYPE_CHOICES", message: req.t("common:field.BASE_TYPE_CHOICES", { - types: Array.from(ConnectionStore.connections.keys()).join( - ", ", - ), + types: Array.from(ConnectionStore.connections.keys()).join(", "), }), }, }); diff --git a/src/api/routes/connections/#connection_name/callback.ts b/src/api/routes/connections/#connection_name/callback.ts index ee0db94a..f47c48f3 100644 --- a/src/api/routes/connections/#connection_name/callback.ts +++ b/src/api/routes/connections/#connection_name/callback.ts @@ -17,55 +17,44 @@ */ import { route } from "@spacebar/api"; -import { - ConnectionCallbackSchema, - ConnectionStore, - emitEvent, - FieldErrors, -} from "@spacebar/util"; +import { ConnectionCallbackSchema, ConnectionStore, emitEvent, FieldErrors } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); -router.post( - "/", - route({ requestBody: "ConnectionCallbackSchema" }), - async (req: Request, res: Response) => { - const { connection_name } = req.params; - const connection = ConnectionStore.connections.get(connection_name); - if (!connection) - throw FieldErrors({ - provider_id: { - code: "BASE_TYPE_CHOICES", - message: req.t("common:field.BASE_TYPE_CHOICES", { - types: Array.from( - ConnectionStore.connections.keys(), - ).join(", "), - }), - }, - }); - - if (!connection.settings.enabled) - throw FieldErrors({ - provider_id: { - message: "This connection has been disabled server-side.", - }, - }); - - const body = req.body as ConnectionCallbackSchema; - const userId = connection.getUserId(body.state); - const connectedAccnt = await connection.handleCallback(body); - - // whether we should emit a connections update event, only used when a connection doesnt already exist - if (connectedAccnt) - emitEvent({ - event: "USER_CONNECTIONS_UPDATE", - data: { ...connectedAccnt, token_data: undefined }, - user_id: userId, - }); - - res.sendStatus(204); - }, -); +router.post("/", route({ requestBody: "ConnectionCallbackSchema" }), async (req: Request, res: Response) => { + const { connection_name } = req.params; + const connection = ConnectionStore.connections.get(connection_name); + if (!connection) + throw FieldErrors({ + provider_id: { + code: "BASE_TYPE_CHOICES", + message: req.t("common:field.BASE_TYPE_CHOICES", { + types: Array.from(ConnectionStore.connections.keys()).join(", "), + }), + }, + }); + + if (!connection.settings.enabled) + throw FieldErrors({ + provider_id: { + message: "This connection has been disabled server-side.", + }, + }); + + const body = req.body as ConnectionCallbackSchema; + const userId = connection.getUserId(body.state); + const connectedAccnt = await connection.handleCallback(body); + + // whether we should emit a connections update event, only used when a connection doesnt already exist + if (connectedAccnt) + emitEvent({ + event: "USER_CONNECTIONS_UPDATE", + data: { ...connectedAccnt, token_data: undefined }, + user_id: userId, + }); + + res.sendStatus(204); +}); export default router; diff --git a/src/api/routes/discoverable-guilds.ts b/src/api/routes/discoverable-guilds.ts index b8c6a386..e2a47cd7 100644 --- a/src/api/routes/discoverable-guilds.ts +++ b/src/api/routes/discoverable-guilds.ts @@ -70,7 +70,7 @@ router.get( offset: Number(offset || Config.get().guild.discovery.offset), limit: Number(limit || configLimit), }); - }, + } ); export default router; diff --git a/src/api/routes/discovery.ts b/src/api/routes/discovery.ts index a045c191..e4e7dbf7 100644 --- a/src/api/routes/discovery.ts +++ b/src/api/routes/discovery.ts @@ -38,12 +38,10 @@ router.get( // const { locale, primary_only } = req.query; const { 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); - }, + } ); export default router; diff --git a/src/api/routes/download.ts b/src/api/routes/download.ts index 85fb41be..6bb6d61c 100644 --- a/src/api/routes/download.ts +++ b/src/api/routes/download.ts @@ -52,7 +52,7 @@ router.get( }); res.redirect(release.url); - }, + } ); export default router; diff --git a/src/api/routes/gateway/bot.ts b/src/api/routes/gateway/bot.ts index d9101159..af12d424 100644 --- a/src/api/routes/gateway/bot.ts +++ b/src/api/routes/gateway/bot.ts @@ -43,7 +43,7 @@ router.get( max_concurrency: 1, }, }); - }, + } ); export default router; diff --git a/src/api/routes/gateway/index.ts b/src/api/routes/gateway/index.ts index 9100d5ee..4ef16818 100644 --- a/src/api/routes/gateway/index.ts +++ b/src/api/routes/gateway/index.ts @@ -36,7 +36,7 @@ router.get( res.json({ url: endpointPublic || process.env.GATEWAY || "ws://localhost:3001", }); - }, + } ); export default router; diff --git a/src/api/routes/gifs/search.ts b/src/api/routes/gifs/search.ts index 305a2a48..b4628753 100644 --- a/src/api/routes/gifs/search.ts +++ b/src/api/routes/gifs/search.ts @@ -36,9 +36,7 @@ router.get( media_format: { type: "string", description: "Media format", - values: Object.keys(TenorMediaTypes).filter((key) => - isNaN(Number(key)), - ), + values: Object.keys(TenorMediaTypes).filter((key) => isNaN(Number(key))), }, locale: { type: "string", @@ -65,13 +63,13 @@ router.get( agent, method: "get", headers: { "Content-Type": "application/json" }, - }, + } ); const { results } = await response.json(); res.json(results.map(parseGifResult)).status(200); - }, + } ); export default router; diff --git a/src/api/routes/gifs/trending-gifs.ts b/src/api/routes/gifs/trending-gifs.ts index 77a61efc..c2aec7c3 100644 --- a/src/api/routes/gifs/trending-gifs.ts +++ b/src/api/routes/gifs/trending-gifs.ts @@ -31,9 +31,7 @@ router.get( media_format: { type: "string", description: "Media format", - values: Object.keys(TenorMediaTypes).filter((key) => - isNaN(Number(key)), - ), + values: Object.keys(TenorMediaTypes).filter((key) => isNaN(Number(key))), }, locale: { type: "string", @@ -60,13 +58,13 @@ router.get( agent, method: "get", headers: { "Content-Type": "application/json" }, - }, + } ); const { results } = await response.json(); res.json(results.map(parseGifResult)).status(200); - }, + } ); export default router; diff --git a/src/api/routes/gifs/trending.ts b/src/api/routes/gifs/trending.ts index fe726842..ebf42629 100644 --- a/src/api/routes/gifs/trending.ts +++ b/src/api/routes/gifs/trending.ts @@ -17,12 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - TenorCategoriesResults, - TenorTrendingResults, - getGifApiKey, - parseGifResult, -} from "@spacebar/util"; +import { TenorCategoriesResults, TenorTrendingResults, getGifApiKey, parseGifResult } from "@spacebar/util"; import { Request, Response, Router } from "express"; import fetch from "node-fetch"; import { ProxyAgent } from "proxy-agent"; @@ -55,28 +50,20 @@ router.get( const agent = new ProxyAgent(); const [responseSource, trendGifSource] = await Promise.all([ - fetch( - `https://g.tenor.com/v1/categories?locale=${locale}&key=${apiKey}`, - { - agent, - method: "get", - headers: { "Content-Type": "application/json" }, - }, - ), - fetch( - `https://g.tenor.com/v1/trending?locale=${locale}&key=${apiKey}`, - { - agent, - method: "get", - headers: { "Content-Type": "application/json" }, - }, - ), + fetch(`https://g.tenor.com/v1/categories?locale=${locale}&key=${apiKey}`, { + agent, + method: "get", + headers: { "Content-Type": "application/json" }, + }), + fetch(`https://g.tenor.com/v1/trending?locale=${locale}&key=${apiKey}`, { + agent, + method: "get", + headers: { "Content-Type": "application/json" }, + }), ]); - const { tags } = - (await responseSource.json()) as TenorCategoriesResults; - const { results } = - (await trendGifSource.json()) as TenorTrendingResults; + const { tags } = (await responseSource.json()) as TenorCategoriesResults; + const { results } = (await trendGifSource.json()) as TenorTrendingResults; res.json({ categories: tags.map((x) => ({ @@ -85,7 +72,7 @@ router.get( })), gifs: [parseGifResult(results[0])], }).status(200); - }, + } ); export default router; diff --git a/src/api/routes/guild-recommendations.ts b/src/api/routes/guild-recommendations.ts index 876780df..14f0dfbc 100644 --- a/src/api/routes/guild-recommendations.ts +++ b/src/api/routes/guild-recommendations.ts @@ -39,9 +39,7 @@ router.get( const showAllGuilds = Config.get().guild.discovery.showAllGuilds; const genLoadId = (size: number) => - [...Array(size)] - .map(() => Math.floor(Math.random() * 16).toString(16)) - .join(""); + [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join(""); const guilds = showAllGuilds ? await Guild.find({ take: Math.abs(Number(limit || 24)) }) @@ -53,7 +51,7 @@ router.get( recommended_guilds: guilds, load_id: `server_recs/${genLoadId(32)}`, }).status(200); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts index 0776ab62..1fcc6806 100644 --- a/src/api/routes/guilds/#guild_id/bans.ts +++ b/src/api/routes/guilds/#guild_id/bans.ts @@ -78,7 +78,7 @@ router.get( }); return res.json(bansObj); - }, + } ); router.get( @@ -115,7 +115,7 @@ router.get( delete ban.ip; return res.json(ban); - }, + } ); router.put( @@ -139,14 +139,8 @@ router.put( 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 - ) - throw new HTTPError( - "You are the guild owner, hence can't ban yourself", - 403, - ); + 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); @@ -175,7 +169,7 @@ router.put( ]); return res.json(ban); - }, + } ); router.put( @@ -200,10 +194,7 @@ router.put( const banned_user = await User.getPublicUser(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, - ); + throw new HTTPError("You are the guild owner, hence can't ban yourself", 403); const ban = Ban.create({ user_id: req.params.user_id, @@ -227,7 +218,7 @@ router.put( ]); return res.json(ban); - }, + } ); router.delete( @@ -273,7 +264,7 @@ router.delete( ]); return res.status(204).send(); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index 68208fee..0ad3e252 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -42,7 +42,7 @@ router.get( const channels = await Channel.find({ where: { guild_id } }); res.json(channels); - }, + } ); router.post( @@ -67,13 +67,10 @@ router.post( const { guild_id } = req.params; const body = req.body as ChannelModifySchema; - const channel = await Channel.createChannel( - { ...body, guild_id }, - req.user_id, - ); + const channel = await Channel.createChannel({ ...body, guild_id }, req.user_id); res.status(201).json(channel); - }, + } ); router.patch( @@ -102,9 +99,7 @@ router.patch( }); // The channels not listed for this query - const notMentioned = guild.channel_ordering.filter( - (x) => !body.find((c) => c.id == x), - ); + const notMentioned = guild.channel_ordering.filter((x) => !body.find((c) => c.id == x)); const withParents = body.filter((x) => x.parent_id != undefined); const withPositions = body.filter((x) => x.position != undefined); @@ -124,7 +119,7 @@ router.patch( channel_id: channel.id, guild_id, } as ChannelUpdateEvent); - }), + }) ); // have to do the parents after the positions @@ -141,10 +136,7 @@ router.patch( ]); if (opt.lock_permissions) - await Channel.update( - { id: channel.id }, - { permission_overwrites: parent.permission_overwrites }, - ); + await Channel.update({ id: channel.id }, { permission_overwrites: parent.permission_overwrites }); const parentPos = notMentioned.indexOf(parent.id); notMentioned.splice(parentPos + 1, 0, channel.id); @@ -156,16 +148,13 @@ router.patch( channel_id: channel.id, guild_id, } as ChannelUpdateEvent); - }), + }) ); - await Guild.update( - { id: guild_id }, - { channel_ordering: notMentioned }, - ); + await Guild.update({ id: guild_id }, { channel_ordering: notMentioned }); return res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/delete.ts b/src/api/routes/guilds/#guild_id/delete.ts index dee52c81..9f874a5c 100644 --- a/src/api/routes/guilds/#guild_id/delete.ts +++ b/src/api/routes/guilds/#guild_id/delete.ts @@ -45,8 +45,7 @@ router.post( where: { id: guild_id }, select: ["owner_id"], }); - if (guild.owner_id !== req.user_id) - throw new HTTPError("You are not the owner of this guild", 401); + if (guild.owner_id !== req.user_id) throw new HTTPError("You are not the owner of this guild", 401); await Promise.all([ Guild.delete({ id: guild_id }), // this will also delete all guild related data @@ -60,7 +59,7 @@ router.post( ]); return res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/discovery-requirements.ts b/src/api/routes/guilds/#guild_id/discovery-requirements.ts index 741fa9b3..6de571c5 100644 --- a/src/api/routes/guilds/#guild_id/discovery-requirements.ts +++ b/src/api/routes/guilds/#guild_id/discovery-requirements.ts @@ -59,7 +59,7 @@ router.get( }, minimum_size: 0, }); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/emojis.ts b/src/api/routes/guilds/#guild_id/emojis.ts index ef28f989..fa821cf0 100644 --- a/src/api/routes/guilds/#guild_id/emojis.ts +++ b/src/api/routes/guilds/#guild_id/emojis.ts @@ -57,7 +57,7 @@ router.get( }); return res.json(emojis); - }, + } ); router.get( @@ -86,7 +86,7 @@ router.get( }); return res.json(emoji); - }, + } ); router.post( @@ -116,10 +116,7 @@ router.post( }); const { maxEmojis } = Config.get().limits.guild; - if (emoji_count >= maxEmojis) - throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams( - maxEmojis, - ); + if (emoji_count >= maxEmojis) throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(maxEmojis); if (body.require_colons == null) body.require_colons = true; const user = await User.findOneOrFail({ where: { id: req.user_id } }); @@ -147,7 +144,7 @@ router.post( } as GuildEmojisUpdateEvent); return res.status(201).json(emoji); - }, + } ); router.patch( @@ -184,7 +181,7 @@ router.patch( } as GuildEmojisUpdateEvent); return res.json(emoji); - }, + } ); router.delete( @@ -216,7 +213,7 @@ router.delete( } as GuildEmojisUpdateEvent); res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/index.ts b/src/api/routes/guilds/#guild_id/index.ts index 839ec363..87388abc 100644 --- a/src/api/routes/guilds/#guild_id/index.ts +++ b/src/api/routes/guilds/#guild_id/index.ts @@ -58,17 +58,13 @@ router.get( Guild.findOneOrFail({ where: { id: guild_id } }), Member.findOne({ where: { guild_id: guild_id, id: req.user_id } }), ]); - if (!member) - throw new HTTPError( - "You are not a member of the guild you are trying to access", - 401, - ); + if (!member) throw new HTTPError("You are not a member of the guild you are trying to access", 401); return res.send({ ...guild, joined_at: member?.joined_at, }); - }, + } ); router.patch( @@ -99,9 +95,7 @@ router.patch( const permission = await getPermission(req.user_id, guild_id); if (!rights.has("MANAGE_GUILDS") && !permission.has("MANAGE_GUILD")) - throw DiscordApiErrors.MISSING_PERMISSIONS.withParams( - "MANAGE_GUILDS", - ); + throw DiscordApiErrors.MISSING_PERMISSIONS.withParams("MANAGE_GUILDS"); const guild = await Guild.findOneOrFail({ where: { id: guild_id }, @@ -110,47 +104,29 @@ router.patch( // TODO: guild update check image - if (body.icon && body.icon != guild.icon) - body.icon = await handleFile(`/icons/${guild_id}`, body.icon); + if (body.icon && body.icon != guild.icon) body.icon = await handleFile(`/icons/${guild_id}`, body.icon); if (body.banner && body.banner !== guild.banner) body.banner = await handleFile(`/banners/${guild_id}`, body.banner); if (body.splash && body.splash !== guild.splash) - body.splash = await handleFile( - `/splashes/${guild_id}`, - body.splash, - ); + body.splash = await handleFile(`/splashes/${guild_id}`, body.splash); - if ( - body.discovery_splash && - body.discovery_splash !== guild.discovery_splash - ) - body.discovery_splash = await handleFile( - `/discovery-splashes/${guild_id}`, - body.discovery_splash, - ); + if (body.discovery_splash && body.discovery_splash !== guild.discovery_splash) + body.discovery_splash = await handleFile(`/discovery-splashes/${guild_id}`, body.discovery_splash); if (body.features) { const diff = guild.features .filter((x) => !body.features?.includes(x)) - .concat( - body.features.filter((x) => !guild.features.includes(x)), - ); + .concat(body.features.filter((x) => !guild.features.includes(x))); // TODO move these - const MUTABLE_FEATURES = [ - "COMMUNITY", - "INVITES_DISABLED", - "DISCOVERABLE", - ]; + const MUTABLE_FEATURES = ["COMMUNITY", "INVITES_DISABLED", "DISCOVERABLE"]; for (const feature of diff) { if (MUTABLE_FEATURES.includes(feature)) continue; - throw SpacebarApiErrors.FEATURE_IS_IMMUTABLE.withParams( - feature, - ); + throw SpacebarApiErrors.FEATURE_IS_IMMUTABLE.withParams(feature); } // for some reason, they don't update in the assign. @@ -179,7 +155,7 @@ router.patch( ], }, undefined, - { skipPermissionCheck: true }, + { skipPermissionCheck: true } ); await Guild.insertChannelInOrder(guild.id, channel.id, 0, guild); @@ -212,7 +188,7 @@ router.patch( ], }, undefined, - { skipPermissionCheck: true }, + { skipPermissionCheck: true } ); await Guild.insertChannelInOrder(guild.id, channel.id, 0, guild); @@ -242,7 +218,7 @@ router.patch( ]); return res.json(data); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/invites.ts b/src/api/routes/guilds/#guild_id/invites.ts index a0ffa3f4..5311a77f 100644 --- a/src/api/routes/guilds/#guild_id/invites.ts +++ b/src/api/routes/guilds/#guild_id/invites.ts @@ -41,7 +41,7 @@ router.get( }); return res.json(invites); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/member-verification.ts b/src/api/routes/guilds/#guild_id/member-verification.ts index 2c39093e..b30c379d 100644 --- a/src/api/routes/guilds/#guild_id/member-verification.ts +++ b/src/api/routes/guilds/#guild_id/member-verification.ts @@ -36,7 +36,7 @@ router.get( message: "Unknown Guild Member Verification Form", code: 10068, }); - }, + } ); export default 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 c168f2dc..5dd6f2b8 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 @@ -62,13 +62,9 @@ router.get( select: { index: true, // only grab public member props - ...Object.fromEntries( - PublicMemberProjection.map((x) => [x, true]), - ), + ...Object.fromEntries(PublicMemberProjection.map((x) => [x, true])), // and public user props - user: Object.fromEntries( - PublicUserProjection.map((x) => [x, true]), - ), + user: Object.fromEntries(PublicUserProjection.map((x) => [x, true])), roles: { id: true, }, @@ -80,7 +76,7 @@ router.get( user: member.user.toPublicUser(), roles: member.roles.map((x) => x.id), }); - }, + } ); router.patch( @@ -104,8 +100,7 @@ router.patch( }), async (req: Request, res: Response) => { const { guild_id } = req.params; - const member_id = - req.params.member_id === "@me" ? req.user_id : req.params.member_id; + const member_id = req.params.member_id === "@me" ? req.user_id : req.params.member_id; const body = req.body as MemberChangeSchema; const member = await Member.findOneOrFail({ @@ -128,19 +123,13 @@ router.patch( } } - if ( - ("bio" in body || "avatar" in body) && - req.params.member_id != "@me" - ) { + if (("bio" in body || "avatar" in body) && req.params.member_id != "@me") { const rights = await getRights(req.user_id); rights.hasThrow("MANAGE_USERS"); } if (body.avatar) - body.avatar = await handleFile( - `/guilds/${guild_id}/users/${member_id}/avatars`, - body.avatar as string, - ); + body.avatar = await handleFile(`/guilds/${guild_id}/users/${member_id}/avatars`, body.avatar as string); member.assign(body); @@ -152,8 +141,7 @@ router.patch( body.roles = body.roles || []; body.roles.filter((x) => !!x); - if (body.roles.indexOf(everyone.id) === -1) - body.roles.push(everyone.id); + if (body.roles.indexOf(everyone.id) === -1) body.roles.push(everyone.id); // foreign key constraint will fail if role doesn't exist member.roles = body.roles.map((x) => Role.create({ id: x })); } @@ -170,7 +158,7 @@ router.patch( } as GuildMemberUpdateEvent); res.json(member); - }, + } ); router.put( @@ -222,7 +210,7 @@ router.put( await Member.addToGuild(member_id, guild_id); res.send({ ...guild, emojis: emoji, roles: roles, stickers: stickers }); - }, + } ); router.delete( @@ -249,7 +237,7 @@ router.delete( await Member.removeFromGuild(member_id, guild_id); res.sendStatus(204); - }, + } ); export default router; 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 7b8e44d3..8162f36b 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 @@ -49,7 +49,7 @@ router.patch( await Member.changeNickname(member_id, guild_id, req.body.nick); res.status(200).send(); - }, + } ); export default 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 46dd70bb..da28e312 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 @@ -38,7 +38,7 @@ router.delete( await Member.removeRole(member_id, guild_id, role_id); res.sendStatus(204); - }, + } ); router.put( @@ -55,7 +55,7 @@ router.put( await Member.addRole(member_id, guild_id, role_id); res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/members/index.ts b/src/api/routes/guilds/#guild_id/members/index.ts index 9260308d..8ea590d0 100644 --- a/src/api/routes/guilds/#guild_id/members/index.ts +++ b/src/api/routes/guilds/#guild_id/members/index.ts @@ -33,8 +33,7 @@ router.get( query: { limit: { type: "number", - description: - "max number of members to return (1-1000). default 1", + description: "max number of members to return (1-1000). default 1", }, after: { type: "string", @@ -52,8 +51,7 @@ router.get( async (req: Request, res: Response) => { const { guild_id } = req.params; const limit = Number(req.query.limit) || 1; - if (limit > 1000 || limit < 1) - throw new HTTPError("Limit must be between 1 and 1000"); + if (limit > 1000 || limit < 1) throw new HTTPError("Limit must be between 1 and 1000"); const after = `${req.query.after}`; const query = after ? { id: MoreThan(after) } : {}; @@ -67,7 +65,7 @@ router.get( }); return res.json(members); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/messages/search.ts b/src/api/routes/guilds/#guild_id/messages/search.ts index 637d1e43..aec36f85 100644 --- a/src/api/routes/guilds/#guild_id/messages/search.ts +++ b/src/api/routes/guilds/#guild_id/messages/search.ts @@ -54,14 +54,10 @@ router.get( } = req.query; const parsedLimit = Number(limit) || 50; - if (parsedLimit < 1 || parsedLimit > 100) - throw new HTTPError("limit must be between 1 and 100", 422); + if (parsedLimit < 1 || parsedLimit > 100) throw new HTTPError("limit must be between 1 and 100", 422); if (sort_order) { - if ( - typeof sort_order != "string" || - ["desc", "asc"].indexOf(sort_order) == -1 - ) + if (typeof sort_order != "string" || ["desc", "asc"].indexOf(sort_order) == -1) throw FieldErrors({ sort_order: { message: "Value must be one of ('desc', 'asc').", @@ -70,20 +66,13 @@ router.get( }); // todo this is wrong } - const permissions = await getPermission( - req.user_id, - req.params.guild_id, - channel_id as string | undefined, - ); + const permissions = await getPermission(req.user_id, req.params.guild_id, channel_id as string | undefined); permissions.hasThrow("VIEW_CHANNEL"); - if (!permissions.has("READ_MESSAGE_HISTORY")) - return res.json({ messages: [], total_results: 0 }); + if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json({ messages: [], total_results: 0 }); const query: FindManyOptions<Message> = { order: { - timestamp: sort_order - ? (sort_order.toUpperCase() as "ASC" | "DESC") - : "DESC", + timestamp: sort_order ? (sort_order.toUpperCase() as "ASC" | "DESC") : "DESC", }, take: parsedLimit || 0, where: { @@ -114,16 +103,8 @@ router.get( const ids = []; for (const channel of channels) { - const perm = await getPermission( - req.user_id, - req.params.guild_id, - channel.id, - ); - if ( - !perm.has("VIEW_CHANNEL") || - !perm.has("READ_MESSAGE_HISTORY") - ) - continue; + const perm = await getPermission(req.user_id, req.params.guild_id, channel.id); + if (!perm.has("VIEW_CHANNEL") || !perm.has("READ_MESSAGE_HISTORY")) continue; ids.push(channel.id); } @@ -170,7 +151,7 @@ router.get( messages: messagesDto, total_results: messages.length, }); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/profile/index.ts b/src/api/routes/guilds/#guild_id/profile/index.ts index 60526259..e34136a4 100644 --- a/src/api/routes/guilds/#guild_id/profile/index.ts +++ b/src/api/routes/guilds/#guild_id/profile/index.ts @@ -57,10 +57,7 @@ router.patch( }); if (body.banner) - body.banner = await handleFile( - `/guilds/${guild_id}/users/${req.user_id}/avatars`, - body.banner as string, - ); + body.banner = await handleFile(`/guilds/${guild_id}/users/${req.user_id}/avatars`, body.banner as string); member = await OrmUtils.mergeDeep(member, body); @@ -74,7 +71,7 @@ router.patch( } as GuildMemberUpdateEvent); res.json(member); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/prune.ts b/src/api/routes/guilds/#guild_id/prune.ts index 2c77340d..7fa31ed2 100644 --- a/src/api/routes/guilds/#guild_id/prune.ts +++ b/src/api/routes/guilds/#guild_id/prune.ts @@ -23,12 +23,7 @@ import { IsNull, LessThan } from "typeorm"; const router = Router(); //Returns all inactive members, respecting role hierarchy -const inactiveMembers = async ( - guild_id: string, - user_id: string, - days: number, - roles: string[] = [], -) => { +const inactiveMembers = async (guild_id: string, user_id: string, days: number, roles: string[] = []) => { const date = new Date(); date.setDate(date.getDate() - days); //Snowflake should have `generateFromTime` method? Or similar? @@ -55,9 +50,7 @@ const inactiveMembers = async ( //I'm sure I can do this in the above db query ( and it would probably be better to do so ), but oh well. if (roles.length && members.length) - members = members.filter((user) => - user.roles?.some((role) => roles.includes(role.id)), - ); + members = members.filter((user) => user.roles?.some((role) => roles.includes(role.id))); const me = await Member.findOneOrFail({ where: { id: user_id, guild_id }, @@ -73,8 +66,8 @@ const inactiveMembers = async ( member.roles?.some( (role) => role.position < myHighestRole || //roles higher than me can't be kicked - me.id === guild.owner_id, //owner can kick anyone - ), + me.id === guild.owner_id //owner can kick anyone + ) ); return members; @@ -95,15 +88,10 @@ router.get( let roles = req.query.include_roles; if (typeof roles === "string") roles = [roles]; //express will return array otherwise - const members = await inactiveMembers( - req.params.guild_id, - req.user_id, - days, - roles as string[], - ); + const members = await inactiveMembers(req.params.guild_id, req.user_id, days, roles as string[]); res.send({ pruned: members.length }); - }, + } ); router.post( @@ -127,19 +115,12 @@ router.post( if (typeof roles === "string") roles = [roles]; const { guild_id } = req.params; - const members = await inactiveMembers( - guild_id, - req.user_id, - days, - roles as string[], - ); + const members = await inactiveMembers(guild_id, req.user_id, days, roles as string[]); - await Promise.all( - members.map((x) => Member.removeFromGuild(x.id, guild_id)), - ); + await Promise.all(members.map((x) => Member.removeFromGuild(x.id, guild_id))); res.send({ purged: members.length }); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/regions.ts b/src/api/routes/guilds/#guild_id/regions.ts index b0ae0602..e40a73e2 100644 --- a/src/api/routes/guilds/#guild_id/regions.ts +++ b/src/api/routes/guilds/#guild_id/regions.ts @@ -38,13 +38,8 @@ router.get( const { guild_id } = req.params; const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); //TODO we should use an enum for guild's features and not hardcoded strings - return res.json( - await getVoiceRegions( - getIpAdress(req), - guild.features.includes("VIP_REGIONS"), - ), - ); - }, + return res.json(await getVoiceRegions(getIpAdress(req), guild.features.includes("VIP_REGIONS"))); + } ); export default 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 ea1a782a..ae10addf 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 @@ -53,7 +53,7 @@ router.get( where: { guild_id, id: role_id }, }); return res.json(role); - }, + } ); router.delete( @@ -75,8 +75,7 @@ router.delete( }), async (req: Request, res: Response) => { const { guild_id, role_id } = req.params; - if (role_id === guild_id) - throw new HTTPError("You can't delete the @everyone role"); + if (role_id === guild_id) throw new HTTPError("You can't delete the @everyone role"); await Promise.all([ Role.delete({ @@ -94,7 +93,7 @@ router.delete( ]); res.sendStatus(204); - }, + } ); // TODO: check role hierarchy @@ -123,11 +122,7 @@ router.patch( const { role_id, guild_id } = req.params; const body = req.body as RoleModifySchema; - if (body.icon && body.icon.length) - body.icon = await handleFile( - `/role-icons/${role_id}`, - body.icon as string, - ); + if (body.icon && body.icon.length) body.icon = await handleFile(`/role-icons/${role_id}`, body.icon as string); else body.icon = undefined; const role = await Role.findOneOrFail({ @@ -135,10 +130,7 @@ router.patch( }); role.assign({ ...body, - permissions: String( - (req.permission?.bitfield || 0n) & - BigInt(body.permissions || "0"), - ), + permissions: String((req.permission?.bitfield || 0n) & BigInt(body.permissions || "0")), }); await Promise.all([ @@ -154,7 +146,7 @@ router.patch( ]); res.json(role); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/roles/#role_id/members.ts b/src/api/routes/guilds/#guild_id/roles/#role_id/members.ts index 539cd5d8..a34908a6 100644 --- a/src/api/routes/guilds/#guild_id/roles/#role_id/members.ts +++ b/src/api/routes/guilds/#guild_id/roles/#role_id/members.ts @@ -22,41 +22,31 @@ import { route } from "@spacebar/api"; const router = Router(); -router.patch( - "/", - route({ permission: "MANAGE_ROLES" }), - async (req: Request, res: Response) => { - // Payload is JSON containing a list of member_ids, the new list of members to have the role - const { guild_id, role_id } = req.params; - const { member_ids } = req.body; - - // don't mess with @everyone - if (role_id == guild_id) throw DiscordApiErrors.INVALID_ROLE; - - const members = await Member.find({ - where: { guild_id }, - relations: ["roles"], - }); - - const [add, remove] = partition( - members, - (member) => - member_ids.includes(member.id) && - !member.roles.map((role) => role.id).includes(role_id), - ); - - // TODO (erkin): have a bulk add/remove function that adds the roles in a single txn - await Promise.all([ - ...add.map((member) => - Member.addRole(member.id, guild_id, role_id), - ), - ...remove.map((member) => - Member.removeRole(member.id, guild_id, role_id), - ), - ]); - - res.sendStatus(204); - }, -); +router.patch("/", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => { + // Payload is JSON containing a list of member_ids, the new list of members to have the role + const { guild_id, role_id } = req.params; + const { member_ids } = req.body; + + // don't mess with @everyone + if (role_id == guild_id) throw DiscordApiErrors.INVALID_ROLE; + + const members = await Member.find({ + where: { guild_id }, + relations: ["roles"], + }); + + const [add, remove] = partition( + members, + (member) => member_ids.includes(member.id) && !member.roles.map((role) => role.id).includes(role_id) + ); + + // TODO (erkin): have a bulk add/remove function that adds the roles in a single txn + await Promise.all([ + ...add.map((member) => Member.addRole(member.id, guild_id, role_id)), + ...remove.map((member) => Member.removeRole(member.id, guild_id, role_id)), + ]); + + res.sendStatus(204); +}); export default router; diff --git a/src/api/routes/guilds/#guild_id/roles/index.ts b/src/api/routes/guilds/#guild_id/roles/index.ts index e2c34e7f..1e60c2cf 100644 --- a/src/api/routes/guilds/#guild_id/roles/index.ts +++ b/src/api/routes/guilds/#guild_id/roles/index.ts @@ -68,8 +68,7 @@ router.post( const role_count = await Role.count({ where: { guild_id } }); const { maxRoles } = Config.get().limits.guild; - if (role_count > maxRoles) - throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles); + if (role_count > maxRoles) throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles); const role = Role.create({ // values before ...body are default and can be overriden @@ -80,10 +79,7 @@ router.post( ...body, guild_id: guild_id, managed: false, - permissions: String( - (req.permission?.bitfield || 0n) & - BigInt(body.permissions || "0"), - ), + permissions: String((req.permission?.bitfield || 0n) & BigInt(body.permissions || "0")), tags: undefined, icon: undefined, unicode_emoji: undefined, @@ -112,7 +108,7 @@ router.post( ]); res.json(role); - }, + } ); router.patch( @@ -136,11 +132,7 @@ router.patch( const { guild_id } = req.params; const body = req.body as RolePositionUpdateSchema; - await Promise.all( - body.map(async (x) => - Role.update({ guild_id, id: x.id }, { position: x.position }), - ), - ); + await Promise.all(body.map(async (x) => Role.update({ guild_id, id: x.id }, { position: x.position }))); const roles = await Role.find({ where: body.map((x) => ({ id: x.id, guild_id })), @@ -155,12 +147,12 @@ router.patch( guild_id, role: x, }, - } as GuildRoleUpdateEvent), - ), + } as GuildRoleUpdateEvent) + ) ); res.json(roles); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/stickers.ts b/src/api/routes/guilds/#guild_id/stickers.ts index 88f9a40e..92b09aab 100644 --- a/src/api/routes/guilds/#guild_id/stickers.ts +++ b/src/api/routes/guilds/#guild_id/stickers.ts @@ -50,7 +50,7 @@ router.get( await Member.IsInGuildOrFail(req.user_id, guild_id); res.json(await Sticker.find({ where: { guild_id } })); - }, + } ); const bodyParser = multer({ @@ -102,7 +102,7 @@ router.post( await sendStickerUpdateEvent(guild_id); res.json(sticker); - }, + } ); function getStickerFormat(mime_type: string) { @@ -116,9 +116,7 @@ function getStickerFormat(mime_type: string) { case "image/gif": return StickerFormatType.GIF; default: - throw new HTTPError( - "invalid sticker format: must be png, apng or lottie", - ); + throw new HTTPError("invalid sticker format: must be png, apng or lottie"); } } @@ -141,9 +139,9 @@ router.get( res.json( await Sticker.findOneOrFail({ where: { guild_id, id: sticker_id }, - }), + }) ); - }, + } ); router.patch( @@ -175,7 +173,7 @@ router.patch( await sendStickerUpdateEvent(guild_id); return res.json(sticker); - }, + } ); async function sendStickerUpdateEvent(guild_id: string) { @@ -207,7 +205,7 @@ router.delete( await sendStickerUpdateEvent(guild_id); return res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/templates.ts b/src/api/routes/guilds/#guild_id/templates.ts index 85ae0ac9..ae40377e 100644 --- a/src/api/routes/guilds/#guild_id/templates.ts +++ b/src/api/routes/guilds/#guild_id/templates.ts @@ -57,7 +57,7 @@ router.get( }); return res.json(templates); - }, + } ); router.post( @@ -102,7 +102,7 @@ router.post( }).save(); res.json(template); - }, + } ); router.delete( @@ -123,7 +123,7 @@ router.delete( }); res.json(template); - }, + } ); router.put( @@ -148,7 +148,7 @@ router.put( }).save(); res.json(template); - }, + } ); router.patch( @@ -173,7 +173,7 @@ router.patch( }).save(); res.json(template); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/vanity-url.ts b/src/api/routes/guilds/#guild_id/vanity-url.ts index a64ae2c9..1cfa9883 100644 --- a/src/api/routes/guilds/#guild_id/vanity-url.ts +++ b/src/api/routes/guilds/#guild_id/vanity-url.ts @@ -17,13 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Channel, - ChannelType, - Guild, - Invite, - VanityUrlSchema, -} from "@spacebar/util"; +import { Channel, ChannelType, Guild, Invite, VanityUrlSchema } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; @@ -64,11 +58,9 @@ router.get( }); if (!invite || invite.length == 0) return res.json({ code: null }); - return res.json( - invite.map((x) => ({ code: x.code, uses: x.uses })), - ); + return res.json(invite.map((x) => ({ code: x.code, uses: x.uses }))); } - }, + } ); router.patch( @@ -94,11 +86,9 @@ router.patch( const code = body.code?.replace(InviteRegex, ""); const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); - if (!guild.features.includes("VANITY_URL")) - throw new HTTPError("Your guild doesn't support vanity urls"); + if (!guild.features.includes("VANITY_URL")) throw new HTTPError("Your guild doesn't support vanity urls"); - if (!code || code.length === 0) - throw new HTTPError("Code cannot be null or empty"); + if (!code || code.length === 0) throw new HTTPError("Code cannot be null or empty"); const invite = await Invite.findOne({ where: { code } }); if (invite) throw new HTTPError("Invite already exists"); @@ -112,7 +102,7 @@ router.patch( { guild_id }, { code: code, - }, + } ); return res.json({ code }); @@ -132,7 +122,7 @@ router.patch( }).save(); return res.json({ code: code }); - }, + } ); export default 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 60c69075..c13d053b 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 @@ -52,14 +52,9 @@ router.patch( async (req: Request, res: Response) => { const body = req.body as VoiceStateUpdateSchema; const { guild_id } = req.params; - const user_id = - req.params.user_id === "@me" ? req.user_id : req.params.user_id; + const user_id = req.params.user_id === "@me" ? req.user_id : req.params.user_id; - const perms = await getPermission( - req.user_id, - guild_id, - body.channel_id, - ); + const perms = await getPermission(req.user_id, guild_id, body.channel_id); /* From https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state @@ -98,7 +93,7 @@ router.patch( } as VoiceStateUpdateEvent), ]); return res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/welcome-screen.ts b/src/api/routes/guilds/#guild_id/welcome-screen.ts index 81000b4b..74565a13 100644 --- a/src/api/routes/guilds/#guild_id/welcome-screen.ts +++ b/src/api/routes/guilds/#guild_id/welcome-screen.ts @@ -17,12 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Channel, - Guild, - GuildUpdateWelcomeScreenSchema, - Member, -} from "@spacebar/util"; +import { Channel, Guild, GuildUpdateWelcomeScreenSchema, Member } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -46,7 +41,7 @@ router.get( await Member.IsInGuildOrFail(req.user_id, guild_id); res.json(guild.welcome_screen); - }, + } ); router.patch( @@ -70,11 +65,9 @@ router.patch( const guild = await Guild.findOneOrFail({ where: { id: guild_id } }); - if (body.enabled != undefined) - guild.welcome_screen.enabled = body.enabled; + if (body.enabled != undefined) guild.welcome_screen.enabled = body.enabled; - if (body.description != undefined) - guild.welcome_screen.description = body.description; + if (body.description != undefined) guild.welcome_screen.description = body.description; if (body.welcome_channels != undefined) { // Ensure channels exist within the guild @@ -83,8 +76,8 @@ router.patch( Channel.findOneOrFail({ where: { id: channel_id, guild_id }, select: { id: true }, - }), - ) || [], + }) + ) || [] ); guild.welcome_screen.welcome_channels = body.welcome_channels; } @@ -92,7 +85,7 @@ router.patch( await guild.save(); res.status(200).json(guild.welcome_screen); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/widget.json.ts b/src/api/routes/guilds/#guild_id/widget.json.ts index 39f49804..c2450699 100644 --- a/src/api/routes/guilds/#guild_id/widget.json.ts +++ b/src/api/routes/guilds/#guild_id/widget.json.ts @@ -81,10 +81,8 @@ router.get( // Only return channels where @everyone has the CONNECT permission if ( doc.permission_overwrites === undefined || - Permissions.channelPermission( - doc.permission_overwrites, - Permissions.FLAGS.CONNECT, - ) === Permissions.FLAGS.CONNECT + Permissions.channelPermission(doc.permission_overwrites, Permissions.FLAGS.CONNECT) === + Permissions.FLAGS.CONNECT ) { channels.push({ id: doc.id, @@ -110,7 +108,7 @@ router.get( res.set("Cache-Control", "public, max-age=300"); return res.json(data); - }, + } ); export default router; diff --git a/src/api/routes/guilds/#guild_id/widget.png.ts b/src/api/routes/guilds/#guild_id/widget.png.ts index c9ba8afc..c5080709 100644 --- a/src/api/routes/guilds/#guild_id/widget.png.ts +++ b/src/api/routes/guilds/#guild_id/widget.png.ts @@ -57,15 +57,8 @@ router.get( // Fetch parameter const style = req.query.style?.toString() || "shield"; - if ( - !["shield", "banner1", "banner2", "banner3", "banner4"].includes( - style, - ) - ) { - throw new HTTPError( - "Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').", - 400, - ); + if (!["shield", "banner1", "banner2", "banner3", "banner4"].includes(style)) { + throw new HTTPError("Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').", 400); } // Setup canvas @@ -74,17 +67,7 @@ router.get( const sizeOf = require("image-size"); // TODO: Widget style templates need Spacebar branding - const source = path.join( - __dirname, - "..", - "..", - "..", - "..", - "..", - "assets", - "widget", - `${style}.png`, - ); + const source = path.join(__dirname, "..", "..", "..", "..", "..", "assets", "widget", `${style}.png`); if (!fs.existsSync(source)) { throw new HTTPError("Widget template does not exist.", 400); } @@ -100,99 +83,32 @@ router.get( switch (style) { case "shield": ctx.textAlign = "center"; - await drawText( - ctx, - 73, - 13, - "#FFFFFF", - "thin 10px Verdana", - presence, - ); + await drawText(ctx, 73, 13, "#FFFFFF", "thin 10px Verdana", presence); break; case "banner1": if (icon) await drawIcon(ctx, 20, 27, 50, icon); - await drawText( - ctx, - 83, - 51, - "#FFFFFF", - "12px Verdana", - name, - 22, - ); - await drawText( - ctx, - 83, - 66, - "#C9D2F0FF", - "thin 11px Verdana", - presence, - ); + await drawText(ctx, 83, 51, "#FFFFFF", "12px Verdana", name, 22); + await drawText(ctx, 83, 66, "#C9D2F0FF", "thin 11px Verdana", presence); break; case "banner2": if (icon) await drawIcon(ctx, 13, 19, 36, icon); - await drawText( - ctx, - 62, - 34, - "#FFFFFF", - "12px Verdana", - name, - 15, - ); - await drawText( - ctx, - 62, - 49, - "#C9D2F0FF", - "thin 11px Verdana", - presence, - ); + await drawText(ctx, 62, 34, "#FFFFFF", "12px Verdana", name, 15); + await drawText(ctx, 62, 49, "#C9D2F0FF", "thin 11px Verdana", presence); break; case "banner3": if (icon) await drawIcon(ctx, 20, 20, 50, icon); - await drawText( - ctx, - 83, - 44, - "#FFFFFF", - "12px Verdana", - name, - 27, - ); - await drawText( - ctx, - 83, - 58, - "#C9D2F0FF", - "thin 11px Verdana", - presence, - ); + await drawText(ctx, 83, 44, "#FFFFFF", "12px Verdana", name, 27); + await drawText(ctx, 83, 58, "#C9D2F0FF", "thin 11px Verdana", presence); break; case "banner4": if (icon) await drawIcon(ctx, 21, 136, 50, icon); - await drawText( - ctx, - 84, - 156, - "#FFFFFF", - "13px Verdana", - name, - 27, - ); - await drawText( - ctx, - 84, - 171, - "#C9D2F0FF", - "thin 12px Verdana", - presence, - ); + await drawText(ctx, 84, 156, "#FFFFFF", "13px Verdana", name, 27); + await drawText(ctx, 84, 171, "#C9D2F0FF", "thin 12px Verdana", presence); break; default: throw new HTTPError( "Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').", - 400, + 400 ); } @@ -201,16 +117,10 @@ router.get( res.set("Content-Type", "image/png"); res.set("Cache-Control", "public, max-age=3600"); return res.send(buffer); - }, + } ); -async function drawIcon( - canvas: any, - x: number, - y: number, - scale: number, - icon: string, -) { +async function drawIcon(canvas: any, x: number, y: number, scale: number, icon: string) { const img = new (require("canvas").Image)(); img.src = icon; @@ -234,12 +144,11 @@ async function drawText( color: string, font: string, text: string, - maxcharacters?: number, + maxcharacters?: number ) { canvas.fillStyle = color; canvas.font = font; - if (text.length > (maxcharacters || 0) && maxcharacters) - text = text.slice(0, maxcharacters) + "..."; + if (text.length > (maxcharacters || 0) && maxcharacters) text = text.slice(0, maxcharacters) + "..."; canvas.fillText(text, x, y); } diff --git a/src/api/routes/guilds/#guild_id/widget.ts b/src/api/routes/guilds/#guild_id/widget.ts index cae0d6be..17667f29 100644 --- a/src/api/routes/guilds/#guild_id/widget.ts +++ b/src/api/routes/guilds/#guild_id/widget.ts @@ -44,7 +44,7 @@ router.get( enabled: guild.widget_enabled || false, channel_id: guild.widget_channel_id || null, }); - }, + } ); // https://discord.com/developers/docs/resources/guild#modify-guild-widget @@ -74,12 +74,12 @@ router.patch( { widget_enabled: body.enabled, widget_channel_id: body.channel_id, - }, + } ); // Widget invite for the widget_channel_id gets created as part of the /guilds/{guild.id}/widget.json request return res.json(body); - }, + } ); export default router; diff --git a/src/api/routes/guilds/index.ts b/src/api/routes/guilds/index.ts index 545beb18..a0d2f33d 100644 --- a/src/api/routes/guilds/index.ts +++ b/src/api/routes/guilds/index.ts @@ -17,14 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Config, - DiscordApiErrors, - Guild, - GuildCreateSchema, - Member, - getRights, -} from "@spacebar/util"; +import { Config, DiscordApiErrors, Guild, GuildCreateSchema, Member, getRights } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -73,7 +66,7 @@ router.post( await Member.addToGuild(req.user_id, guild.id); res.status(201).json(guild); - }, + } ); export default router; diff --git a/src/api/routes/guilds/templates/index.ts b/src/api/routes/guilds/templates/index.ts index 8f718a21..693c8dcb 100644 --- a/src/api/routes/guilds/templates/index.ts +++ b/src/api/routes/guilds/templates/index.ts @@ -47,13 +47,11 @@ router.get( }, }), async (req: Request, res: Response) => { - const { allowDiscordTemplates, allowRaws, enabled } = - Config.get().templates; + const { allowDiscordTemplates, allowRaws, enabled } = Config.get().templates; if (!enabled) res.json({ code: 403, - message: - "Template creation & usage is disabled on this instance.", + message: "Template creation & usage is disabled on this instance.", }).sendStatus(403); const { code } = req.params; @@ -63,8 +61,7 @@ router.get( return res .json({ code: 403, - message: - "Discord templates cannot be used on this instance.", + message: "Discord templates cannot be used on this instance.", }) .sendStatus(403); const discordTemplateID = code.split("discord:", 2)[1]; @@ -74,7 +71,7 @@ router.get( { method: "get", headers: { "Content-Type": "application/json" }, - }, + } ); return res.json(await discordTemplateData.json()); } @@ -95,75 +92,70 @@ router.get( where: { code: code }, }); res.json(template); - }, + } ); -router.post( - "/:code", - route({ requestBody: "GuildTemplateCreateSchema" }), - async (req: Request, res: Response) => { - const { - enabled, - allowTemplateCreation, - // allowDiscordTemplates, - // allowRaws, - } = Config.get().templates; - if (!enabled) - return res - .json({ - code: 403, - message: - "Template creation & usage is disabled on this instance.", - }) - .sendStatus(403); - if (!allowTemplateCreation) - return res - .json({ - code: 403, - message: "Template creation is disabled on this instance.", - }) - .sendStatus(403); - - const { code } = req.params; - const body = req.body as GuildTemplateCreateSchema; - - const { maxGuilds } = Config.get().limits.user; - - const guild_count = await Member.count({ where: { id: req.user_id } }); - if (guild_count >= maxGuilds) { - throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds); - } - - const template = await Template.findOneOrFail({ - where: { code: code }, - }); - - const guild_id = Snowflake.generate(); - - const [guild] = await Promise.all([ - Guild.create({ - ...body, - ...template.serialized_source_guild, - id: guild_id, - owner_id: req.user_id, - }).save(), - Role.create({ - id: guild_id, - guild_id: guild_id, - color: 0, - hoist: false, - managed: true, - mentionable: true, - name: "@everyone", - permissions: BigInt("2251804225").toString(), // TODO: where did this come from? - position: 0, - }).save(), - ]); - - await Member.addToGuild(req.user_id, guild_id); - - res.status(201).json({ id: guild.id }); - }, -); +router.post("/:code", route({ requestBody: "GuildTemplateCreateSchema" }), async (req: Request, res: Response) => { + const { + enabled, + allowTemplateCreation, + // allowDiscordTemplates, + // allowRaws, + } = Config.get().templates; + if (!enabled) + return res + .json({ + code: 403, + message: "Template creation & usage is disabled on this instance.", + }) + .sendStatus(403); + if (!allowTemplateCreation) + return res + .json({ + code: 403, + message: "Template creation is disabled on this instance.", + }) + .sendStatus(403); + + const { code } = req.params; + const body = req.body as GuildTemplateCreateSchema; + + const { maxGuilds } = Config.get().limits.user; + + const guild_count = await Member.count({ where: { id: req.user_id } }); + if (guild_count >= maxGuilds) { + throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds); + } + + const template = await Template.findOneOrFail({ + where: { code: code }, + }); + + const guild_id = Snowflake.generate(); + + const [guild] = await Promise.all([ + Guild.create({ + ...body, + ...template.serialized_source_guild, + id: guild_id, + owner_id: req.user_id, + }).save(), + Role.create({ + id: guild_id, + guild_id: guild_id, + color: 0, + hoist: false, + managed: true, + mentionable: true, + name: "@everyone", + permissions: BigInt("2251804225").toString(), // TODO: where did this come from? + position: 0, + }).save(), + ]); + + await Member.addToGuild(req.user_id, guild_id); + + res.status(201).json({ id: guild.id }); +}); export default router; diff --git a/src/api/routes/invites/index.ts b/src/api/routes/invites/index.ts index 28a3b429..661b1f7c 100644 --- a/src/api/routes/invites/index.ts +++ b/src/api/routes/invites/index.ts @@ -53,7 +53,7 @@ router.get( }); res.status(200).send(invite); - }, + } ); router.post( @@ -89,21 +89,14 @@ router.post( 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_DISABLED")) - 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_DISABLED")) throw new HTTPError("Sorry, this guild has joins closed.", 403); const invite = await Invite.joinGuild(req.user_id, code); res.json(invite); - }, + } ); // * cant use permission of route() function because path doesn't have guild_id/channel_id @@ -127,20 +120,10 @@ router.delete( const invite = await Invite.findOneOrFail({ where: { code } }); const { guild_id, channel_id } = invite; - const permission = await getPermission( - req.user_id, - guild_id, - channel_id, - ); - - if ( - !permission.has("MANAGE_GUILD") && - !permission.has("MANAGE_CHANNELS") - ) - throw new HTTPError( - "You missing the MANAGE_GUILD or MANAGE_CHANNELS permission", - 401, - ); + const permission = await getPermission(req.user_id, guild_id, channel_id); + + if (!permission.has("MANAGE_GUILD") && !permission.has("MANAGE_CHANNELS")) + throw new HTTPError("You missing the MANAGE_GUILD or MANAGE_CHANNELS permission", 401); await Promise.all([ Invite.delete({ code }), @@ -156,7 +139,7 @@ router.delete( ]); res.json({ invite: invite }); - }, + } ); export default router; diff --git a/src/api/routes/oauth2/applications/@me.ts b/src/api/routes/oauth2/applications/@me.ts index 0c23bdb1..3c404d25 100644 --- a/src/api/routes/oauth2/applications/@me.ts +++ b/src/api/routes/oauth2/applications/@me.ts @@ -17,11 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Application, - DiscordApiErrors, - PublicUserProjection, -} from "@spacebar/util"; +import { Application, DiscordApiErrors, PublicUserProjection } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -40,9 +36,7 @@ router.get( where: { id: req.params.id }, relations: ["bot", "owner"], select: { - owner: Object.fromEntries( - PublicUserProjection.map((x) => [x, true]), - ), + owner: Object.fromEntries(PublicUserProjection.map((x) => [x, true])), }, }); @@ -51,9 +45,8 @@ router.get( res.json({ ...app, owner: app.owner.toPublicUser(), - install_params: - app.install_params !== null ? app.install_params : undefined, + install_params: app.install_params !== null ? app.install_params : undefined, }); - }, + } ); export default router; diff --git a/src/api/routes/oauth2/authorize.ts b/src/api/routes/oauth2/authorize.ts index 2f2351f3..bdb12455 100644 --- a/src/api/routes/oauth2/authorize.ts +++ b/src/api/routes/oauth2/authorize.ts @@ -84,13 +84,7 @@ router.get( id: req.user_id, bot: false, }, - select: [ - "id", - "username", - "avatar", - "discriminator", - "public_flags", - ], + select: ["id", "username", "avatar", "discriminator", "public_flags"], }); const guilds = await Member.find({ @@ -165,7 +159,7 @@ router.get( }, authorized: false, }); - }, + } ); router.post( @@ -210,17 +204,9 @@ router.post( // TODO: captcha verification // TODO: MFA verification - const perms = await getPermission( - req.user_id, - body.guild_id, - undefined, - { member_relations: ["user"] }, - ); + const perms = await getPermission(req.user_id, body.guild_id, undefined, { member_relations: ["user"] }); // getPermission cache won't exist if we're owner - if ( - Object.keys(perms.cache || {}).length > 0 && - perms.cache.member?.user.bot - ) + if (Object.keys(perms.cache || {}).length > 0 && perms.cache.member?.user.bot) throw DiscordApiErrors.UNAUTHORIZED; perms.hasThrow("MANAGE_GUILD"); @@ -234,19 +220,14 @@ router.post( // TODO: use DiscordApiErrors // findOneOrFail throws code 404 if (!app) throw new ApiError("Unknown Application", 10002, 404); - if (!app.bot) - throw new ApiError( - "OAuth2 application does not have a bot", - 50010, - 400, - ); + if (!app.bot) throw new ApiError("OAuth2 application does not have a bot", 50010, 400); await Member.addToGuild(app.id, body.guild_id); return res.json({ location: "/oauth2/authorized", // redirect URL }); - }, + } ); export default router; diff --git a/src/api/routes/ping.ts b/src/api/routes/ping.ts index 73330239..3161eb2c 100644 --- a/src/api/routes/ping.ts +++ b/src/api/routes/ping.ts @@ -48,7 +48,7 @@ router.get( tosPage: general.tosPage, }, }); - }, + } ); export default router; diff --git a/src/api/routes/policies/instance/domains.ts b/src/api/routes/policies/instance/domains.ts index afeb0e85..5ddd2114 100644 --- a/src/api/routes/policies/instance/domains.ts +++ b/src/api/routes/policies/instance/domains.ts @@ -34,20 +34,14 @@ router.get( const { cdn, gateway, api } = Config.get(); const IdentityForm = { - cdn: - cdn.endpointPublic || - process.env.CDN || - "http://localhost:3001", - gateway: - gateway.endpointPublic || - process.env.GATEWAY || - "ws://localhost:3001", + cdn: cdn.endpointPublic || process.env.CDN || "http://localhost:3001", + gateway: gateway.endpointPublic || process.env.GATEWAY || "ws://localhost:3001", defaultApiVersion: api.defaultVersion ?? 9, apiEndpoint: api.endpointPublic ?? "http://localhost:3001/api/", }; res.json(IdentityForm); - }, + } ); export default router; diff --git a/src/api/routes/policies/instance/index.ts b/src/api/routes/policies/instance/index.ts index 6e269a5c..9ac3f869 100644 --- a/src/api/routes/policies/instance/index.ts +++ b/src/api/routes/policies/instance/index.ts @@ -33,7 +33,7 @@ router.get( async (req: Request, res: Response) => { const { general } = Config.get(); res.json(general); - }, + } ); export default router; diff --git a/src/api/routes/policies/instance/limits.ts b/src/api/routes/policies/instance/limits.ts index 9852459d..79139d96 100644 --- a/src/api/routes/policies/instance/limits.ts +++ b/src/api/routes/policies/instance/limits.ts @@ -33,7 +33,7 @@ router.get( async (req: Request, res: Response) => { const { limits } = Config.get(); res.json(limits); - }, + } ); export default router; diff --git a/src/api/routes/policies/stats.ts b/src/api/routes/policies/stats.ts index b2cd3d5a..615ca6ba 100644 --- a/src/api/routes/policies/stats.ts +++ b/src/api/routes/policies/stats.ts @@ -17,14 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Config, - getRights, - Guild, - Member, - Message, - User, -} from "@spacebar/util"; +import { Config, getRights, Guild, Member, Message, User } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); @@ -54,7 +47,7 @@ router.get( members: await Member.count(), }, }); - }, + } ); export default router; diff --git a/src/api/routes/read-states/ack-bulk.ts b/src/api/routes/read-states/ack-bulk.ts index 3ee25d1a..d93b0450 100644 --- a/src/api/routes/read-states/ack-bulk.ts +++ b/src/api/routes/read-states/ack-bulk.ts @@ -61,7 +61,7 @@ router.post( ]); return res.status(204); - }, + } ); export default router; diff --git a/src/api/routes/scheduled-maintenances/upcoming_json.ts b/src/api/routes/scheduled-maintenances/upcoming_json.ts index c1fc0ff3..3b0bd500 100644 --- a/src/api/routes/scheduled-maintenances/upcoming_json.ts +++ b/src/api/routes/scheduled-maintenances/upcoming_json.ts @@ -20,15 +20,11 @@ import { Router, Request, Response } from "express"; import { route } from "@spacebar/api"; const router = Router(); -router.get( - "/scheduled-maintenances/upcoming.json", - route({}), - async (req: Request, res: Response) => { - res.json({ - page: {}, - scheduled_maintenances: {}, - }); - }, -); +router.get("/scheduled-maintenances/upcoming.json", route({}), async (req: Request, res: Response) => { + res.json({ + page: {}, + scheduled_maintenances: {}, + }); +}); export default router; diff --git a/src/api/routes/science.ts b/src/api/routes/science.ts index d5cdc173..6eb4a8e9 100644 --- a/src/api/routes/science.ts +++ b/src/api/routes/science.ts @@ -31,7 +31,7 @@ router.post( (req: Request, res: Response) => { // TODO: res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/sticker-packs/index.ts b/src/api/routes/sticker-packs/index.ts index 569d1104..ec12e30f 100644 --- a/src/api/routes/sticker-packs/index.ts +++ b/src/api/routes/sticker-packs/index.ts @@ -37,7 +37,7 @@ router.get( }); res.json({ sticker_packs }); - }, + } ); export default router; diff --git a/src/api/routes/stickers/#sticker_id/index.ts b/src/api/routes/stickers/#sticker_id/index.ts index 2ea81bf9..72765652 100644 --- a/src/api/routes/stickers/#sticker_id/index.ts +++ b/src/api/routes/stickers/#sticker_id/index.ts @@ -34,7 +34,7 @@ router.get( const { sticker_id } = req.params; res.json(await Sticker.find({ where: { id: sticker_id } })); - }, + } ); export default router; diff --git a/src/api/routes/stop.ts b/src/api/routes/stop.ts index 79e132d7..392c2d72 100644 --- a/src/api/routes/stop.ts +++ b/src/api/routes/stop.ts @@ -36,7 +36,7 @@ router.post( console.log(`/stop was called by ${req.user_id} at ${new Date()}`); res.sendStatus(200); process.kill(process.pid, "SIGTERM"); - }, + } ); export default router; diff --git a/src/api/routes/updates.ts b/src/api/routes/updates.ts index 101bd3bc..bddf4ca0 100644 --- a/src/api/routes/updates.ts +++ b/src/api/routes/updates.ts @@ -62,7 +62,7 @@ router.get( url: release.url, notes: release.notes, }); - }, + } ); export default router; diff --git a/src/api/routes/users/#id/delete.ts b/src/api/routes/users/#id/delete.ts index 5b1a682c..b1888d06 100644 --- a/src/api/routes/users/#id/delete.ts +++ b/src/api/routes/users/#id/delete.ts @@ -17,13 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - emitEvent, - Member, - PrivateUserProjection, - User, - UserDeleteEvent, -} from "@spacebar/util"; +import { emitEvent, Member, PrivateUserProjection, User, UserDeleteEvent } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); @@ -47,10 +41,7 @@ router.post( where: { id: req.params.id }, select: [...PrivateUserProjection, "data"], }); - await Promise.all([ - Member.delete({ id: req.params.id }), - User.delete({ id: req.params.id }), - ]); + await Promise.all([Member.delete({ id: req.params.id }), User.delete({ id: req.params.id })]); // TODO: respect intents as USER_DELETE has potential to cause privacy issues await emitEvent({ @@ -60,7 +51,7 @@ router.post( } as UserDeleteEvent); res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/users/#id/index.ts b/src/api/routes/users/#id/index.ts index 1bd413d3..b6fee65e 100644 --- a/src/api/routes/users/#id/index.ts +++ b/src/api/routes/users/#id/index.ts @@ -35,7 +35,7 @@ router.get( const { id } = req.params; res.json(await User.getPublicUser(id)); - }, + } ); export default router; diff --git a/src/api/routes/users/#id/profile.ts b/src/api/routes/users/#id/profile.ts index eecec0f3..4a4965d8 100644 --- a/src/api/routes/users/#id/profile.ts +++ b/src/api/routes/users/#id/profile.ts @@ -30,129 +30,115 @@ import { Request, Response, Router } from "express"; const router: Router = Router(); -router.get( - "/", - route({ responses: { 200: { body: "UserProfileResponse" } } }), - async (req: Request, res: Response) => { - if (req.params.id === "@me") req.params.id = req.user_id; +router.get("/", route({ responses: { 200: { body: "UserProfileResponse" } } }), async (req: Request, res: Response) => { + if (req.params.id === "@me") req.params.id = req.user_id; - const { guild_id, with_mutual_guilds } = req.query; + const { guild_id, with_mutual_guilds } = req.query; - const user = await User.getPublicUser(req.params.id, { - relations: ["connected_accounts"], + const user = await User.getPublicUser(req.params.id, { + relations: ["connected_accounts"], + }); + + const mutual_guilds: object[] = []; + let premium_guild_since; + + if (with_mutual_guilds == "true") { + const requested_member = await Member.find({ + where: { id: req.params.id }, + }); + const self_member = await Member.find({ + where: { id: req.user_id }, }); - const mutual_guilds: object[] = []; - let premium_guild_since; - - if (with_mutual_guilds == "true") { - 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) { - premium_guild_since = rmem.premium_since; - } - } else { + 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) { - if (smem.guild_id === rmem.guild_id) { - mutual_guilds.push({ - id: rmem.guild_id, - nick: rmem.nick, - }); - } + } + for (const smem of self_member) { + if (smem.guild_id === rmem.guild_id) { + mutual_guilds.push({ + id: rmem.guild_id, + nick: rmem.nick, + }); } } } - - const guild_member = - guild_id && typeof guild_id == "string" - ? await Member.findOneOrFail({ - where: { id: req.params.id, guild_id: guild_id }, - relations: ["roles"], - }) - : undefined; - - // TODO: make proper DTO's in util? - - const userProfile = { - bio: req.user_bot ? null : user.bio, - accent_color: user.accent_color, - banner: user.banner, - pronouns: user.pronouns, - theme_colors: user.theme_colors, - }; - - const guildMemberProfile = { - accent_color: null, - banner: guild_member?.banner || null, - bio: guild_member?.bio || "", - guild_id, - }; - res.json({ - connected_accounts: user.connected_accounts.filter( - (x) => x.visibility != 0, - ), - premium_guild_since: premium_guild_since, // TODO - premium_since: user.premium_since, // TODO - mutual_guilds: mutual_guilds, // TODO {id: "", nick: null} when ?with_mutual_guilds=true - user: user.toPublicUser(), - premium_type: user.premium_type, - profile_themes_experiment_bucket: 4, // TODO: This doesn't make it available, for some reason? - user_profile: userProfile, - guild_member: guild_member?.toPublicMember(), - guild_member_profile: guild_id && guildMemberProfile, - }); - }, -); - -router.patch( - "/", - route({ requestBody: "UserProfileModifySchema" }), - async (req: Request, res: Response) => { - const body = req.body as UserProfileModifySchema; - - if (body.banner) - body.banner = await handleFile( - `/banners/${req.user_id}`, - body.banner as string, - ); - const user = await User.findOneOrFail({ - where: { id: req.user_id }, - select: [...PrivateUserProjection, "data"], - }); - - user.assign(body); - await user.save(); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - delete user.data; - - // TODO: send update member list event in gateway - await emitEvent({ - event: "USER_UPDATE", - user_id: req.user_id, - data: user, - } as UserUpdateEvent); - - res.json({ - accent_color: user.accent_color, - bio: user.bio, - banner: user.banner, - theme_colors: user.theme_colors, - pronouns: user.pronouns, - }); - }, -); + } + + const guild_member = + guild_id && typeof guild_id == "string" + ? await Member.findOneOrFail({ + where: { id: req.params.id, guild_id: guild_id }, + relations: ["roles"], + }) + : undefined; + + // TODO: make proper DTO's in util? + + const userProfile = { + bio: req.user_bot ? null : user.bio, + accent_color: user.accent_color, + banner: user.banner, + pronouns: user.pronouns, + theme_colors: user.theme_colors, + }; + + const guildMemberProfile = { + accent_color: null, + banner: guild_member?.banner || null, + bio: guild_member?.bio || "", + guild_id, + }; + res.json({ + connected_accounts: user.connected_accounts.filter((x) => x.visibility != 0), + premium_guild_since: premium_guild_since, // TODO + premium_since: user.premium_since, // TODO + mutual_guilds: mutual_guilds, // TODO {id: "", nick: null} when ?with_mutual_guilds=true + user: user.toPublicUser(), + premium_type: user.premium_type, + profile_themes_experiment_bucket: 4, // TODO: This doesn't make it available, for some reason? + user_profile: userProfile, + guild_member: guild_member?.toPublicMember(), + guild_member_profile: guild_id && guildMemberProfile, + }); +}); + +router.patch("/", route({ requestBody: "UserProfileModifySchema" }), async (req: Request, res: Response) => { + const body = req.body as UserProfileModifySchema; + + if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string); + const user = await User.findOneOrFail({ + where: { id: req.user_id }, + select: [...PrivateUserProjection, "data"], + }); + + user.assign(body); + await user.save(); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + delete user.data; + + // TODO: send update member list event in gateway + await emitEvent({ + event: "USER_UPDATE", + user_id: req.user_id, + data: user, + } as UserUpdateEvent); + + res.json({ + accent_color: user.accent_color, + bio: user.bio, + banner: user.banner, + theme_colors: user.theme_colors, + pronouns: user.pronouns, + }); +}); export default router; diff --git a/src/api/routes/users/#id/relationships.ts b/src/api/routes/users/#id/relationships.ts index 3737ca00..c4c128a8 100644 --- a/src/api/routes/users/#id/relationships.ts +++ b/src/api/routes/users/#id/relationships.ts @@ -46,11 +46,7 @@ router.get( 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 - ) { + if (rmem.to_id === smem.to_id && rmem.type === 1 && rmem.to_id !== req.user_id) { const relation_user = await User.getPublicUser(rmem.to_id); mutual_relations.push({ @@ -64,7 +60,7 @@ router.get( } res.json(mutual_relations); - }, + } ); export default router; diff --git a/src/api/routes/users/@me/channels.ts b/src/api/routes/users/@me/channels.ts index 8a8fadd9..ee7fefa7 100644 --- a/src/api/routes/users/@me/channels.ts +++ b/src/api/routes/users/@me/channels.ts @@ -17,12 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Channel, - DmChannelCreateSchema, - DmChannelDTO, - Recipient, -} from "@spacebar/util"; +import { Channel, DmChannelCreateSchema, DmChannelDTO, Recipient } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router: Router = Router(); @@ -41,14 +36,8 @@ router.get( where: { user_id: req.user_id, closed: false }, relations: ["channel", "channel.recipients"], }); - res.json( - await Promise.all( - recipients.map((r) => - DmChannelDTO.from(r.channel, [req.user_id]), - ), - ), - ); - }, + res.json(await Promise.all(recipients.map((r) => DmChannelDTO.from(r.channel, [req.user_id])))); + } ); router.post( @@ -63,14 +52,8 @@ router.post( }), async (req: Request, res: Response) => { const body = req.body as DmChannelCreateSchema; - res.json( - await Channel.createDMChannel( - body.recipients, - req.user_id, - body.name, - ), - ); - }, + res.json(await Channel.createDMChannel(body.recipients, req.user_id, body.name)); + } ); export default router; diff --git a/src/api/routes/users/@me/connections/#connection_name/#connection_id/access-token.ts b/src/api/routes/users/@me/connections/#connection_name/#connection_id/access-token.ts index 789a7878..97c03efd 100644 --- a/src/api/routes/users/@me/connections/#connection_name/#connection_id/access-token.ts +++ b/src/api/routes/users/@me/connections/#connection_name/#connection_id/access-token.ts @@ -77,18 +77,13 @@ router.get("/", route({}), async (req: Request, res: Response) => { }); if (!connectedAccount) throw DiscordApiErrors.UNKNOWN_CONNECTION; if (connectedAccount.revoked) throw DiscordApiErrors.CONNECTION_REVOKED; - if (!connectedAccount.token_data) - throw new ApiError("No token data", 0, 400); + if (!connectedAccount.token_data) throw new ApiError("No token data", 0, 400); let access_token = connectedAccount.token_data.access_token; const { expires_at, expires_in, fetched_at } = connectedAccount.token_data; - if ( - (expires_at && expires_at < Date.now()) || - (expires_in && fetched_at + expires_in * 1000 < Date.now()) - ) { - if (!(connection instanceof RefreshableConnection)) - throw new ApiError("Access token expired", 0, 400); + if ((expires_at && expires_at < Date.now()) || (expires_in && fetched_at + expires_in * 1000 < Date.now())) { + if (!(connection instanceof RefreshableConnection)) throw new ApiError("Access token expired", 0, 400); const tokenData = await connection.refresh(connectedAccount); access_token = tokenData.access_token; } diff --git a/src/api/routes/users/@me/connections/#connection_name/#connection_id/index.ts b/src/api/routes/users/@me/connections/#connection_name/#connection_id/index.ts index 351ec99a..afb5b2e9 100644 --- a/src/api/routes/users/@me/connections/#connection_name/#connection_id/index.ts +++ b/src/api/routes/users/@me/connections/#connection_name/#connection_id/index.ts @@ -17,68 +17,59 @@ */ import { route } from "@spacebar/api"; -import { - ConnectedAccount, - ConnectionUpdateSchema, - DiscordApiErrors, - emitEvent, -} from "@spacebar/util"; +import { ConnectedAccount, ConnectionUpdateSchema, DiscordApiErrors, emitEvent } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); // TODO: connection update schema -router.patch( - "/", - route({ requestBody: "ConnectionUpdateSchema" }), - async (req: Request, res: Response) => { - const { connection_name, connection_id } = req.params; - const body = req.body as ConnectionUpdateSchema; +router.patch("/", route({ requestBody: "ConnectionUpdateSchema" }), async (req: Request, res: Response) => { + const { connection_name, connection_id } = req.params; + const body = req.body as ConnectionUpdateSchema; - const connection = await ConnectedAccount.findOne({ - where: { - user_id: req.user_id, - external_id: connection_id, - type: connection_name, - }, - select: [ - "external_id", - "type", - "name", - "verified", - "visibility", - "show_activity", - "revoked", - "friend_sync", - "integrations", - ], - }); + const connection = await ConnectedAccount.findOne({ + where: { + user_id: req.user_id, + external_id: connection_id, + type: connection_name, + }, + select: [ + "external_id", + "type", + "name", + "verified", + "visibility", + "show_activity", + "revoked", + "friend_sync", + "integrations", + ], + }); - if (!connection) return DiscordApiErrors.UNKNOWN_CONNECTION; - // TODO: do we need to do anything if the connection is revoked? + if (!connection) return DiscordApiErrors.UNKNOWN_CONNECTION; + // TODO: do we need to do anything if the connection is revoked? - if (typeof body.visibility === "boolean") - //@ts-expect-error For some reason the client sends this as a boolean, even tho docs say its a number? - body.visibility = body.visibility ? 1 : 0; - if (typeof body.show_activity === "boolean") - //@ts-expect-error For some reason the client sends this as a boolean, even tho docs say its a number? - body.show_activity = body.show_activity ? 1 : 0; - if (typeof body.metadata_visibility === "boolean") - //@ts-expect-error For some reason the client sends this as a boolean, even tho docs say its a number? - body.metadata_visibility = body.metadata_visibility ? 1 : 0; + if (typeof body.visibility === "boolean") + //@ts-expect-error For some reason the client sends this as a boolean, even tho docs say its a number? + body.visibility = body.visibility ? 1 : 0; + if (typeof body.show_activity === "boolean") + //@ts-expect-error For some reason the client sends this as a boolean, even tho docs say its a number? + body.show_activity = body.show_activity ? 1 : 0; + if (typeof body.metadata_visibility === "boolean") + //@ts-expect-error For some reason the client sends this as a boolean, even tho docs say its a number? + body.metadata_visibility = body.metadata_visibility ? 1 : 0; - connection.assign(req.body); + connection.assign(req.body); - await ConnectedAccount.update( - { - user_id: req.user_id, - external_id: connection_id, - type: connection_name, - }, - connection, - ); - res.json(connection.toJSON()); - }, -); + await ConnectedAccount.update( + { + user_id: req.user_id, + external_id: connection_id, + type: connection_name, + }, + connection + ); + res.json(connection.toJSON()); +}); router.delete("/", route({}), async (req: Request, res: Response) => { const { connection_name, connection_id } = req.params; diff --git a/src/api/routes/users/@me/delete.ts b/src/api/routes/users/@me/delete.ts index e36a1e92..a8e043f6 100644 --- a/src/api/routes/users/@me/delete.ts +++ b/src/api/routes/users/@me/delete.ts @@ -46,10 +46,7 @@ router.post( if (user.data.hash) { // guest accounts can delete accounts without password - correctpass = await bcrypt.compare( - req.body.password, - user.data.hash, - ); + correctpass = await bcrypt.compare(req.body.password, user.data.hash); if (!correctpass) { throw new HTTPError(req.t("auth:login.INVALID_PASSWORD")); } @@ -58,16 +55,13 @@ router.post( // TODO: decrement guild member count if (correctpass) { - await Promise.all([ - User.delete({ id: req.user_id }), - Member.delete({ id: req.user_id }), - ]); + await Promise.all([User.delete({ id: req.user_id }), Member.delete({ id: req.user_id })]); res.sendStatus(204); } else { res.sendStatus(401); } - }, + } ); export default router; diff --git a/src/api/routes/users/@me/disable.ts b/src/api/routes/users/@me/disable.ts index b4d03e62..8d9e161c 100644 --- a/src/api/routes/users/@me/disable.ts +++ b/src/api/routes/users/@me/disable.ts @@ -45,10 +45,7 @@ router.post( if (user.data.hash) { // guest accounts can delete accounts without password - correctpass = await bcrypt.compare( - req.body.password, - user.data.hash, - ); //Not sure if user typed right password :/ + correctpass = await bcrypt.compare(req.body.password, user.data.hash); //Not sure if user typed right password :/ } if (correctpass) { @@ -61,7 +58,7 @@ router.post( code: 50018, }); } - }, + } ); export default router; diff --git a/src/api/routes/users/@me/guilds.ts b/src/api/routes/users/@me/guilds.ts index 0bce432b..1d2f79ce 100644 --- a/src/api/routes/users/@me/guilds.ts +++ b/src/api/routes/users/@me/guilds.ts @@ -17,15 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Config, - Guild, - GuildDeleteEvent, - GuildMemberRemoveEvent, - Member, - User, - emitEvent, -} from "@spacebar/util"; +import { Config, Guild, GuildDeleteEvent, GuildMemberRemoveEvent, Member, User, emitEvent } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; @@ -53,7 +45,7 @@ router.get( } res.json(guild); - }, + } ); // user send to leave a certain guild @@ -79,17 +71,9 @@ router.delete( }); if (!guild) throw new HTTPError("Guild doesn't exist", 404); - if (guild.owner_id === req.user_id) - throw new HTTPError("You can't leave your own guild", 400); - if ( - autoJoin.enabled && - autoJoin.guilds.includes(guild_id) && - !autoJoin.canLeave - ) { - throw new HTTPError( - "You can't leave instance auto join guilds", - 400, - ); + if (guild.owner_id === req.user_id) throw new HTTPError("You can't leave your own guild", 400); + if (autoJoin.enabled && autoJoin.guilds.includes(guild_id) && !autoJoin.canLeave) { + throw new HTTPError("You can't leave instance auto join guilds", 400); } await Promise.all([ @@ -115,7 +99,7 @@ router.delete( } as GuildMemberRemoveEvent); return res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/users/@me/guilds/#guild_id/settings.ts b/src/api/routes/users/@me/guilds/#guild_id/settings.ts index ac6586ce..ae5f3922 100644 --- a/src/api/routes/users/@me/guilds/#guild_id/settings.ts +++ b/src/api/routes/users/@me/guilds/#guild_id/settings.ts @@ -17,12 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - Channel, - Member, - OrmUtils, - UserGuildSettingsSchema, -} from "@spacebar/util"; +import { Channel, Member, OrmUtils, UserGuildSettingsSchema } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); @@ -42,7 +37,7 @@ router.get( select: ["settings"], }); return res.json(user.settings); - }, + } ); router.patch( @@ -76,7 +71,7 @@ router.patch( Member.update({ id: req.user_id, guild_id: req.params.guild_id }, user); res.json(user.settings); - }, + } ); export default router; diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts index ad11a428..606f33bb 100644 --- a/src/api/routes/users/@me/index.ts +++ b/src/api/routes/users/@me/index.ts @@ -47,9 +47,9 @@ router.get( await User.findOne({ select: PrivateUserProjection, where: { id: req.user_id }, - }), + }) ); - }, + } ); router.patch( @@ -79,23 +79,12 @@ router.patch( // Populated on password change let newToken: string | undefined; - if (body.avatar) - body.avatar = await handleFile( - `/avatars/${req.user_id}`, - body.avatar as string, - ); - if (body.banner) - body.banner = await handleFile( - `/banners/${req.user_id}`, - body.banner as string, - ); + if (body.avatar) body.avatar = await handleFile(`/avatars/${req.user_id}`, body.avatar as string); + if (body.banner) body.banner = await handleFile(`/banners/${req.user_id}`, body.banner as string); if (body.password) { if (user.data?.hash) { - const same_password = await bcrypt.compare( - body.password, - user.data.hash || "", - ); + const same_password = await bcrypt.compare(body.password, user.data.hash || ""); if (!same_password) { throw FieldErrors({ password: { @@ -199,7 +188,7 @@ router.patch( ...user, newToken, }); - }, + } ); export default router; diff --git a/src/api/routes/users/@me/mfa/codes-verification.ts b/src/api/routes/users/@me/mfa/codes-verification.ts index f71704a9..f7083b53 100644 --- a/src/api/routes/users/@me/mfa/codes-verification.ts +++ b/src/api/routes/users/@me/mfa/codes-verification.ts @@ -17,13 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - BackupCode, - CodesVerificationSchema, - DiscordApiErrors, - User, - generateMfaBackupCodes, -} from "@spacebar/util"; +import { BackupCode, CodesVerificationSchema, DiscordApiErrors, User, generateMfaBackupCodes } from "@spacebar/util"; import { Request, Response, Router } from "express"; const router = Router(); @@ -52,15 +46,11 @@ router.post( // Once that's done, this route can verify `key` // const user = await User.findOneOrFail({ where: { id: req.user_id } }); - if ((await User.count({ where: { id: req.user_id } })) === 0) - throw DiscordApiErrors.UNKNOWN_USER; + if ((await User.count({ where: { id: req.user_id } })) === 0) throw DiscordApiErrors.UNKNOWN_USER; let codes: BackupCode[]; if (regenerate) { - 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())); @@ -78,7 +68,7 @@ router.post( return res.json({ backup_codes: codes.map((x) => ({ ...x, expired: undefined })), }); - }, + } ); export default router; diff --git a/src/api/routes/users/@me/mfa/codes.ts b/src/api/routes/users/@me/mfa/codes.ts index f9cfc4c4..d6e5a481 100644 --- a/src/api/routes/users/@me/mfa/codes.ts +++ b/src/api/routes/users/@me/mfa/codes.ts @@ -17,13 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - BackupCode, - FieldErrors, - generateMfaBackupCodes, - MfaCodesSchema, - User, -} from "@spacebar/util"; +import { BackupCode, FieldErrors, generateMfaBackupCodes, MfaCodesSchema, User } from "@spacebar/util"; import bcrypt from "bcrypt"; import { Request, Response, Router } from "express"; @@ -36,8 +30,7 @@ router.post( route({ requestBody: "MfaCodesSchema", deprecated: true, - description: - "This route is replaced with users/@me/mfa/codes-verification in newer clients", + description: "This route is replaced with users/@me/mfa/codes-verification in newer clients", responses: { 200: { body: "APIBackupCodeArray", @@ -69,10 +62,7 @@ router.post( let codes: BackupCode[]; if (regenerate) { - 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())); @@ -90,7 +80,7 @@ router.post( return res.json({ 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 362152d7..6a0960d8 100644 --- a/src/api/routes/users/@me/mfa/totp/disable.ts +++ b/src/api/routes/users/@me/mfa/totp/disable.ts @@ -17,12 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - BackupCode, - TotpDisableSchema, - User, - generateToken, -} from "@spacebar/util"; +import { BackupCode, TotpDisableSchema, User, generateToken } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; import { verifyToken } from "node-2fa"; @@ -53,11 +48,7 @@ router.post( 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( @@ -65,20 +56,20 @@ router.post( { mfa_enabled: false, totp_secret: "", - }, + } ); await BackupCode.update( { user: { id: req.user_id } }, { expired: true, - }, + } ); return res.json({ token: await generateToken(user.id), }); - }, + } ); 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 19836e4d..6d66fb95 100644 --- a/src/api/routes/users/@me/mfa/totp/enable.ts +++ b/src/api/routes/users/@me/mfa/totp/enable.ts @@ -17,12 +17,7 @@ */ import { route } from "@spacebar/api"; -import { - TotpEnableSchema, - User, - generateMfaBackupCodes, - generateToken, -} from "@spacebar/util"; +import { TotpEnableSchema, User, generateMfaBackupCodes, generateToken } from "@spacebar/util"; import bcrypt from "bcrypt"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; @@ -61,21 +56,16 @@ router.post( } } - 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); const backup_codes = generateMfaBackupCodes(req.user_id); 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), @@ -84,7 +74,7 @@ router.post( expired: undefined, })), }); - }, + } ); export default router; diff --git a/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts b/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts index 9cf42def..ba5df994 100644 --- a/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts +++ b/src/api/routes/users/@me/mfa/webauthn/credentials/#key_id/index.ts @@ -41,11 +41,10 @@ router.delete( }); // disable webauthn if there are no keys left - if (keys === 0) - await User.update({ id: req.user_id }, { webauthn_enabled: false }); + if (keys === 0) await User.update({ id: req.user_id }, { webauthn_enabled: false }); res.sendStatus(204); - }, + } ); export default router; diff --git a/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts b/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts index f383ffb7..b8e33da7 100644 --- a/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts +++ b/src/api/routes/users/@me/mfa/webauthn/credentials/index.ts @@ -35,15 +35,11 @@ import { ExpectedAttestationResult } from "fido2-lib"; import { HTTPError } from "lambert-server"; const router = Router(); -const isGenerateSchema = ( - body: WebAuthnPostSchema, -): body is GenerateWebAuthnCredentialsSchema => { +const isGenerateSchema = (body: WebAuthnPostSchema): body is GenerateWebAuthnCredentialsSchema => { return "password" in body; }; -const isCreateSchema = ( - body: WebAuthnPostSchema, -): body is CreateWebAuthnCredentialSchema => { +const isCreateSchema = (body: WebAuthnPostSchema): body is CreateWebAuthnCredentialSchema => { return "credential" in body; }; @@ -67,7 +63,7 @@ router.get("/", route({}), async (req: Request, res: Response) => { securityKeys.map((key) => ({ id: key.id, name: key.name, - })), + })) ); }); @@ -94,24 +90,13 @@ router.post( where: { id: req.user_id, }, - select: [ - "data", - "id", - "disabled", - "deleted", - "totp_secret", - "mfa_enabled", - "username", - ], + select: ["data", "id", "disabled", "deleted", "totp_secret", "mfa_enabled", "username"], relations: ["settings"], }); if (isGenerateSchema(req.body)) { const { password } = req.body; - const same_password = await bcrypt.compare( - password, - user.data.hash || "", - ); + const same_password = await bcrypt.compare(password, user.data.hash || ""); if (!same_password) { throw FieldErrors({ password: { @@ -121,14 +106,11 @@ router.post( }); } - const registrationOptions = - await WebAuthn.fido2.attestationOptions(); + const registrationOptions = await WebAuthn.fido2.attestationOptions(); const challenge = JSON.stringify({ publicKey: { ...registrationOptions, - challenge: Buffer.from( - registrationOptions.challenge, - ).toString("base64"), + challenge: Buffer.from(registrationOptions.challenge).toString("base64"), user: { id: user.id, name: user.username, @@ -151,35 +133,22 @@ router.post( const clientAttestationResponse = JSON.parse(credential); - if (!clientAttestationResponse.rawId) - throw new HTTPError("Missing rawId", 400); + if (!clientAttestationResponse.rawId) throw new HTTPError("Missing rawId", 400); - const rawIdBuffer = Buffer.from( - clientAttestationResponse.rawId, - "base64", - ); + const rawIdBuffer = Buffer.from(clientAttestationResponse.rawId, "base64"); clientAttestationResponse.rawId = toArrayBuffer(rawIdBuffer); - const attestationExpectations: ExpectedAttestationResult = - JSON.parse( - Buffer.from( - clientAttestationResponse.response.clientDataJSON, - "base64", - ).toString(), - ); - - const regResult = await WebAuthn.fido2.attestationResult( - clientAttestationResponse, - { - ...attestationExpectations, - factor: "second", - }, + const attestationExpectations: ExpectedAttestationResult = JSON.parse( + Buffer.from(clientAttestationResponse.response.clientDataJSON, "base64").toString() ); + const regResult = await WebAuthn.fido2.attestationResult(clientAttestationResponse, { + ...attestationExpectations, + factor: "second", + }); + const authnrData = regResult.authnrData; - const keyId = Buffer.from(authnrData.get("credId")).toString( - "base64", - ); + const keyId = Buffer.from(authnrData.get("credId")).toString("base64"); const counter = authnrData.get("counter"); const publicKey = authnrData.get("credentialPublicKeyPem"); @@ -191,10 +160,7 @@ router.post( key_id: keyId, }); - await Promise.all([ - securityKey.save(), - User.update({ id: req.user_id }, { webauthn_enabled: true }), - ]); + await Promise.all([securityKey.save(), User.update({ id: req.user_id }, { webauthn_enabled: true })]); return res.json({ name, @@ -203,7 +169,7 @@ router.post( } else { throw DiscordApiErrors.INVALID_AUTHENTICATION_TOKEN; } - }, + } ); export default router; diff --git a/src/api/routes/users/@me/notes.ts b/src/api/routes/users/@me/notes.ts index 248e61f9..51dcbd7f 100644 --- a/src/api/routes/users/@me/notes.ts +++ b/src/api/routes/users/@me/notes.ts @@ -49,7 +49,7 @@ router.get( note_user_id: id, user_id: req.user_id, }); - }, + } ); router.put( @@ -79,10 +79,7 @@ router.put( }, }) ) { - 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(), @@ -108,7 +105,7 @@ router.put( }); return res.status(204); - }, + } ); export default router; diff --git a/src/api/routes/users/@me/relationships.ts b/src/api/routes/users/@me/relationships.ts index bce0a654..9bee19a3 100644 --- a/src/api/routes/users/@me/relationships.ts +++ b/src/api/routes/users/@me/relationships.ts @@ -33,10 +33,7 @@ import { HTTPError } from "lambert-server"; const router = Router(); -const userProjection: (keyof User)[] = [ - "relationships", - ...PublicUserProjection, -]; +const userProjection: (keyof User)[] = ["relationships", ...PublicUserProjection]; router.get( "/", @@ -68,7 +65,7 @@ router.get( }); return res.json(related_users); - }, + } ); router.put( @@ -94,9 +91,9 @@ router.put( relations: ["relationships", "relationships.to"], select: userProjection, }), - req.body.type ?? RelationshipType.friends, + req.body.type ?? RelationshipType.friends ); - }, + } ); router.post( @@ -121,16 +118,13 @@ router.post( relations: ["relationships", "relationships.to"], select: userProjection, where: { - discriminator: String(req.body.discriminator).padStart( - 4, - "0", - ), //Discord send the discriminator as integer, we need to add leading zeroes + discriminator: String(req.body.discriminator).padStart(4, "0"), //Discord send the discriminator as integer, we need to add leading zeroes username: req.body.username, }, }), - req.body.type, + req.body.type ); - }, + } ); router.delete( @@ -148,8 +142,7 @@ router.delete( }), async (req: Request, res: Response) => { const { id } = req.params; - if (id === req.user_id) - throw new HTTPError("You can't remove yourself as a friend"); + if (id === req.user_id) throw new HTTPError("You can't remove yourself as a friend"); const user = await User.findOneOrFail({ where: { id: req.user_id }, @@ -163,12 +156,9 @@ router.delete( }); const relationship = user.relationships.find((x) => x.to_id === id); - const friendRequest = friend.relationships.find( - (x) => x.to_id === req.user_id, - ); + const friendRequest = friend.relationships.find((x) => x.to_id === req.user_id); - if (!relationship) - throw new HTTPError("You are not friends with the user", 404); + if (!relationship) throw new HTTPError("You are not friends with the user", 404); if (relationship?.type === RelationshipType.blocked) { // unblock user @@ -203,20 +193,14 @@ router.delete( ]); return res.sendStatus(204); - }, + } ); export default router; -async function updateRelationship( - req: Request, - res: Response, - friend: User, - type: RelationshipType, -) { +async function updateRelationship(req: Request, res: Response, friend: User, type: RelationshipType) { const id = friend.id; - if (id === req.user_id) - throw new HTTPError("You can't add yourself as a friend"); + if (id === req.user_id) throw new HTTPError("You can't add yourself as a friend"); const user = await User.findOneOrFail({ where: { id: req.user_id }, @@ -225,15 +209,12 @@ async function updateRelationship( }); let relationship = user.relationships.find((x) => x.to_id === id); - const friendRequest = friend.relationships.find( - (x) => x.to_id === req.user_id, - ); + const friendRequest = friend.relationships.find((x) => x.to_id === req.user_id); // TODO: you can add infinitely many blocked users (should this be prevented?) if (type === RelationshipType.blocked) { if (relationship) { - if (relationship.type === RelationshipType.blocked) - throw new HTTPError("You already blocked the user"); + if (relationship.type === RelationshipType.blocked) throw new HTTPError("You already blocked the user"); relationship.type = RelationshipType.blocked; await relationship.save(); } else { @@ -265,8 +246,7 @@ async function updateRelationship( } const { maxFriends } = Config.get().limits.user; - if (user.relationships.length >= maxFriends) - throw DiscordApiErrors.MAXIMUM_FRIENDS.withParams(maxFriends); + if (user.relationships.length >= maxFriends) throw DiscordApiErrors.MAXIMUM_FRIENDS.withParams(maxFriends); let incoming_relationship = Relationship.create({ nickname: undefined, @@ -282,8 +262,7 @@ async function updateRelationship( }); if (friendRequest) { - if (friendRequest.type === RelationshipType.blocked) - throw new HTTPError("The user blocked you"); + if (friendRequest.type === RelationshipType.blocked) throw new HTTPError("The user blocked you"); if (friendRequest.type === RelationshipType.friends) throw new HTTPError("You are already friends with the user"); // accept friend request @@ -292,12 +271,9 @@ async function updateRelationship( } if (relationship) { - if (relationship.type === RelationshipType.outgoing) - throw new HTTPError("You already sent a friend request"); + if (relationship.type === RelationshipType.outgoing) throw new HTTPError("You already sent a friend request"); if (relationship.type === RelationshipType.blocked) - throw new HTTPError( - "Unblock the user before sending a friend request", - ); + throw new HTTPError("Unblock the user before sending a friend request"); if (relationship.type === RelationshipType.friends) throw new HTTPError("You are already friends with the user"); outgoing_relationship = relationship; diff --git a/src/api/routes/users/@me/settings.ts b/src/api/routes/users/@me/settings.ts index d22d6de1..a6e2f92a 100644 --- a/src/api/routes/users/@me/settings.ts +++ b/src/api/routes/users/@me/settings.ts @@ -40,7 +40,7 @@ router.get( relations: ["settings"], }); return res.json(user.settings); - }, + } ); router.patch( @@ -73,7 +73,7 @@ router.patch( await user.settings.save(); res.json({ ...user.settings, index: undefined }); - }, + } ); export default router; diff --git a/src/api/routes/voice/regions.ts b/src/api/routes/voice/regions.ts index 10a8b21d..064b6021 100644 --- a/src/api/routes/voice/regions.ts +++ b/src/api/routes/voice/regions.ts @@ -32,7 +32,7 @@ router.get( }), async (req: Request, res: Response) => { res.json(await getVoiceRegions(getIpAdress(req), true)); //vip true? - }, + } ); export default router; diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index 6172a3d0..24e307ca 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -58,12 +58,9 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { where: { id: opts.channel_id }, relations: ["recipients"], }); - if (!channel || !opts.channel_id) - throw new HTTPError("Channel not found", 404); + if (!channel || !opts.channel_id) throw new HTTPError("Channel not found", 404); - const stickers = opts.sticker_ids - ? await Sticker.find({ where: { id: In(opts.sticker_ids) } }) - : undefined; + const stickers = opts.sticker_ids ? await Sticker.find({ where: { id: In(opts.sticker_ids) } }) : undefined; const message = Message.create({ ...opts, sticker_items: stickers, @@ -75,10 +72,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { type: opts.type ?? 0, }); - if ( - message.content && - message.content.length > Config.get().limits.message.maxCharacters - ) { + if (message.content && message.content.length > Config.get().limits.message.maxCharacters) { throw new HTTPError("Content length over max character limit"); } @@ -98,11 +92,7 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { }); } - const permission = await getPermission( - opts.author_id, - channel.guild_id, - opts.channel_id, - ); + const permission = await getPermission(opts.author_id, channel.guild_id, opts.channel_id); permission.hasThrow("SEND_MESSAGES"); if (permission.cache.member) { message.member = permission.cache.member; @@ -118,13 +108,9 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { }); 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", - ); + 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", - ); + throw new HTTPError("You can only reference messages from this channel"); } } /** Q: should be checked if the referenced message exists? ANSWER: NO @@ -162,27 +148,22 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> { }*/ for (const [, mention] of content.matchAll(USER_MENTION)) { - if (!mention_user_ids.includes(mention)) - mention_user_ids.push(mention); + if (!mention_user_ids.includes(mention)) mention_user_ids.push(mention); } await Promise.all( - Array.from(content.matchAll(ROLE_MENTION)).map( - async ([, mention]) => { - const role = await Role.findOneOrFail({ - where: { id: mention, guild_id: channel.guild_id }, - }); - if (role.mentionable || permission.has("MANAGE_ROLES")) { - mention_role_ids.push(mention); - } - }, - ), + Array.from(content.matchAll(ROLE_MENTION)).map(async ([, mention]) => { + const role = await Role.findOneOrFail({ + where: { id: mention, guild_id: channel.guild_id }, + }); + if (role.mentionable || permission.has("MANAGE_ROLES")) { + mention_role_ids.push(mention); + } + }) ); if (permission.has("MENTION_EVERYONE")) { - mention_everyone = - !!content.match(EVERYONE_MENTION) || - !!content.match(HERE_MENTION); + mention_everyone = !!content.match(EVERYONE_MENTION) || !!content.match(HERE_MENTION); } } @@ -222,8 +203,7 @@ export async function postHandleMessage(message: Message) { } // bit gross, but whatever! - const endpointPublic = - Config.get().cdn.endpointPublic || "http://127.0.0.1"; // lol + const endpointPublic = Config.get().cdn.endpointPublic || "http://127.0.0.1"; // lol const handler = url.hostname == new URL(endpointPublic).hostname ? EmbedHandlers["self"] @@ -262,10 +242,7 @@ export async function postHandleMessage(message: Message) { channel_id: message.channel_id, data, } as MessageUpdateEvent), - Message.update( - { id: message.id, channel_id: message.channel_id }, - { embeds: data.embeds }, - ), + Message.update({ id: message.id, channel_id: message.channel_id }, { embeds: data.embeds }), ...cachePromises, ]); } @@ -283,9 +260,7 @@ export async function sendMessage(opts: MessageOptions) { ]); // no await as it should catch error non-blockingly - postHandleMessage(message).catch((e) => - console.error("[Message] post-message handler failed", e), - ); + postHandleMessage(message).catch((e) => console.error("[Message] post-message handler failed", e)); return message; } diff --git a/src/api/util/handlers/Voice.ts b/src/api/util/handlers/Voice.ts index db06bd33..1407a180 100644 --- a/src/api/util/handlers/Voice.ts +++ b/src/api/util/handlers/Voice.ts @@ -21,9 +21,7 @@ import { distanceBetweenLocations, IPAnalysis } from "../utility/ipAddress"; export async function getVoiceRegions(ipAddress: string, vip: boolean) { const regions = Config.get().regions; - const availableRegions = regions.available.filter((ar) => - vip ? true : !ar.vip, - ); + const availableRegions = regions.available.filter((ar) => (vip ? true : !ar.vip)); let optimalId = regions.default; if (!regions.useDefaultAsOptimal) { @@ -33,10 +31,7 @@ export async function getVoiceRegions(ipAddress: string, vip: boolean) { for (const ar of availableRegions) { //TODO the endpoint location should be saved in the database if not already present to prevent IPAnalysis call - const dist = distanceBetweenLocations( - clientIpAnalysis, - ar.location || (await IPAnalysis(ar.endpoint)), - ); + const dist = distanceBetweenLocations(clientIpAnalysis, ar.location || (await IPAnalysis(ar.endpoint))); if (dist < min) { min = dist; diff --git a/src/api/util/handlers/route.ts b/src/api/util/handlers/route.ts index 5a0b48e6..9668b09b 100644 --- a/src/api/util/handlers/route.ts +++ b/src/api/util/handlers/route.ts @@ -84,24 +84,17 @@ export function route(opts: RouteOptions) { let validate: AnyValidateFunction | undefined; if (opts.requestBody) { validate = ajv.getSchema(opts.requestBody); - if (!validate) - throw new Error(`Body schema ${opts.requestBody} not found`); + if (!validate) throw new Error(`Body schema ${opts.requestBody} not found`); } return async (req: Request, res: Response, next: NextFunction) => { if (opts.permission) { const required = new Permissions(opts.permission); - req.permission = await getPermission( - req.user_id, - req.params.guild_id, - req.params.channel_id, - ); + req.permission = await getPermission(req.user_id, req.params.guild_id, req.params.channel_id); // bitfield comparison: check if user lacks certain permission if (!req.permission.has(required)) { - throw DiscordApiErrors.MISSING_PERMISSIONS.withParams( - opts.permission as string, - ); + throw DiscordApiErrors.MISSING_PERMISSIONS.withParams(opts.permission as string); } } @@ -110,25 +103,20 @@ export function route(opts: RouteOptions) { req.rights = await getRights(req.user_id); if (!req.rights || !req.rights.has(required)) { - throw SpacebarApiErrors.MISSING_RIGHTS.withParams( - opts.right as string, - ); + throw SpacebarApiErrors.MISSING_RIGHTS.withParams(opts.right as string); } } if (validate) { const valid = validate(normalizeBody(req.body)); if (!valid) { - const fields: Record< - string, - { code?: string; message: string } - > = {}; + const fields: Record<string, { code?: string; message: string }> = {}; validate.errors?.forEach( (x) => (fields[x.instancePath.slice(1)] = { code: x.keyword, message: x.message || "", - }), + }) ); throw FieldErrors(fields); } diff --git a/src/api/util/utility/Base64.ts b/src/api/util/utility/Base64.ts index c6d1257c..7426d22b 100644 --- a/src/api/util/utility/Base64.ts +++ b/src/api/util/utility/Base64.ts @@ -16,8 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -const alphabet = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"; +const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"; // binary to string lookup table const b2s = alphabet.split(""); diff --git a/src/api/util/utility/EmbedHandlers.ts b/src/api/util/utility/EmbedHandlers.ts index 0f1e88a5..78e56986 100644 --- a/src/api/util/utility/EmbedHandlers.ts +++ b/src/api/util/utility/EmbedHandlers.ts @@ -27,8 +27,7 @@ export const DEFAULT_FETCH_OPTIONS: RequestInit = { redirect: "follow", follow: 1, headers: { - "user-agent": - "Mozilla/5.0 (compatible; Spacebar/1.0; +https://github.com/spacebarchat/server)", + "user-agent": "Mozilla/5.0 (compatible; Spacebar/1.0; +https://github.com/spacebarchat/server)", }, // size: 1024 * 1024 * 5, // grabbed from config later compress: true, @@ -38,7 +37,7 @@ export const DEFAULT_FETCH_OPTIONS: RequestInit = { const makeEmbedImage = ( url: string | undefined, width: number | undefined, - height: number | undefined, + height: number | undefined ): Required<EmbedImage> | undefined => { if (!url || !width || !height) return undefined; return { @@ -51,13 +50,8 @@ const makeEmbedImage = ( let hasWarnedAboutImagor = false; -export const getProxyUrl = ( - url: URL, - width: number, - height: number, -): string => { - const { resizeWidthMax, resizeHeightMax, imagorServerUrl } = - Config.get().cdn; +export const getProxyUrl = (url: URL, width: number, height: number): string => { + const { resizeWidthMax, resizeHeightMax, imagorServerUrl } = Config.get().cdn; const secret = Config.get().security.requestSignature; width = Math.min(width || 500, resizeWidthMax || width); height = Math.min(height || 500, resizeHeightMax || width); @@ -81,8 +75,8 @@ export const getProxyUrl = ( console.log( "[Embeds]", yellow( - "Imagor has not been set up correctly. https://docs.spacebar.chat/setup/server/configuration/imagor/", - ), + "Imagor has not been set up correctly. https://docs.spacebar.chat/setup/server/configuration/imagor/" + ) ); } @@ -157,11 +151,7 @@ const genericImageHandler = async (url: URL): Promise<Embed | null> => { const response = await doFetch(url); if (!response) return null; const metas = getMetaDescriptions(await response.text()); - image = makeEmbedImage( - metas.image || metas.image_fallback, - metas.width, - metas.height, - ); + image = makeEmbedImage(metas.image || metas.image_fallback, metas.width, metas.height); } if (!image) return null; @@ -182,8 +172,7 @@ export const EmbedHandlers: { ...DEFAULT_FETCH_OPTIONS, method: "HEAD", }); - if (type.headers.get("content-type")?.indexOf("image") !== -1) - return await genericImageHandler(url); + if (type.headers.get("content-type")?.indexOf("image") !== -1) return await genericImageHandler(url); const response = await doFetch(url); if (!response) return null; @@ -275,9 +264,7 @@ export const EmbedHandlers: { const text = json.data.text; const created_at = new Date(json.data.created_at); const metrics = json.data.public_metrics; - const media = json.includes.media?.filter( - (x: { type: string }) => x.type == "photo", - ); + const media = json.includes.media?.filter((x: { type: string }) => x.type == "photo"); const embed: Embed = { type: EmbedType.rich, @@ -286,11 +273,7 @@ export const EmbedHandlers: { author: { url: `https://twitter.com/${author.username}`, name: `${author.name} (@${author.username})`, - proxy_icon_url: getProxyUrl( - new URL(author.profile_image_url), - 400, - 400, - ), + proxy_icon_url: getProxyUrl(new URL(author.profile_image_url), 400, 400), icon_url: author.profile_image_url, }, timestamp: created_at, @@ -310,14 +293,11 @@ export const EmbedHandlers: { footer: { text: "Twitter", proxy_icon_url: getProxyUrl( - new URL( - "https://abs.twimg.com/icons/apple-touch-icon-192x192.png", - ), - 192, + new URL("https://abs.twimg.com/icons/apple-touch-icon-192x192.png"), 192, + 192 ), - icon_url: - "https://abs.twimg.com/icons/apple-touch-icon-192x192.png", + icon_url: "https://abs.twimg.com/icons/apple-touch-icon-192x192.png", }, // Discord doesn't send this? // provider: { @@ -331,11 +311,7 @@ export const EmbedHandlers: { width: media[0].width, height: media[0].height, url: media[0].url, - proxy_url: getProxyUrl( - new URL(media[0].url), - media[0].width, - media[0].height, - ), + proxy_url: getProxyUrl(new URL(media[0].url), media[0].width, media[0].height), }; media.shift(); } @@ -388,11 +364,7 @@ export const EmbedHandlers: { type: EmbedType.image, title: metas.title, description: metas.description, - image: makeEmbedImage( - metas.image || metas.image_fallback, - metas.width, - metas.height, - ), + image: makeEmbedImage(metas.image || metas.image_fallback, metas.width, metas.height), provider: { url: "https://pixiv.net", name: "Pixiv", @@ -404,17 +376,9 @@ export const EmbedHandlers: { const response = await doFetch(url); if (!response) return null; const metas = getMetaDescriptions(await response.text()); - const numReviews = metas.$("#review_summary_num_reviews").val() as - | string - | undefined; - const price = metas - .$(".game_purchase_price.price") - .data("price-final") as number | undefined; - const releaseDate = metas - .$(".release_date") - .find("div.date") - .text() - .trim(); + const numReviews = metas.$("#review_summary_num_reviews").val() as string | undefined; + const price = metas.$(".game_purchase_price.price").data("price-final") as number | undefined; + const releaseDate = metas.$(".release_date").find("div.date").text().trim(); const isReleased = new Date(releaseDate) < new Date(); const fields: Embed["fields"] = []; @@ -452,9 +416,7 @@ export const EmbedHandlers: { width: 460, height: 215, url: metas.image, - proxy_url: metas.image - ? getProxyUrl(new URL(metas.image), 460, 215) - : undefined, + proxy_url: metas.image ? getProxyUrl(new URL(metas.image), 460, 215) : undefined, }, provider: { url: "https://store.steampowered.com", @@ -485,19 +447,11 @@ export const EmbedHandlers: { const metas = getMetaDescriptions(await response.text()); return { - video: makeEmbedImage( - metas.youtube_embed, - metas.width, - metas.height, - ), + video: makeEmbedImage(metas.youtube_embed, metas.width, metas.height), url: url.href, type: metas.youtube_embed ? EmbedType.video : EmbedType.link, title: metas.title, - thumbnail: makeEmbedImage( - metas.image || metas.image_fallback, - metas.width, - metas.height, - ), + thumbnail: makeEmbedImage(metas.image || metas.image_fallback, metas.width, metas.height), provider: { url: "https://www.youtube.com", name: "YouTube", diff --git a/src/api/util/utility/RandomInviteID.ts b/src/api/util/utility/RandomInviteID.ts index 926750d3..f3b9197b 100644 --- a/src/api/util/utility/RandomInviteID.ts +++ b/src/api/util/utility/RandomInviteID.ts @@ -24,8 +24,7 @@ import crypto from "crypto"; export function random(length = 6) { // Declare all characters - const chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; // Pick characers randomly let str = ""; @@ -38,8 +37,7 @@ export function random(length = 6) { export function snowflakeBasedInvite() { // Declare all characters - const chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const base = BigInt(chars.length); let snowflake = Snowflake.generateWorkerProcess(); diff --git a/src/api/util/utility/String.ts b/src/api/util/utility/String.ts index eef69e39..e531b447 100644 --- a/src/api/util/utility/String.ts +++ b/src/api/util/utility/String.ts @@ -20,13 +20,7 @@ import { Request } from "express"; import { ntob } from "./Base64"; import { FieldErrors } from "@spacebar/util"; -export function checkLength( - str: string, - min: number, - max: number, - key: string, - req: Request, -) { +export function checkLength(str: string, min: number, max: number, key: string, req: Request) { if (str.length < min || str.length > max) { throw FieldErrors({ [key]: { diff --git a/src/api/util/utility/captcha.ts b/src/api/util/utility/captcha.ts index db1b7957..9326eeb8 100644 --- a/src/api/util/utility/captcha.ts +++ b/src/api/util/utility/captcha.ts @@ -49,7 +49,7 @@ export async function verifyCaptcha(response: string, ip?: string) { if (!service || !secret || !sitekey) throw new Error( - "CAPTCHA is not configured correctly. https://docs.spacebar.chat/setup/server/security/captcha/", + "CAPTCHA is not configured correctly. https://docs.spacebar.chat/setup/server/security/captcha/" ); const res = await fetch(verifyEndpoints[service], { diff --git a/src/api/util/utility/ipAddress.ts b/src/api/util/utility/ipAddress.ts index c51daf6c..731812d7 100644 --- a/src/api/util/utility/ipAddress.ts +++ b/src/api/util/utility/ipAddress.ts @@ -83,9 +83,7 @@ export async function IPAnalysis(ip: string): Promise<typeof exampleData> { const { ipdataApiKey } = Config.get().security; if (!ipdataApiKey) return { ...exampleData, ip }; - return ( - await fetch(`https://api.ipdata.co/${ip}?api-key=${ipdataApiKey}`) - ).json(); + return (await fetch(`https://api.ipdata.co/${ip}?api-key=${ipdataApiKey}`)).json(); } export function isProxy(data: typeof exampleData) { @@ -102,37 +100,20 @@ export function getIpAdress(req: Request): string { return ( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - req.headers[Config.get().security.forwardedFor] || - req.socket.remoteAddress + req.headers[Config.get().security.forwardedFor] || req.socket.remoteAddress ); } type Location = { latitude: number; longitude: number }; -export function distanceBetweenLocations( - loc1: Location, - loc2: Location, -): number { - return distanceBetweenCoords( - loc1.latitude, - loc1.longitude, - loc2.latitude, - loc2.longitude, - ); +export function distanceBetweenLocations(loc1: Location, loc2: Location): number { + return distanceBetweenCoords(loc1.latitude, loc1.longitude, loc2.latitude, loc2.longitude); } //Haversine function -function distanceBetweenCoords( - lat1: number, - lon1: number, - lat2: number, - lon2: number, -) { +function distanceBetweenCoords(lat1: number, lon1: number, lat2: number, lon2: number) { const p = 0.017453292519943295; // Math.PI / 180 const c = Math.cos; - const a = - 0.5 - - c((lat2 - lat1) * p) / 2 + - (c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p))) / 2; + const a = 0.5 - c((lat2 - lat1) * p) / 2 + (c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p))) / 2; return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km } diff --git a/src/api/util/utility/passwordStrength.ts b/src/api/util/utility/passwordStrength.ts index fd627fbf..2f3e8bb1 100644 --- a/src/api/util/utility/passwordStrength.ts +++ b/src/api/util/utility/passwordStrength.ts @@ -36,8 +36,7 @@ const reSYMBOLS = /[A-Z,a-z,0-9]/g; * Returns: 0 > pw > 1 */ export function checkPassword(password: string): number { - const { minLength, minNumbers, minUpperCase, minSymbols } = - Config.get().register.password; + const { minLength, minNumbers, minUpperCase, minSymbols } = Config.get().register.password; let strength = 0; // checks for total password len @@ -61,10 +60,7 @@ export function checkPassword(password: string): number { } // checks if password only consists of numbers or only consists of chars - if ( - password.length == password.count(reNUMBER) || - password.length === password.count(reUPPERCASELETTER) - ) { + if (password.length == password.count(reNUMBER) || password.length === password.count(reUPPERCASELETTER)) { strength = 0; } @@ -77,8 +73,6 @@ export function checkPassword(password: string): number { const 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); + strength += entropies.reduceRight((a: number, x: number) => a - x * Math.log2(x)) / Math.log2(password.length); return strength; } diff --git a/src/bundle/Server.ts b/src/bundle/Server.ts index d281120d..ba1715e1 100644 --- a/src/bundle/Server.ts +++ b/src/bundle/Server.ts @@ -51,9 +51,7 @@ async function main() { await Config.init(); await Sentry.init(app); - await new Promise((resolve) => - server.listen({ port }, () => resolve(undefined)), - ); + await new Promise((resolve) => server.listen({ port }, () => resolve(undefined))); await Promise.all([api.start(), cdn.start(), gateway.start()]); Sentry.errorHandler(app); diff --git a/src/bundle/start.ts b/src/bundle/start.ts index df50fd4c..beb4762d 100644 --- a/src/bundle/start.ts +++ b/src/bundle/start.ts @@ -49,21 +49,11 @@ if (cluster.isPrimary) { ███████║██║ ██║ ██║╚██████╗███████╗██████╔╝██║ ██║██║ ██║ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ - spacebar-server | ${yellow( - `Pre-release (${ - commit !== null - ? commit.slice(0, 7) - : "Unknown (Git cannot be found)" - })`, - )} + spacebar-server | ${yellow(`Pre-release (${commit !== null ? commit.slice(0, 7) : "Unknown (Git cannot be found)"})`)} -Commit Hash: ${ - commit !== null - ? `${cyan(commit)} (${yellow(commit.slice(0, 7))})` - : "Unknown (Git cannot be found)" - } +Commit Hash: ${commit !== null ? `${cyan(commit)} (${yellow(commit.slice(0, 7))})` : "Unknown (Git cannot be found)"} Cores: ${cyan(os.cpus().length)} (Using ${cores} thread(s).) -`), +`) ); if (commit == null) { @@ -98,11 +88,7 @@ Cores: ${cyan(os.cpus().length)} (Using ${cores} thread(s).) }); cluster.on("exit", (worker) => { - console.log( - `[Worker] ${red( - `died with PID: ${worker.process.pid} , restarting ...`, - )}`, - ); + console.log(`[Worker] ${red(`died with PID: ${worker.process.pid} , restarting ...`)}`); cluster.fork(); }); } diff --git a/src/bundle/stats.ts b/src/bundle/stats.ts index b690eb75..707b77ee 100644 --- a/src/bundle/stats.ts +++ b/src/bundle/stats.ts @@ -34,8 +34,8 @@ export function initStats() { console.warn( red( `[Process] Warning Spacebar is running as root, this highly discouraged and might expose your system vulnerable to attackers.` + - `Please run Spacebar as a user without root privileges.`, - ), + `Please run Spacebar as a user without root privileges.` + ) ); } diff --git a/src/cdn/Server.ts b/src/cdn/Server.ts index 255452a0..7cead16d 100644 --- a/src/cdn/Server.ts +++ b/src/cdn/Server.ts @@ -43,16 +43,10 @@ export class CDNServer extends Server { // TODO: use better CSP policy res.set( "Content-security-policy", - "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';", - ); - res.set( - "Access-Control-Allow-Headers", - req.header("Access-Control-Request-Headers") || "*", - ); - res.set( - "Access-Control-Allow-Methods", - req.header("Access-Control-Request-Methods") || "*", + "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';" ); + res.set("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers") || "*"); + res.set("Access-Control-Allow-Methods", req.header("Access-Control-Request-Methods") || "*"); next(); }); this.app.use(bodyParser.json({ inflate: true, limit: "10mb" })); @@ -95,16 +89,10 @@ export class CDNServer extends Server { this.app.use("/channel-icons/", avatarsRoute); this.log("verbose", "[Server] Route /channel-icons registered"); - this.app.use( - "/guilds/:guild_id/users/:user_id/avatars", - guildProfilesRoute, - ); + this.app.use("/guilds/:guild_id/users/:user_id/avatars", guildProfilesRoute); this.log("verbose", "[Server] Route /guilds/avatars registered"); - this.app.use( - "/guilds/:guild_id/users/:user_id/banners", - guildProfilesRoute, - ); + this.app.use("/guilds/:guild_id/users/:user_id/banners", guildProfilesRoute); this.log("verbose", "[Server] Route /guilds/banners registered"); Sentry.errorHandler(this.app); diff --git a/src/cdn/routes/attachments.ts b/src/cdn/routes/attachments.ts index 19bb0b90..3db41da6 100644 --- a/src/cdn/routes/attachments.ts +++ b/src/cdn/routes/attachments.ts @@ -26,93 +26,75 @@ import imageSize from "image-size"; const router = Router(); -const SANITIZED_CONTENT_TYPE = [ - "text/html", - "text/mhtml", - "multipart/related", - "application/xhtml+xml", -]; - -router.post( - "/:channel_id", - multer.single("file"), - async (req: Request, res: Response) => { - if (req.headers.signature !== Config.get().security.requestSignature) - throw new HTTPError("Invalid request signature"); - if (!req.file) throw new HTTPError("file missing"); - - const { buffer, mimetype, size, originalname } = req.file; - const { channel_id } = req.params; - const filename = originalname - .replaceAll(" ", "_") - .replace(/[^a-zA-Z0-9._]+/g, ""); - const id = Snowflake.generate(); - const path = `attachments/${channel_id}/${id}/${filename}`; - - const endpoint = - Config.get()?.cdn.endpointPublic || "http://localhost:3001"; - - await storage.set(path, buffer); - let width; - let height; - if (mimetype.includes("image")) { - const dimensions = imageSize(buffer); - if (dimensions) { - width = dimensions.width; - height = dimensions.height; - } +const SANITIZED_CONTENT_TYPE = ["text/html", "text/mhtml", "multipart/related", "application/xhtml+xml"]; + +router.post("/:channel_id", multer.single("file"), async (req: Request, res: Response) => { + if (req.headers.signature !== Config.get().security.requestSignature) + throw new HTTPError("Invalid request signature"); + if (!req.file) throw new HTTPError("file missing"); + + const { buffer, mimetype, size, originalname } = req.file; + const { channel_id } = req.params; + const filename = originalname.replaceAll(" ", "_").replace(/[^a-zA-Z0-9._]+/g, ""); + const id = Snowflake.generate(); + const path = `attachments/${channel_id}/${id}/${filename}`; + + const endpoint = Config.get()?.cdn.endpointPublic || "http://localhost:3001"; + + await storage.set(path, buffer); + let width; + let height; + if (mimetype.includes("image")) { + const dimensions = imageSize(buffer); + if (dimensions) { + width = dimensions.width; + height = dimensions.height; } + } - const file = { - id, - content_type: mimetype, - filename: filename, - size, - url: `${endpoint}/${path}`, - width, - height, - }; - - return res.json(file); - }, -); - -router.get( - "/:channel_id/:id/:filename", - async (req: Request, res: Response) => { - const { channel_id, id, filename } = req.params; - // const { format } = req.query; - - const path = `attachments/${channel_id}/${id}/${filename}`; - const file = await storage.get(path); - if (!file) throw new HTTPError("File not found"); - const type = await FileType.fromBuffer(file); - let content_type = type?.mime || "application/octet-stream"; - - if (SANITIZED_CONTENT_TYPE.includes(content_type)) { - content_type = "application/octet-stream"; - } + const file = { + id, + content_type: mimetype, + filename: filename, + size, + url: `${endpoint}/${path}`, + width, + height, + }; + + return res.json(file); +}); + +router.get("/:channel_id/:id/:filename", async (req: Request, res: Response) => { + const { channel_id, id, filename } = req.params; + // const { format } = req.query; + + const path = `attachments/${channel_id}/${id}/${filename}`; + const file = await storage.get(path); + if (!file) throw new HTTPError("File not found"); + const type = await FileType.fromBuffer(file); + let content_type = type?.mime || "application/octet-stream"; + + if (SANITIZED_CONTENT_TYPE.includes(content_type)) { + content_type = "application/octet-stream"; + } - res.set("Content-Type", content_type); - res.set("Cache-Control", "public, max-age=31536000"); + res.set("Content-Type", content_type); + res.set("Cache-Control", "public, max-age=31536000"); - return res.send(file); - }, -); + return res.send(file); +}); -router.delete( - "/:channel_id/:id/:filename", - async (req: Request, res: Response) => { - if (req.headers.signature !== Config.get().security.requestSignature) - throw new HTTPError("Invalid request signature"); +router.delete("/:channel_id/:id/:filename", async (req: Request, res: Response) => { + if (req.headers.signature !== Config.get().security.requestSignature) + throw new HTTPError("Invalid request signature"); - const { channel_id, id, filename } = req.params; - const path = `attachments/${channel_id}/${id}/${filename}`; + const { channel_id, id, filename } = req.params; + const path = `attachments/${channel_id}/${id}/${filename}`; - await storage.delete(path); + await storage.delete(path); - return res.send({ success: true }); - }, -); + return res.send({ success: true }); +}); export default router; diff --git a/src/cdn/routes/avatars.ts b/src/cdn/routes/avatars.ts index 6af3243f..0887f9c7 100644 --- a/src/cdn/routes/avatars.ts +++ b/src/cdn/routes/avatars.ts @@ -30,51 +30,36 @@ import { multer } from "../util/multer"; // TODO: delete old icons const ANIMATED_MIME_TYPES = ["image/apng", "image/gif", "image/gifv"]; -const STATIC_MIME_TYPES = [ - "image/png", - "image/jpeg", - "image/webp", - "image/svg+xml", - "image/svg", -]; +const STATIC_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/svg+xml", "image/svg"]; const ALLOWED_MIME_TYPES = [...ANIMATED_MIME_TYPES, ...STATIC_MIME_TYPES]; const router = Router(); -router.post( - "/:user_id", - multer.single("file"), - async (req: Request, res: Response) => { - if (req.headers.signature !== Config.get().security.requestSignature) - throw new HTTPError("Invalid request signature"); - if (!req.file) throw new HTTPError("Missing file"); - const { buffer, size } = req.file; - const { user_id } = req.params; - - let hash = crypto - .createHash("md5") - .update(Snowflake.generate()) - .digest("hex"); - - const type = await FileType.fromBuffer(buffer); - if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) - throw new HTTPError("Invalid file type"); - if (ANIMATED_MIME_TYPES.includes(type.mime)) hash = `a_${hash}`; // animated icons have a_ infront of the hash - - const path = `avatars/${user_id}/${hash}`; - const endpoint = - Config.get().cdn.endpointPublic || "http://localhost:3001"; - - await storage.set(path, buffer); - - return res.json({ - id: hash, - content_type: type.mime, - size, - url: `${endpoint}${req.baseUrl}/${user_id}/${hash}`, - }); - }, -); +router.post("/:user_id", multer.single("file"), async (req: Request, res: Response) => { + if (req.headers.signature !== Config.get().security.requestSignature) + throw new HTTPError("Invalid request signature"); + if (!req.file) throw new HTTPError("Missing file"); + const { buffer, size } = req.file; + const { user_id } = req.params; + + let hash = crypto.createHash("md5").update(Snowflake.generate()).digest("hex"); + + const type = await FileType.fromBuffer(buffer); + if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) throw new HTTPError("Invalid file type"); + if (ANIMATED_MIME_TYPES.includes(type.mime)) hash = `a_${hash}`; // animated icons have a_ infront of the hash + + const path = `avatars/${user_id}/${hash}`; + const endpoint = Config.get().cdn.endpointPublic || "http://localhost:3001"; + + await storage.set(path, buffer); + + return res.json({ + id: hash, + content_type: type.mime, + size, + url: `${endpoint}${req.baseUrl}/${user_id}/${hash}`, + }); +}); router.get("/:user_id", async (req: Request, res: Response) => { let { user_id } = req.params; diff --git a/src/cdn/routes/guild-profiles.ts b/src/cdn/routes/guild-profiles.ts index 1ee5eeca..01377b9d 100644 --- a/src/cdn/routes/guild-profiles.ts +++ b/src/cdn/routes/guild-profiles.ts @@ -30,13 +30,7 @@ import { storage } from "../util/Storage"; // TODO: delete old icons const ANIMATED_MIME_TYPES = ["image/apng", "image/gif", "image/gifv"]; -const STATIC_MIME_TYPES = [ - "image/png", - "image/jpeg", - "image/webp", - "image/svg+xml", - "image/svg", -]; +const STATIC_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/svg+xml", "image/svg"]; const ALLOWED_MIME_TYPES = [...ANIMATED_MIME_TYPES, ...STATIC_MIME_TYPES]; const router = Router(); @@ -48,14 +42,10 @@ router.post("/", multer.single("file"), async (req: Request, res: Response) => { const { buffer, size } = req.file; const { guild_id, user_id } = req.params; - let hash = crypto - .createHash("md5") - .update(Snowflake.generate()) - .digest("hex"); + let hash = crypto.createHash("md5").update(Snowflake.generate()).digest("hex"); const type = await FileType.fromBuffer(buffer); - if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) - throw new HTTPError("Invalid file type"); + if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) throw new HTTPError("Invalid file type"); if (ANIMATED_MIME_TYPES.includes(type.mime)) hash = `a_${hash}`; // animated icons have a_ infront of the hash const path = `guilds/${guild_id}/users/${user_id}/avatars/${hash}`; diff --git a/src/cdn/routes/role-icons.ts b/src/cdn/routes/role-icons.ts index 8040405a..f599a154 100644 --- a/src/cdn/routes/role-icons.ts +++ b/src/cdn/routes/role-icons.ts @@ -30,50 +30,35 @@ import { multer } from "../util/multer"; // TODO: generate different sizes of icon // TODO: generate different image types of icon -const STATIC_MIME_TYPES = [ - "image/png", - "image/jpeg", - "image/webp", - "image/svg+xml", - "image/svg", -]; +const STATIC_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/svg+xml", "image/svg"]; const ALLOWED_MIME_TYPES = [...STATIC_MIME_TYPES]; const router = Router(); -router.post( - "/:role_id", - multer.single("file"), - async (req: Request, res: Response) => { - if (req.headers.signature !== Config.get().security.requestSignature) - throw new HTTPError("Invalid request signature"); - if (!req.file) throw new HTTPError("Missing file"); - const { buffer, size } = req.file; - const { role_id } = req.params; - - const hash = crypto - .createHash("md5") - .update(Snowflake.generate()) - .digest("hex"); - - const type = await FileType.fromBuffer(buffer); - if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) - throw new HTTPError("Invalid file type"); - - const path = `role-icons/${role_id}/${hash}.png`; - const endpoint = - Config.get().cdn.endpointPublic || "http://localhost:3001"; - - await storage.set(path, buffer); - - return res.json({ - id: hash, - content_type: type.mime, - size, - url: `${endpoint}${req.baseUrl}/${role_id}/${hash}`, - }); - }, -); +router.post("/:role_id", multer.single("file"), async (req: Request, res: Response) => { + if (req.headers.signature !== Config.get().security.requestSignature) + throw new HTTPError("Invalid request signature"); + if (!req.file) throw new HTTPError("Missing file"); + const { buffer, size } = req.file; + const { role_id } = req.params; + + const hash = crypto.createHash("md5").update(Snowflake.generate()).digest("hex"); + + const type = await FileType.fromBuffer(buffer); + if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) throw new HTTPError("Invalid file type"); + + const path = `role-icons/${role_id}/${hash}.png`; + const endpoint = Config.get().cdn.endpointPublic || "http://localhost:3001"; + + await storage.set(path, buffer); + + return res.json({ + id: hash, + content_type: type.mime, + size, + url: `${endpoint}${req.baseUrl}/${role_id}/${hash}`, + }); +}); router.get("/:role_id", async (req: Request, res: Response) => { const { role_id } = req.params; @@ -97,19 +82,10 @@ router.get("/:role_id/:hash", async (req: Request, res: Response) => { const role_icon_hash = hash.split(".")[0]; let file: Buffer | null = null; - const extensions_to_try = [ - requested_extension, - "png", - "jpg", - "jpeg", - "webp", - "svg", - ]; + const extensions_to_try = [requested_extension, "png", "jpg", "jpeg", "webp", "svg"]; for (let i = 0; i < extensions_to_try.length; i++) { - file = await storage.get( - `role-icons/${role_id}/${role_icon_hash}.${extensions_to_try[i]}`, - ); + file = await storage.get(`role-icons/${role_id}/${role_icon_hash}.${extensions_to_try[i]}`); if (file) break; } diff --git a/src/cdn/util/FileStorage.ts b/src/cdn/util/FileStorage.ts index 10b36743..5e53081b 100644 --- a/src/cdn/util/FileStorage.ts +++ b/src/cdn/util/FileStorage.ts @@ -30,8 +30,7 @@ function getPath(path: string) { const root = process.env.STORAGE_LOCATION || "../"; const filename = join(root, path); - if (path.indexOf("\0") !== -1 || !filename.startsWith(root)) - throw new Error("invalid path"); + if (path.indexOf("\0") !== -1 || !filename.startsWith(root)) throw new Error("invalid path"); return filename; } @@ -53,8 +52,7 @@ export class FileStorage implements Storage { async set(path: string, value: Buffer) { path = getPath(path); - if (!fs.existsSync(dirname(path))) - fs.mkdirSync(dirname(path), { recursive: true }); + if (!fs.existsSync(dirname(path))) fs.mkdirSync(dirname(path), { recursive: true }); const ret = Readable.from(value); const cleaned_file = fs.createWriteStream(path); diff --git a/src/cdn/util/S3Storage.ts b/src/cdn/util/S3Storage.ts index 81acd945..fd079ef0 100644 --- a/src/cdn/util/S3Storage.ts +++ b/src/cdn/util/S3Storage.ts @@ -29,11 +29,7 @@ const readableToBuffer = (readable: Readable): Promise<Buffer> => }); export class S3Storage implements Storage { - public constructor( - private client: S3, - private bucket: string, - private basePath?: string, - ) {} + public constructor(private client: S3, private bucket: string, private basePath?: string) {} /** * Always return a string, to ensure consistency. diff --git a/src/cdn/util/Storage.ts b/src/cdn/util/Storage.ts index 26289af6..609e38e9 100644 --- a/src/cdn/util/Storage.ts +++ b/src/cdn/util/Storage.ts @@ -49,16 +49,12 @@ if (process.env.STORAGE_PROVIDER === "file" || !process.env.STORAGE_PROVIDER) { bucket = process.env.STORAGE_BUCKET; if (!region) { - console.error( - `[CDN] You must provide a region when using the S3 storage provider.`, - ); + console.error(`[CDN] You must provide a region when using the S3 storage provider.`); process.exit(1); } if (!bucket) { - console.error( - `[CDN] You must provide a bucket when using the S3 storage provider.`, - ); + console.error(`[CDN] You must provide a bucket when using the S3 storage provider.`); process.exit(1); } @@ -66,9 +62,7 @@ if (process.env.STORAGE_PROVIDER === "file" || !process.env.STORAGE_PROVIDER) { let location = process.env.STORAGE_LOCATION; if (!location) { - console.warn( - `[CDN] STORAGE_LOCATION unconfigured for S3 provider, defaulting to the bucket root...`, - ); + console.warn(`[CDN] STORAGE_LOCATION unconfigured for S3 provider, defaulting to the bucket root...`); location = undefined; } diff --git a/src/connections/BattleNet/index.ts b/src/connections/BattleNet/index.ts index 4fdfccb1..446e0deb 100644 --- a/src/connections/BattleNet/index.ts +++ b/src/connections/BattleNet/index.ts @@ -47,11 +47,7 @@ export default class BattleNetConnection extends Connection { settings: BattleNetSettings = new BattleNetSettings(); init(): void { - const settings = - ConnectionLoader.getConnectionConfig<BattleNetSettings>( - this.id, - this.settings, - ); + const settings = ConnectionLoader.getConnectionConfig<BattleNetSettings>(this.id, this.settings); if (settings.enabled && (!settings.clientId || !settings.clientSecret)) throw new Error(`Invalid settings for connection ${this.id}`); @@ -73,10 +69,7 @@ export default class BattleNetConnection extends Connection { return this.tokenUrl; } - async exchangeCode( - state: string, - code: string, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + async exchangeCode(state: string, code: string): Promise<ConnectedAccountCommonOAuthTokenResponse> { this.validateState(state); const url = this.getTokenUrl(); @@ -92,7 +85,7 @@ export default class BattleNetConnection extends Connection { client_id: this.settings.clientId as string, client_secret: this.settings.clientSecret as string, redirect_uri: this.getRedirectUri(), - }), + }) ) .post() .json<ConnectedAccountCommonOAuthTokenResponse>() @@ -116,9 +109,7 @@ export default class BattleNetConnection extends Connection { }); } - async handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null> { + async handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null> { const { state, code } = params; if (!code) throw new Error("No code provided"); diff --git a/src/connections/Discord/index.ts b/src/connections/Discord/index.ts index 731086f1..2c5ada04 100644 --- a/src/connections/Discord/index.ts +++ b/src/connections/Discord/index.ts @@ -43,10 +43,7 @@ export default class DiscordConnection extends Connection { settings: DiscordSettings = new DiscordSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig<DiscordSettings>( - this.id, - this.settings, - ); + const settings = ConnectionLoader.getConnectionConfig<DiscordSettings>(this.id, this.settings); if (settings.enabled && (!settings.clientId || !settings.clientSecret)) throw new Error(`Invalid settings for connection ${this.id}`); @@ -71,10 +68,7 @@ export default class DiscordConnection extends Connection { return this.tokenUrl; } - async exchangeCode( - state: string, - code: string, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + async exchangeCode(state: string, code: string): Promise<ConnectedAccountCommonOAuthTokenResponse> { this.validateState(state); const url = this.getTokenUrl(); @@ -90,7 +84,7 @@ export default class DiscordConnection extends Connection { grant_type: "authorization_code", code: code, redirect_uri: this.getRedirectUri(), - }), + }) ) .post() .json<ConnectedAccountCommonOAuthTokenResponse>() @@ -114,9 +108,7 @@ export default class DiscordConnection extends Connection { }); } - async handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null> { + async handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null> { const { state, code } = params; if (!code) throw new Error("No code provided"); diff --git a/src/connections/EpicGames/index.ts b/src/connections/EpicGames/index.ts index e5b2d336..6c36b3e1 100644 --- a/src/connections/EpicGames/index.ts +++ b/src/connections/EpicGames/index.ts @@ -33,8 +33,7 @@ export interface UserResponse { preferredLanguage: string; } -export interface EpicTokenResponse - extends ConnectedAccountCommonOAuthTokenResponse { +export interface EpicTokenResponse extends ConnectedAccountCommonOAuthTokenResponse { expires_at: string; refresh_expires_in: number; refresh_expires_at: string; @@ -47,17 +46,12 @@ export default class EpicGamesConnection extends Connection { public readonly id = "epicgames"; public readonly authorizeUrl = "https://www.epicgames.com/id/authorize"; public readonly tokenUrl = "https://api.epicgames.dev/epic/oauth/v1/token"; - public readonly userInfoUrl = - "https://api.epicgames.dev/epic/id/v1/accounts"; + public readonly userInfoUrl = "https://api.epicgames.dev/epic/id/v1/accounts"; public readonly scopes = ["basic profile"]; settings: EpicGamesSettings = new EpicGamesSettings(); init(): void { - const settings = - ConnectionLoader.getConnectionConfig<EpicGamesSettings>( - this.id, - this.settings, - ); + const settings = ConnectionLoader.getConnectionConfig<EpicGamesSettings>(this.id, this.settings); if (settings.enabled && (!settings.clientId || !settings.clientSecret)) throw new Error(`Invalid settings for connection ${this.id}`); @@ -79,10 +73,7 @@ export default class EpicGamesConnection extends Connection { return this.tokenUrl; } - async exchangeCode( - state: string, - code: string, - ): Promise<EpicTokenResponse> { + async exchangeCode(state: string, code: string): Promise<EpicTokenResponse> { this.validateState(state); const url = this.getTokenUrl(); @@ -90,16 +81,16 @@ export default class EpicGamesConnection extends Connection { return wretch(url.toString()) .headers({ Accept: "application/json", - Authorization: `Basic ${Buffer.from( - `${this.settings.clientId}:${this.settings.clientSecret}`, - ).toString("base64")}`, + Authorization: `Basic ${Buffer.from(`${this.settings.clientId}:${this.settings.clientSecret}`).toString( + "base64" + )}`, "Content-Type": "application/x-www-form-urlencoded", }) .body( new URLSearchParams({ grant_type: "authorization_code", code, - }), + }) ) .post() .json<EpicTokenResponse>() @@ -110,9 +101,7 @@ export default class EpicGamesConnection extends Connection { } async getUser(token: string): Promise<UserResponse[]> { - const { sub } = JSON.parse( - Buffer.from(token.split(".")[1], "base64").toString("utf8"), - ); + const { sub } = JSON.parse(Buffer.from(token.split(".")[1], "base64").toString("utf8")); const url = new URL(this.userInfoUrl); url.searchParams.append("accountId", sub); @@ -128,9 +117,7 @@ export default class EpicGamesConnection extends Connection { }); } - async handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null> { + async handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null> { const { state, code } = params; if (!code) throw new Error("No code provided"); diff --git a/src/connections/Facebook/index.ts b/src/connections/Facebook/index.ts index 2bf26f34..bc2ecd25 100644 --- a/src/connections/Facebook/index.ts +++ b/src/connections/Facebook/index.ts @@ -43,19 +43,14 @@ interface UserResponse { export default class FacebookConnection extends Connection { public readonly id = "facebook"; - public readonly authorizeUrl = - "https://www.facebook.com/v14.0/dialog/oauth"; - public readonly tokenUrl = - "https://graph.facebook.com/v14.0/oauth/access_token"; + public readonly authorizeUrl = "https://www.facebook.com/v14.0/dialog/oauth"; + public readonly tokenUrl = "https://graph.facebook.com/v14.0/oauth/access_token"; public readonly userInfoUrl = "https://graph.facebook.com/v14.0/me"; public readonly scopes = ["public_profile"]; settings: FacebookSettings = new FacebookSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig<FacebookSettings>( - this.id, - this.settings, - ); + const settings = ConnectionLoader.getConnectionConfig<FacebookSettings>(this.id, this.settings); if (settings.enabled && (!settings.clientId || !settings.clientSecret)) throw new Error(`Invalid settings for connection ${this.id}`); @@ -77,19 +72,13 @@ export default class FacebookConnection extends Connection { getTokenUrl(code: string): string { const url = new URL(this.tokenUrl); url.searchParams.append("client_id", this.settings.clientId as string); - url.searchParams.append( - "client_secret", - this.settings.clientSecret as string, - ); + url.searchParams.append("client_secret", this.settings.clientSecret as string); url.searchParams.append("code", code); url.searchParams.append("redirect_uri", this.getRedirectUri()); return url.toString(); } - async exchangeCode( - state: string, - code: string, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + async exchangeCode(state: string, code: string): Promise<ConnectedAccountCommonOAuthTokenResponse> { this.validateState(state); const url = this.getTokenUrl(code); @@ -121,9 +110,7 @@ export default class FacebookConnection extends Connection { }); } - async handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null> { + async handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null> { const { state, code } = params; if (!code) throw new Error("No code provided"); diff --git a/src/connections/GitHub/index.ts b/src/connections/GitHub/index.ts index 25e5f89f..938b0686 100644 --- a/src/connections/GitHub/index.ts +++ b/src/connections/GitHub/index.ts @@ -42,10 +42,7 @@ export default class GitHubConnection extends Connection { settings: GitHubSettings = new GitHubSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig<GitHubSettings>( - this.id, - this.settings, - ); + const settings = ConnectionLoader.getConnectionConfig<GitHubSettings>(this.id, this.settings); if (settings.enabled && (!settings.clientId || !settings.clientSecret)) throw new Error(`Invalid settings for connection ${this.id}`); @@ -65,18 +62,12 @@ export default class GitHubConnection extends Connection { getTokenUrl(code: string): string { const url = new URL(this.tokenUrl); url.searchParams.append("client_id", this.settings.clientId as string); - url.searchParams.append( - "client_secret", - this.settings.clientSecret as string, - ); + url.searchParams.append("client_secret", this.settings.clientSecret as string); url.searchParams.append("code", code); return url.toString(); } - async exchangeCode( - state: string, - code: string, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + async exchangeCode(state: string, code: string): Promise<ConnectedAccountCommonOAuthTokenResponse> { this.validateState(state); const url = this.getTokenUrl(code); @@ -108,9 +99,7 @@ export default class GitHubConnection extends Connection { }); } - async handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null> { + async handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null> { const { state, code } = params; if (!code) throw new Error("No code provided"); diff --git a/src/connections/Reddit/index.ts b/src/connections/Reddit/index.ts index 149cce02..01ac34ed 100644 --- a/src/connections/Reddit/index.ts +++ b/src/connections/Reddit/index.ts @@ -54,10 +54,7 @@ export default class RedditConnection extends Connection { settings: RedditSettings = new RedditSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig<RedditSettings>( - this.id, - this.settings, - ); + const settings = ConnectionLoader.getConnectionConfig<RedditSettings>(this.id, this.settings); if (settings.enabled && (!settings.clientId || !settings.clientSecret)) throw new Error(`Invalid settings for connection ${this.id}`); @@ -79,10 +76,7 @@ export default class RedditConnection extends Connection { return this.tokenUrl; } - async exchangeCode( - state: string, - code: string, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + async exchangeCode(state: string, code: string): Promise<ConnectedAccountCommonOAuthTokenResponse> { this.validateState(state); const url = this.getTokenUrl(); @@ -90,9 +84,9 @@ export default class RedditConnection extends Connection { return wretch(url.toString()) .headers({ Accept: "application/json", - Authorization: `Basic ${Buffer.from( - `${this.settings.clientId}:${this.settings.clientSecret}`, - ).toString("base64")}`, + Authorization: `Basic ${Buffer.from(`${this.settings.clientId}:${this.settings.clientSecret}`).toString( + "base64" + )}`, "Content-Type": "application/x-www-form-urlencoded", }) .body( @@ -100,7 +94,7 @@ export default class RedditConnection extends Connection { grant_type: "authorization_code", code: code, redirect_uri: this.getRedirectUri(), - }), + }) ) .post() .json<ConnectedAccountCommonOAuthTokenResponse>() @@ -124,9 +118,7 @@ export default class RedditConnection extends Connection { }); } - async handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null> { + async handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null> { const { state, code } = params; if (!code) throw new Error("No code provided"); diff --git a/src/connections/Spotify/index.ts b/src/connections/Spotify/index.ts index ece404d8..6a456b44 100644 --- a/src/connections/Spotify/index.ts +++ b/src/connections/Spotify/index.ts @@ -63,10 +63,7 @@ export default class SpotifyConnection extends RefreshableConnection { * So to prevent spamming the spotify api we disable the ability to refresh. */ this.refreshEnabled = false; - const settings = ConnectionLoader.getConnectionConfig<SpotifySettings>( - this.id, - this.settings, - ); + const settings = ConnectionLoader.getConnectionConfig<SpotifySettings>(this.id, this.settings); if (settings.enabled && (!settings.clientId || !settings.clientSecret)) throw new Error(`Invalid settings for connection ${this.id}`); @@ -88,10 +85,7 @@ export default class SpotifyConnection extends RefreshableConnection { return this.tokenUrl; } - async exchangeCode( - state: string, - code: string, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + async exchangeCode(state: string, code: string): Promise<ConnectedAccountCommonOAuthTokenResponse> { this.validateState(state); const url = this.getTokenUrl(); @@ -101,9 +95,7 @@ export default class SpotifyConnection extends RefreshableConnection { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded", Authorization: `Basic ${Buffer.from( - `${this.settings.clientId as string}:${ - this.settings.clientSecret as string - }`, + `${this.settings.clientId as string}:${this.settings.clientSecret as string}` ).toString("base64")}`, }) .body( @@ -111,7 +103,7 @@ export default class SpotifyConnection extends RefreshableConnection { grant_type: "authorization_code", code: code, redirect_uri: this.getRedirectUri(), - }), + }) ) .post() .json<ConnectedAccountCommonOAuthTokenResponse>() @@ -121,11 +113,8 @@ export default class SpotifyConnection extends RefreshableConnection { }); } - async refreshToken( - connectedAccount: ConnectedAccount, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { - if (!connectedAccount.token_data?.refresh_token) - throw new Error("No refresh token available."); + async refreshToken(connectedAccount: ConnectedAccount): Promise<ConnectedAccountCommonOAuthTokenResponse> { + if (!connectedAccount.token_data?.refresh_token) throw new Error("No refresh token available."); const refresh_token = connectedAccount.token_data.refresh_token; const url = this.getTokenUrl(); @@ -134,16 +123,14 @@ export default class SpotifyConnection extends RefreshableConnection { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded", Authorization: `Basic ${Buffer.from( - `${this.settings.clientId as string}:${ - this.settings.clientSecret as string - }`, + `${this.settings.clientId as string}:${this.settings.clientSecret as string}` ).toString("base64")}`, }) .body( new URLSearchParams({ grant_type: "refresh_token", refresh_token, - }), + }) ) .post() .unauthorized(async () => { @@ -173,9 +160,7 @@ export default class SpotifyConnection extends RefreshableConnection { }); } - async handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null> { + async handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null> { const { state, code } = params; if (!code) throw new Error("No code provided"); diff --git a/src/connections/Twitch/index.ts b/src/connections/Twitch/index.ts index 9a6cea35..1cb62f9d 100644 --- a/src/connections/Twitch/index.ts +++ b/src/connections/Twitch/index.ts @@ -47,18 +47,11 @@ export default class TwitchConnection extends RefreshableConnection { public readonly authorizeUrl = "https://id.twitch.tv/oauth2/authorize"; public readonly tokenUrl = "https://id.twitch.tv/oauth2/token"; public readonly userInfoUrl = "https://api.twitch.tv/helix/users"; - public readonly scopes = [ - "channel_subscriptions", - "channel_check_subscription", - "channel:read:subscriptions", - ]; + public readonly scopes = ["channel_subscriptions", "channel_check_subscription", "channel:read:subscriptions"]; settings: TwitchSettings = new TwitchSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig<TwitchSettings>( - this.id, - this.settings, - ); + const settings = ConnectionLoader.getConnectionConfig<TwitchSettings>(this.id, this.settings); if (settings.enabled && (!settings.clientId || !settings.clientSecret)) throw new Error(`Invalid settings for connection ${this.id}`); @@ -80,10 +73,7 @@ export default class TwitchConnection extends RefreshableConnection { return this.tokenUrl; } - async exchangeCode( - state: string, - code: string, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + async exchangeCode(state: string, code: string): Promise<ConnectedAccountCommonOAuthTokenResponse> { this.validateState(state); const url = this.getTokenUrl(); @@ -100,7 +90,7 @@ export default class TwitchConnection extends RefreshableConnection { client_id: this.settings.clientId as string, client_secret: this.settings.clientSecret as string, redirect_uri: this.getRedirectUri(), - }), + }) ) .post() .json<ConnectedAccountCommonOAuthTokenResponse>() @@ -110,11 +100,8 @@ export default class TwitchConnection extends RefreshableConnection { }); } - async refreshToken( - connectedAccount: ConnectedAccount, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { - if (!connectedAccount.token_data?.refresh_token) - throw new Error("No refresh token available."); + async refreshToken(connectedAccount: ConnectedAccount): Promise<ConnectedAccountCommonOAuthTokenResponse> { + if (!connectedAccount.token_data?.refresh_token) throw new Error("No refresh token available."); const refresh_token = connectedAccount.token_data.refresh_token; const url = this.getTokenUrl(); @@ -130,7 +117,7 @@ export default class TwitchConnection extends RefreshableConnection { client_id: this.settings.clientId as string, client_secret: this.settings.clientSecret as string, refresh_token: refresh_token, - }), + }) ) .post() .unauthorized(async () => { @@ -161,9 +148,7 @@ export default class TwitchConnection extends RefreshableConnection { }); } - async handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null> { + async handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null> { const { state, code } = params; if (!code) throw new Error("No code provided"); diff --git a/src/connections/Twitter/index.ts b/src/connections/Twitter/index.ts index 62fd7da1..424fd455 100644 --- a/src/connections/Twitter/index.ts +++ b/src/connections/Twitter/index.ts @@ -55,10 +55,7 @@ export default class TwitterConnection extends RefreshableConnection { settings: TwitterSettings = new TwitterSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig<TwitterSettings>( - this.id, - this.settings, - ); + const settings = ConnectionLoader.getConnectionConfig<TwitterSettings>(this.id, this.settings); if (settings.enabled && (!settings.clientId || !settings.clientSecret)) throw new Error(`Invalid settings for connection ${this.id}`); @@ -82,10 +79,7 @@ export default class TwitterConnection extends RefreshableConnection { return this.tokenUrl; } - async exchangeCode( - state: string, - code: string, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + async exchangeCode(state: string, code: string): Promise<ConnectedAccountCommonOAuthTokenResponse> { this.validateState(state); const url = this.getTokenUrl(); @@ -95,9 +89,7 @@ export default class TwitterConnection extends RefreshableConnection { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded", Authorization: `Basic ${Buffer.from( - `${this.settings.clientId as string}:${ - this.settings.clientSecret as string - }`, + `${this.settings.clientId as string}:${this.settings.clientSecret as string}` ).toString("base64")}`, }) .body( @@ -107,7 +99,7 @@ export default class TwitterConnection extends RefreshableConnection { client_id: this.settings.clientId as string, redirect_uri: this.getRedirectUri(), code_verifier: "challenge", // TODO: properly use PKCE challenge - }), + }) ) .post() .json<ConnectedAccountCommonOAuthTokenResponse>() @@ -117,11 +109,8 @@ export default class TwitterConnection extends RefreshableConnection { }); } - async refreshToken( - connectedAccount: ConnectedAccount, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { - if (!connectedAccount.token_data?.refresh_token) - throw new Error("No refresh token available."); + async refreshToken(connectedAccount: ConnectedAccount): Promise<ConnectedAccountCommonOAuthTokenResponse> { + if (!connectedAccount.token_data?.refresh_token) throw new Error("No refresh token available."); const refresh_token = connectedAccount.token_data.refresh_token; const url = this.getTokenUrl(); @@ -131,9 +120,7 @@ export default class TwitterConnection extends RefreshableConnection { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded", Authorization: `Basic ${Buffer.from( - `${this.settings.clientId as string}:${ - this.settings.clientSecret as string - }`, + `${this.settings.clientId as string}:${this.settings.clientSecret as string}` ).toString("base64")}`, }) .body( @@ -143,7 +130,7 @@ export default class TwitterConnection extends RefreshableConnection { client_id: this.settings.clientId as string, redirect_uri: this.getRedirectUri(), code_verifier: "challenge", // TODO: properly use PKCE challenge - }), + }) ) .post() .json<ConnectedAccountCommonOAuthTokenResponse>() @@ -167,9 +154,7 @@ export default class TwitterConnection extends RefreshableConnection { }); } - async handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null> { + async handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null> { const { state, code } = params; if (!code) throw new Error("No code provided"); diff --git a/src/connections/Xbox/index.ts b/src/connections/Xbox/index.ts index 935ff7ab..9a091315 100644 --- a/src/connections/Xbox/index.ts +++ b/src/connections/Xbox/index.ts @@ -51,21 +51,15 @@ interface XboxUserResponse { export default class XboxConnection extends Connection { public readonly id = "xbox"; - public readonly authorizeUrl = - "https://login.live.com/oauth20_authorize.srf"; + public readonly authorizeUrl = "https://login.live.com/oauth20_authorize.srf"; public readonly tokenUrl = "https://login.live.com/oauth20_token.srf"; - public readonly userInfoUrl = - "https://xsts.auth.xboxlive.com/xsts/authorize"; - public readonly userAuthUrl = - "https://user.auth.xboxlive.com/user/authenticate"; + public readonly userInfoUrl = "https://xsts.auth.xboxlive.com/xsts/authorize"; + public readonly userAuthUrl = "https://user.auth.xboxlive.com/user/authenticate"; public readonly scopes = ["Xboxlive.signin", "Xboxlive.offline_access"]; settings: XboxSettings = new XboxSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig<XboxSettings>( - this.id, - this.settings, - ); + const settings = ConnectionLoader.getConnectionConfig<XboxSettings>(this.id, this.settings); if (settings.enabled && (!settings.clientId || !settings.clientSecret)) throw new Error(`Invalid settings for connection ${this.id}`); @@ -104,7 +98,7 @@ export default class XboxConnection extends Connection { SiteName: "user.auth.xboxlive.com", RpsTicket: `d=${token}`, }, - }), + }) ) .post() .json((res: XboxUserResponse) => res.Token) @@ -114,10 +108,7 @@ export default class XboxConnection extends Connection { }); } - async exchangeCode( - state: string, - code: string, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + async exchangeCode(state: string, code: string): Promise<ConnectedAccountCommonOAuthTokenResponse> { this.validateState(state); const url = this.getTokenUrl(); @@ -127,9 +118,7 @@ export default class XboxConnection extends Connection { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded", Authorization: `Basic ${Buffer.from( - `${this.settings.clientId as string}:${ - this.settings.clientSecret as string - }`, + `${this.settings.clientId as string}:${this.settings.clientSecret as string}` ).toString("base64")}`, }) .body( @@ -139,7 +128,7 @@ export default class XboxConnection extends Connection { client_id: this.settings.clientId as string, redirect_uri: this.getRedirectUri(), scope: this.scopes.join(" "), - }), + }) ) .post() .json<ConnectedAccountCommonOAuthTokenResponse>() @@ -166,7 +155,7 @@ export default class XboxConnection extends Connection { UserTokens: [token], SandboxId: "RETAIL", }, - }), + }) ) .post() .json<XboxUserResponse>() @@ -176,9 +165,7 @@ export default class XboxConnection extends Connection { }); } - async handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null> { + async handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null> { const { state, code } = params; if (!code) throw new Error("No code provided"); @@ -187,10 +174,7 @@ export default class XboxConnection extends Connection { const userToken = await this.getUserToken(tokenData.access_token); const userInfo = await this.getUser(userToken); - const exists = await this.hasConnection( - userId, - userInfo.DisplayClaims.xui[0].xid, - ); + const exists = await this.hasConnection(userId, userInfo.DisplayClaims.xui[0].xid); if (exists) return null; diff --git a/src/connections/Youtube/index.ts b/src/connections/Youtube/index.ts index 844803cf..9cf6f003 100644 --- a/src/connections/Youtube/index.ts +++ b/src/connections/Youtube/index.ts @@ -51,21 +51,14 @@ interface YouTubeConnectionChannelListResult { export default class YoutubeConnection extends Connection { public readonly id = "youtube"; - public readonly authorizeUrl = - "https://accounts.google.com/o/oauth2/v2/auth"; + public readonly authorizeUrl = "https://accounts.google.com/o/oauth2/v2/auth"; public readonly tokenUrl = "https://oauth2.googleapis.com/token"; - public readonly userInfoUrl = - "https://www.googleapis.com/youtube/v3/channels?mine=true&part=snippet"; - public readonly scopes = [ - "https://www.googleapis.com/auth/youtube.readonly", - ]; + public readonly userInfoUrl = "https://www.googleapis.com/youtube/v3/channels?mine=true&part=snippet"; + public readonly scopes = ["https://www.googleapis.com/auth/youtube.readonly"]; settings: YoutubeSettings = new YoutubeSettings(); init(): void { - const settings = ConnectionLoader.getConnectionConfig<YoutubeSettings>( - this.id, - this.settings, - ); + const settings = ConnectionLoader.getConnectionConfig<YoutubeSettings>(this.id, this.settings); if (settings.enabled && (!settings.clientId || !settings.clientSecret)) throw new Error(`Invalid settings for connection ${this.id}`); @@ -87,10 +80,7 @@ export default class YoutubeConnection extends Connection { return this.tokenUrl; } - async exchangeCode( - state: string, - code: string, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + async exchangeCode(state: string, code: string): Promise<ConnectedAccountCommonOAuthTokenResponse> { this.validateState(state); const url = this.getTokenUrl(); @@ -107,7 +97,7 @@ export default class YoutubeConnection extends Connection { client_id: this.settings.clientId as string, client_secret: this.settings.clientSecret as string, redirect_uri: this.getRedirectUri(), - }), + }) ) .post() .json<ConnectedAccountCommonOAuthTokenResponse>() @@ -131,9 +121,7 @@ export default class YoutubeConnection extends Connection { }); } - async handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null> { + async handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null> { const { state, code } = params; if (!code) throw new Error("No code provided"); diff --git a/src/gateway/Server.ts b/src/gateway/Server.ts index 9fba2d4c..5921e9b5 100644 --- a/src/gateway/Server.ts +++ b/src/gateway/Server.ts @@ -19,13 +19,7 @@ import "missing-native-js-functions"; import dotenv from "dotenv"; dotenv.config(); -import { - closeDatabase, - Config, - initDatabase, - initEvent, - Sentry, -} from "@spacebar/util"; +import { closeDatabase, Config, initDatabase, initEvent, Sentry } from "@spacebar/util"; import ws from "ws"; import { Connection } from "./events/Connection"; import http from "http"; @@ -36,15 +30,7 @@ export class Server { public server: http.Server; public production: boolean; - constructor({ - port, - server, - production, - }: { - port: number; - server?: http.Server; - production?: boolean; - }) { + constructor({ port, server, production }: { port: number; server?: http.Server; production?: boolean }) { this.port = port; this.production = production || false; diff --git a/src/gateway/events/Connection.ts b/src/gateway/events/Connection.ts index 1991ebbe..2150ec7b 100644 --- a/src/gateway/events/Connection.ts +++ b/src/gateway/events/Connection.ts @@ -40,15 +40,9 @@ try { // TODO: specify rate limit in config // TODO: check msg max size -export async function Connection( - this: WS.Server, - socket: WebSocket, - request: IncomingMessage, -) { +export async function Connection(this: WS.Server, socket: WebSocket, request: IncomingMessage) { const forwardedFor = Config.get().security.forwardedFor; - const ipAddress = forwardedFor - ? (request.headers[forwardedFor] as string) - : request.socket.remoteAddress; + const ipAddress = forwardedFor ? (request.headers[forwardedFor] as string) : request.socket.remoteAddress; socket.ipAddress = ipAddress; @@ -85,21 +79,17 @@ export async function Connection( const { searchParams } = new URL(`http://localhost${request.url}`); // @ts-ignore socket.encoding = searchParams.get("encoding") || "json"; - if (!["json", "etf"].includes(socket.encoding)) - return socket.close(CLOSECODES.Decode_error); + if (!["json", "etf"].includes(socket.encoding)) return socket.close(CLOSECODES.Decode_error); - if (socket.encoding === "etf" && !erlpack) - throw new Error("Erlpack is not installed: 'npm i erlpack'"); + if (socket.encoding === "etf" && !erlpack) throw new Error("Erlpack is not installed: 'npm i erlpack'"); socket.version = Number(searchParams.get("version")) || 8; - if (socket.version != 8) - return socket.close(CLOSECODES.Invalid_API_version); + if (socket.version != 8) return socket.close(CLOSECODES.Invalid_API_version); // @ts-ignore socket.compress = searchParams.get("compress") || ""; if (socket.compress) { - if (socket.compress !== "zlib-stream") - return socket.close(CLOSECODES.Decode_error); + if (socket.compress !== "zlib-stream") return socket.close(CLOSECODES.Decode_error); socket.deflate = new Deflate(); socket.inflate = new Inflate(); } diff --git a/src/gateway/events/Message.ts b/src/gateway/events/Message.ts index 52d9edd8..8415d04e 100644 --- a/src/gateway/events/Message.ts +++ b/src/gateway/events/Message.ts @@ -60,22 +60,15 @@ export async function Message(this: WebSocket, buffer: WS.Data) { } } else return this.close(CLOSECODES.Decode_error); - if (process.env.WS_VERBOSE) - console.log(`[Websocket] Incomming message: ${JSON.stringify(data)}`); + if (process.env.WS_VERBOSE) console.log(`[Websocket] Incomming message: ${JSON.stringify(data)}`); if (process.env.WS_DUMP) { const id = this.session_id || "unknown"; await fs.mkdir(path.join("dump", id), { recursive: true }); - await fs.writeFile( - path.join("dump", id, `${Date.now()}.in.json`), - JSON.stringify(data, null, 2), - ); + await fs.writeFile(path.join("dump", id, `${Date.now()}.in.json`), JSON.stringify(data, null, 2)); - if (!this.session_id) - console.log( - "[Gateway] Unknown session id, dumping to unknown folder", - ); + if (!this.session_id) console.log("[Gateway] Unknown session id, dumping to unknown folder"); } check.call(this, PayloadSchema, data); @@ -102,7 +95,7 @@ export async function Message(this: WebSocket, buffer: WS.Data) { const ret = await OPCodeHandler.call(this, data); Sentry.setUser({ id: this.user_id }); return ret; - }, + } ); } catch (error) { Sentry.captureException(error, { diff --git a/src/gateway/listener/listener.ts b/src/gateway/listener/listener.ts index b624b8e0..612f7313 100644 --- a/src/gateway/listener/listener.ts +++ b/src/gateway/listener/listener.ts @@ -42,10 +42,7 @@ import { Recipient } from "@spacebar/util"; // Sharding: calculate if the current shard id matches the formula: shard_id = (guild_id >> 22) % num_shards // https://discord.com/developers/docs/topics/gateway#sharding -export function handlePresenceUpdate( - this: WebSocket, - { event, acknowledge, data }: EventOpts, -) { +export function handlePresenceUpdate(this: WebSocket, { event, acknowledge, data }: EventOpts) { acknowledge?.(); if (event === EVENTEnum.PresenceUpdate) { return Send(this, { @@ -96,11 +93,7 @@ export async function setupListener(this: WebSocket) { this.events[this.user_id] = await listenEvent(this.user_id, consumer, opts); relationships.forEach(async (relationship) => { - this.events[relationship.to_id] = await listenEvent( - relationship.to_id, - handlePresenceUpdate.bind(this), - opts, - ); + this.events[relationship.to_id] = await listenEvent(relationship.to_id, handlePresenceUpdate.bind(this), opts); }); dm_channels.forEach(async (channel) => { @@ -113,16 +106,8 @@ export async function setupListener(this: WebSocket) { this.events[guild.id] = await listenEvent(guild.id, consumer, opts); guild.channels.forEach(async (channel) => { - if ( - permission - .overwriteChannel(channel.permission_overwrites ?? []) - .has("VIEW_CHANNEL") - ) { - this.events[channel.id] = await listenEvent( - channel.id, - consumer, - opts, - ); + if (permission.overwriteChannel(channel.permission_overwrites ?? []).has("VIEW_CHANNEL")) { + this.events[channel.id] = await listenEvent(channel.id, consumer, opts); } }); }); @@ -158,7 +143,7 @@ async function consume(this: WebSocket, opts: EventOpts) { this.member_events[data.user.id] = await listenEvent( data.user.id, handlePresenceUpdate.bind(this), - this.listen_options, + this.listen_options ); break; case "GUILD_MEMBER_UPDATE": @@ -172,11 +157,7 @@ async function consume(this: WebSocket, opts: EventOpts) { opts.cancel(); break; case "CHANNEL_CREATE": - if ( - !permission - .overwriteChannel(data.permission_overwrites) - .has("VIEW_CHANNEL") - ) { + if (!permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) { return; } this.events[id] = await listenEvent(id, consumer, listenOpts); @@ -185,7 +166,7 @@ async function consume(this: WebSocket, opts: EventOpts) { this.events[data.user.id] = await listenEvent( data.user.id, handlePresenceUpdate.bind(this), - this.listen_options, + this.listen_options ); break; case "GUILD_CREATE": @@ -193,11 +174,7 @@ async function consume(this: WebSocket, opts: EventOpts) { break; case "CHANNEL_UPDATE": { const exists = this.events[id]; - if ( - permission - .overwriteChannel(data.permission_overwrites) - .has("VIEW_CHANNEL") - ) { + if (permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) { if (exists) break; this.events[id] = await listenEvent(id, consumer, listenOpts); } else { diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index 330ce561..90c001df 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -16,15 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - CLOSECODES, - Capabilities, - OPCODES, - Payload, - Send, - WebSocket, - setupListener, -} from "@spacebar/gateway"; +import { CLOSECODES, Capabilities, OPCODES, Payload, Send, WebSocket, setupListener } from "@spacebar/gateway"; import { Application, Config, @@ -109,9 +101,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { this.shard_count <= 0 ) { // TODO: why do we even care about this right now? - console.log( - `[Gateway] Invalid sharding from ${user.id}: ${identify.shard}`, - ); + console.log(`[Gateway] Invalid sharding from ${user.id}: ${identify.shard}`); return this.close(CLOSECODES.Invalid_shard); } } @@ -134,93 +124,77 @@ export async function onIdentify(this: WebSocket, data: Payload) { // * guild members for this user // * recipients ( dm channels ) // * the bot application, if it exists - const [, application, read_states, members, recipients] = await Promise.all( - [ - session.save(), - - Application.findOne({ - where: { id: this.user_id }, - select: ["id", "flags"], - }), - - ReadState.find({ - where: { user_id: this.user_id }, - select: [ - "id", - "channel_id", - "last_message_id", - "last_pin_timestamp", - "mention_count", - ], - }), - - Member.find({ - where: { id: this.user_id }, - select: { - // We only want some member props - ...Object.fromEntries( - MemberPrivateProjection.map((x) => [x, true]), - ), - settings: true, // guild settings - roles: { id: true }, // the full role is fetched from the `guild` relation - - // TODO: we don't really need every property of - // guild channels, emoji, roles, stickers - // but we do want almost everything from guild. - // How do you do that without just enumerating the guild props? - guild: Object.fromEntries( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - getDatabase()! - .getMetadata(Guild) - .columns.map((x) => [x.propertyName, true]), - ), - }, - relations: [ - "guild", - "guild.channels", - "guild.emojis", - "guild.roles", - "guild.stickers", - "roles", - - // For these entities, `user` is always just the logged in user we fetched above - // "user", - ], - }), - - Recipient.find({ - where: { user_id: this.user_id, closed: false }, - relations: [ - "channel", - "channel.recipients", - "channel.recipients.user", - ], - select: { - channel: { + const [, application, read_states, members, recipients] = await Promise.all([ + session.save(), + + Application.findOne({ + where: { id: this.user_id }, + select: ["id", "flags"], + }), + + ReadState.find({ + where: { user_id: this.user_id }, + select: ["id", "channel_id", "last_message_id", "last_pin_timestamp", "mention_count"], + }), + + Member.find({ + where: { id: this.user_id }, + select: { + // We only want some member props + ...Object.fromEntries(MemberPrivateProjection.map((x) => [x, true])), + settings: true, // guild settings + roles: { id: true }, // the full role is fetched from the `guild` relation + + // TODO: we don't really need every property of + // guild channels, emoji, roles, stickers + // but we do want almost everything from guild. + // How do you do that without just enumerating the guild props? + guild: Object.fromEntries( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + getDatabase()! + .getMetadata(Guild) + .columns.map((x) => [x.propertyName, true]) + ), + }, + relations: [ + "guild", + "guild.channels", + "guild.emojis", + "guild.roles", + "guild.stickers", + "roles", + + // For these entities, `user` is always just the logged in user we fetched above + // "user", + ], + }), + + Recipient.find({ + where: { user_id: this.user_id, closed: false }, + relations: ["channel", "channel.recipients", "channel.recipients.user"], + select: { + channel: { + id: true, + flags: true, + // is_spam: true, // TODO + last_message_id: true, + last_pin_timestamp: true, + type: true, + icon: true, + name: true, + owner_id: true, + recipients: { + // we don't actually need this ID or any other information about the recipient info, + // but typeorm does not select anything from the users relation of recipients unless we select + // at least one column. id: true, - flags: true, - // is_spam: true, // TODO - last_message_id: true, - last_pin_timestamp: true, - type: true, - icon: true, - name: true, - owner_id: true, - recipients: { - // we don't actually need this ID or any other information about the recipient info, - // but typeorm does not select anything from the users relation of recipients unless we select - // at least one column. - id: true, - // We only want public user data for each dm channel - user: Object.fromEntries( - PublicUserProjection.map((x) => [x, true]), - ), - }, + // We only want public user data for each dm channel + user: Object.fromEntries(PublicUserProjection.map((x) => [x, true])), }, }, - }), - ], - ); + }, + }), + ]); // We forgot to migrate user settings from the JSON column of `users` // to the `user_settings` table theyre in now, @@ -273,9 +247,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { return perms.has("VIEW_CHANNEL"); }) .map((channel) => { - channel.position = member.guild.channel_ordering.indexOf( - channel.id, - ); + channel.position = member.guild.channel_ordering.indexOf(channel.id); return channel; }) .sort((a, b) => a.position - b.position); @@ -294,18 +266,15 @@ export async function onIdentify(this: WebSocket, data: Payload) { }); // Generate user_guild_settings - const user_guild_settings_entries: ReadyUserGuildSettingsEntries[] = - members.map((x) => ({ - ...DefaultUserGuildSettings, - ...x.settings, - guild_id: x.guild_id, - channel_overrides: Object.entries( - x.settings.channel_overrides ?? {}, - ).map((y) => ({ - ...y[1], - channel_id: y[0], - })), - })); + const user_guild_settings_entries: ReadyUserGuildSettingsEntries[] = members.map((x) => ({ + ...DefaultUserGuildSettings, + ...x.settings, + guild_id: x.guild_id, + channel_overrides: Object.entries(x.settings.channel_overrides ?? {}).map((y) => ({ + ...y[1], + channel_id: y[0], + })), + })); // Popultaed with users from private channels, relationships. // Uses a set to dedupe for us. @@ -320,16 +289,11 @@ export async function onIdentify(this: WebSocket, data: Payload) { const channel = r.channel as DMChannel; // Remove ourself from the list of other users in dm channel - channel.recipients = channel.recipients.filter( - (recipient) => recipient.user.id !== this.user_id, - ); + channel.recipients = channel.recipients.filter((recipient) => recipient.user.id !== this.user_id); - const channelUsers = channel.recipients?.map((recipient) => - recipient.user.toPublicUser(), - ); + const channelUsers = channel.recipients?.map((recipient) => recipient.user.toPublicUser()); - if (channelUsers && channelUsers.length > 0) - channelUsers.forEach((user) => users.add(user)); + if (channelUsers && channelUsers.length > 0) channelUsers.forEach((user) => users.add(user)); return { id: channel.id, @@ -386,9 +350,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { const d: ReadyEventData = { v: 9, - application: application - ? { id: application.id, flags: application.flags } - : undefined, + application: application ? { id: application.id, flags: application.flags } : undefined, user: user.toPrivateUser(), user_settings: user.settings, guilds: this.capabilities.has(Capabilities.FLAGS.CLIENT_STATE_V2) @@ -413,15 +375,11 @@ export async function onIdentify(this: WebSocket, data: Payload) { sessions: allSessions, resume_gateway_url: - Config.get().gateway.endpointClient || - Config.get().gateway.endpointPublic || - "ws://127.0.0.1:3001", + Config.get().gateway.endpointClient || Config.get().gateway.endpointPublic || "ws://127.0.0.1:3001", // lol hack whatever required_action: - Config.get().login.requireVerification && !user.verified - ? "REQUIRE_VERIFIED_EMAIL" - : undefined, + Config.get().login.requireVerification && !user.verified ? "REQUIRE_VERIFIED_EMAIL" : undefined, consents: { personalization: { @@ -457,10 +415,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { t: EVENTEnum.GuildCreate, s: this.sequence++, d: x, - })?.catch((e) => - console.error(`[Gateway] error when sending bot guilds`, e), - ), - ), + })?.catch((e) => console.error(`[Gateway] error when sending bot guilds`, e)) + ) ); // TODO: ready supplemental diff --git a/src/gateway/opcodes/LazyRequest.ts b/src/gateway/opcodes/LazyRequest.ts index 3c21b708..1318e30e 100644 --- a/src/gateway/opcodes/LazyRequest.ts +++ b/src/gateway/opcodes/LazyRequest.ts @@ -30,13 +30,7 @@ import { Channel, Permissions, } from "@spacebar/util"; -import { - WebSocket, - Payload, - handlePresenceUpdate, - OPCODES, - Send, -} from "@spacebar/gateway"; +import { WebSocket, Payload, handlePresenceUpdate, OPCODES, Send } from "@spacebar/gateway"; import murmur from "murmurhash-js/murmurhash3_gc"; import { check } from "./instanceOf"; @@ -55,9 +49,7 @@ const getMostRelevantSession = (sessions: Session[]) => { // sort sessions by relevance sessions = sessions.sort((a, b) => { return ( - statusMap[a.status] - - statusMap[b.status] + - ((a.activities?.length ?? 0) - (b.activities?.length ?? 0)) * 2 + statusMap[a.status] - statusMap[b.status] + ((a.activities?.length ?? 0) - (b.activities?.length ?? 0)) * 2 ); }); @@ -81,10 +73,7 @@ async function getMembers(guild_id: string, range: [number, number]) { .leftJoinAndSelect("member.user", "user") .leftJoinAndSelect("user.sessions", "session") .addSelect("user.settings") - .addSelect( - "CASE WHEN session.status = 'offline' THEN 0 ELSE 1 END", - "_status", - ) + .addSelect("CASE WHEN session.status = 'offline' THEN 0 ELSE 1 END", "_status") .orderBy("role.position", "DESC") .addOrderBy("_status", "DESC") .addOrderBy("user.username", "ASC") @@ -113,8 +102,8 @@ async function getMembers(guild_id: string, range: [number, number]) { member_roles.push( member_roles.splice( member_roles.findIndex((x) => x.id === x.guild_id), - 1, - )[0], + 1 + )[0] ); const offlineItems = []; @@ -122,7 +111,7 @@ async function getMembers(guild_id: string, range: [number, number]) { for (const role of member_roles) { const [role_members, other_members] = partition( members, - (m: Member) => !!m.roles.find((r) => r.id === role.id), + (m: Member) => !!m.roles.find((r) => r.id === role.id) ); const group = { count: role_members.length, @@ -133,13 +122,9 @@ async function getMembers(guild_id: string, range: [number, number]) { groups.push(group); for (const member of role_members) { - const roles = member.roles - .filter((x: Role) => x.id !== guild_id) - .map((x: Role) => x.id); + const roles = member.roles.filter((x: Role) => x.id !== guild_id).map((x: Role) => x.id); - const session: Session | undefined = getMostRelevantSession( - member.user.sessions, - ); + const session: Session | undefined = getMostRelevantSession(member.user.sessions); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -160,11 +145,7 @@ async function getMembers(guild_id: string, range: [number, number]) { }, }; - if ( - !session || - session.status == "invisible" || - session.status == "offline" - ) { + if (!session || session.status == "invisible" || session.status == "offline") { item.member.presence.status = "offline"; offlineItems.push(item); group.count--; @@ -192,11 +173,7 @@ async function getMembers(guild_id: string, range: [number, number]) { groups, range, members: items - .map((x) => - "member" in x - ? { ...x.member, settings: undefined } - : undefined, - ) + .map((x) => ("member" in x ? { ...x.member, settings: undefined } : undefined)) .filter((x) => !!x), }; } @@ -204,11 +181,7 @@ async function getMembers(guild_id: string, range: [number, number]) { async function subscribeToMemberEvents(this: WebSocket, user_id: string) { if (this.events[user_id]) return false; // already subscribed as friend if (this.member_events[user_id]) return false; // already subscribed in member list - this.member_events[user_id] = await listenEvent( - user_id, - handlePresenceUpdate.bind(this), - this.listen_options, - ); + this.member_events[user_id] = await listenEvent(user_id, handlePresenceUpdate.bind(this), this.listen_options); return true; } @@ -216,8 +189,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { // TODO: check data check.call(this, LazyRequestSchema, d); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { guild_id, typing, channels, activities, members } = - d as LazyRequestSchema; + const { guild_id, typing, channels, activities, members } = d as LazyRequestSchema; if (members) { // Client has requested a PRESENCE_UPDATE for specific member @@ -225,10 +197,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { await Promise.all([ members.map(async (x) => { if (!x) return; - const didSubscribe = await subscribeToMemberEvents.call( - this, - x, - ); + const didSubscribe = await subscribeToMemberEvents.call(this, x); if (!didSubscribe) return; // if we didn't subscribe just now, this is a new subscription @@ -270,9 +239,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { if (!Array.isArray(ranges)) throw new Error("Not a valid Array"); const member_count = await Member.count({ where: { guild_id } }); - const ops = await Promise.all( - ranges.map((x) => getMembers(guild_id, x as [number, number])), - ); + const ops = await Promise.all(ranges.map((x) => getMembers(guild_id, x as [number, number]))); let list_id = "everyone"; @@ -285,10 +252,8 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { channel.permission_overwrites.forEach((overwrite) => { const { id, allow, deny } = overwrite; - if (allow.toBigInt() & Permissions.FLAGS.VIEW_CHANNEL) - perms.push(`allow:${id}`); - else if (deny.toBigInt() & Permissions.FLAGS.VIEW_CHANNEL) - perms.push(`deny:${id}`); + if (allow.toBigInt() & Permissions.FLAGS.VIEW_CHANNEL) perms.push(`allow:${id}`); + else if (deny.toBigInt() & Permissions.FLAGS.VIEW_CHANNEL) perms.push(`deny:${id}`); }); if (perms.length > 0) { @@ -320,9 +285,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { op: "SYNC", range: x.range, })), - online_count: - member_count - - (groups.find((x) => x.id == "offline")?.count ?? 0), + online_count: member_count - (groups.find((x) => x.id == "offline")?.count ?? 0), member_count, id: list_id, guild_id, diff --git a/src/gateway/opcodes/PresenceUpdate.ts b/src/gateway/opcodes/PresenceUpdate.ts index 03736263..24a3461b 100644 --- a/src/gateway/opcodes/PresenceUpdate.ts +++ b/src/gateway/opcodes/PresenceUpdate.ts @@ -17,23 +17,14 @@ */ import { WebSocket, Payload } from "@spacebar/gateway"; -import { - emitEvent, - PresenceUpdateEvent, - Session, - User, - ActivitySchema, -} from "@spacebar/util"; +import { emitEvent, PresenceUpdateEvent, Session, User, ActivitySchema } from "@spacebar/util"; import { check } from "./instanceOf"; export async function onPresenceUpdate(this: WebSocket, { d }: Payload) { check.call(this, ActivitySchema, d); const presence = d as ActivitySchema; - await Session.update( - { session_id: this.session_id }, - { status: presence.status, activities: presence.activities }, - ); + await Session.update({ session_id: this.session_id }, { status: presence.status, activities: presence.activities }); await emitEvent({ event: "PRESENCE_UPDATE", diff --git a/src/gateway/opcodes/VoiceStateUpdate.ts b/src/gateway/opcodes/VoiceStateUpdate.ts index ffc143ed..a543c143 100644 --- a/src/gateway/opcodes/VoiceStateUpdate.ts +++ b/src/gateway/opcodes/VoiceStateUpdate.ts @@ -45,20 +45,14 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { voiceState = await VoiceState.findOneOrFail({ where: { user_id: this.user_id }, }); - if ( - voiceState.session_id !== this.session_id && - body.channel_id === null - ) { + if (voiceState.session_id !== this.session_id && body.channel_id === null) { //Should we also check guild_id === null? //changing deaf or mute on a client that's not the one with the same session of the voicestate in the database should be ignored return; } //If a user change voice channel between guild we should send a left event first - if ( - voiceState.guild_id !== body.guild_id && - voiceState.session_id === this.session_id - ) { + if (voiceState.guild_id !== body.guild_id && voiceState.session_id === this.session_id) { await emitEvent({ event: "VOICE_STATE_UPDATE", data: { ...voiceState, channel_id: null }, @@ -95,8 +89,7 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { }); //If the session changed we generate a new token - if (voiceState.session_id !== this.session_id) - voiceState.token = genVoiceToken(); + if (voiceState.session_id !== this.session_id) voiceState.token = genVoiceToken(); voiceState.session_id = this.session_id; // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -119,13 +112,9 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { const regions = Config.get().regions; let guildRegion: Region; if (guild && guild.region) { - guildRegion = regions.available.filter( - (r) => r.id === guild.region, - )[0]; + guildRegion = regions.available.filter((r) => r.id === guild.region)[0]; } else { - guildRegion = regions.available.filter( - (r) => r.id === regions.default, - )[0]; + guildRegion = regions.available.filter((r) => r.id === regions.default)[0]; } await emitEvent({ diff --git a/src/gateway/opcodes/experiments.json b/src/gateway/opcodes/experiments.json index 0370b5da..be6985bc 100644 --- a/src/gateway/opcodes/experiments.json +++ b/src/gateway/opcodes/experiments.json @@ -1,76 +1,76 @@ [ - [4047587481, 0, 0, -1, 0], - [1509401575, 0, 1, -1, 0], - [1865079242, 0, 1, -1, 0], - [1962538549, 1, 0, -1, 0], - [3816091942, 3, 2, -1, 0], - [4130837190, 0, 10, -1, 0], - [1861568052, 0, 1, -1, 0], - [2290910058, 6, 2, -1, 0], - [1578940118, 1, 1, -1, 0], - [1571676964, 0, 1, -1, 2], - [3640172371, 0, 2, -1, 2], - [1658164312, 2, 1, -1, 0], - [98883956, 1, 1, -1, 0], - [3114091169, 0, 1, -1, 0], - [2570684145, 4, 1, -1, 2], - [4007615411, 0, 1, -1, 0], - [3665310159, 2, 1, -1, 1], - [852550504, 3, 1, -1, 0], - [2333572067, 0, 1, -1, 0], - [935994771, 1, 1, -1, 0], - [1127795596, 1, 1, -1, 0], - [4168223991, 0, 1, -1, 0], - [18585280, 0, 1, -1, 1], - [327482016, 0, 1, -1, 2], - [3458098201, 7, 1, -1, 0], - [478613943, 2, 1, -1, 1], - [2792197902, 0, 1, -1, 2], - [284670956, 0, 1, -1, 0], - [2099185390, 0, 1, -1, 0], - [1202202685, 0, 1, -1, 0], - [2122174751, 0, 1, -1, 0], - [3633864632, 0, 1, -1, 0], - [3103053065, 0, 1, -1, 0], - [820624960, 0, 1, -1, 0], - [1134479292, 0, 1, -1, 0], - [2511257455, 3, 1, -1, 3], - [2599708267, 0, 1, -1, 0], - [613180822, 1, 1, -1, 0], - [2885186814, 0, 1, -1, 0], - [221503477, 0, 1, -1, 0], - [1054317075, 0, 1, -1, 3], - [683872522, 0, 1, -1, 1], - [1739278764, 0, 2, -1, 0], - [2855249023, 0, 1, -1, 0], - [3721841948, 0, 1, -1, 0], - [1285203515, 0, 1, -1, 0], - [1365487849, 6, 1, -1, 0], - [955229746, 0, 1, -1, 0], - [3128009767, 0, 10, -1, 0], - [441885003, 0, 1, -1, 0], - [3433971238, 0, 1, -1, 2], - [1038765354, 3, 1, -1, 0], - [1174347196, 0, 1, -1, 0], - [3649806352, 1, 1, -1, 0], - [2973729510, 2, 1, -1, 0], - [2571931329, 1, 6, -1, 0], - [3884442008, 0, 1, -1, 0], - [978673395, 1, 1, -1, 0], - [4050927174, 0, 1, -1, 0], - [1260103069, 0, 1, -1, 0], - [4168894280, 0, 1, -1, 0], - [4045587091, 0, 1, -1, 0], - [2003494159, 1, 1, -1, 0], - [51193042, 0, 1, -1, 0], - [2634540382, 3, 1, -1, 0], - [886364171, 0, 1, -1, 0], - [3898604944, 0, 1, -1, 0], - [3388129398, 0, 1, -1, 0], - [3964382884, 2, 1, -1, 1], - [3305874255, 0, 1, -1, 0], - [156590431, 0, 1, -1, 0], - [3106485751, 0, 0, -1, 0], - [3035674767, 0, 1, -1, 0], - [851697110, 0, 1, -1, 0] + [4047587481, 0, 0, -1, 0], + [1509401575, 0, 1, -1, 0], + [1865079242, 0, 1, -1, 0], + [1962538549, 1, 0, -1, 0], + [3816091942, 3, 2, -1, 0], + [4130837190, 0, 10, -1, 0], + [1861568052, 0, 1, -1, 0], + [2290910058, 6, 2, -1, 0], + [1578940118, 1, 1, -1, 0], + [1571676964, 0, 1, -1, 2], + [3640172371, 0, 2, -1, 2], + [1658164312, 2, 1, -1, 0], + [98883956, 1, 1, -1, 0], + [3114091169, 0, 1, -1, 0], + [2570684145, 4, 1, -1, 2], + [4007615411, 0, 1, -1, 0], + [3665310159, 2, 1, -1, 1], + [852550504, 3, 1, -1, 0], + [2333572067, 0, 1, -1, 0], + [935994771, 1, 1, -1, 0], + [1127795596, 1, 1, -1, 0], + [4168223991, 0, 1, -1, 0], + [18585280, 0, 1, -1, 1], + [327482016, 0, 1, -1, 2], + [3458098201, 7, 1, -1, 0], + [478613943, 2, 1, -1, 1], + [2792197902, 0, 1, -1, 2], + [284670956, 0, 1, -1, 0], + [2099185390, 0, 1, -1, 0], + [1202202685, 0, 1, -1, 0], + [2122174751, 0, 1, -1, 0], + [3633864632, 0, 1, -1, 0], + [3103053065, 0, 1, -1, 0], + [820624960, 0, 1, -1, 0], + [1134479292, 0, 1, -1, 0], + [2511257455, 3, 1, -1, 3], + [2599708267, 0, 1, -1, 0], + [613180822, 1, 1, -1, 0], + [2885186814, 0, 1, -1, 0], + [221503477, 0, 1, -1, 0], + [1054317075, 0, 1, -1, 3], + [683872522, 0, 1, -1, 1], + [1739278764, 0, 2, -1, 0], + [2855249023, 0, 1, -1, 0], + [3721841948, 0, 1, -1, 0], + [1285203515, 0, 1, -1, 0], + [1365487849, 6, 1, -1, 0], + [955229746, 0, 1, -1, 0], + [3128009767, 0, 10, -1, 0], + [441885003, 0, 1, -1, 0], + [3433971238, 0, 1, -1, 2], + [1038765354, 3, 1, -1, 0], + [1174347196, 0, 1, -1, 0], + [3649806352, 1, 1, -1, 0], + [2973729510, 2, 1, -1, 0], + [2571931329, 1, 6, -1, 0], + [3884442008, 0, 1, -1, 0], + [978673395, 1, 1, -1, 0], + [4050927174, 0, 1, -1, 0], + [1260103069, 0, 1, -1, 0], + [4168894280, 0, 1, -1, 0], + [4045587091, 0, 1, -1, 0], + [2003494159, 1, 1, -1, 0], + [51193042, 0, 1, -1, 0], + [2634540382, 3, 1, -1, 0], + [886364171, 0, 1, -1, 0], + [3898604944, 0, 1, -1, 0], + [3388129398, 0, 1, -1, 0], + [3964382884, 2, 1, -1, 1], + [3305874255, 0, 1, -1, 0], + [156590431, 0, 1, -1, 0], + [3106485751, 0, 0, -1, 0], + [3035674767, 0, 1, -1, 0], + [851697110, 0, 1, -1, 0] ] diff --git a/src/gateway/util/Send.ts b/src/gateway/util/Send.ts index 57f87d8b..86f75a9a 100644 --- a/src/gateway/util/Send.ts +++ b/src/gateway/util/Send.ts @@ -37,15 +37,13 @@ const recurseJsonReplace = (json: any) => { json[key] = JSONReplacer.call(json, key, json[key]); - if (typeof json[key] == "object" && json[key] !== null) - json[key] = recurseJsonReplace(json[key]); + if (typeof json[key] == "object" && json[key] !== null) json[key] = recurseJsonReplace(json[key]); } return json; }; export function Send(socket: WebSocket, data: Payload) { - if (process.env.WS_VERBOSE) - console.log(`[Websocket] Outgoing message: ${JSON.stringify(data)}`); + if (process.env.WS_VERBOSE) console.log(`[Websocket] Outgoing message: ${JSON.stringify(data)}`); if (process.env.WS_DUMP) { const id = socket.session_id || "unknown"; @@ -54,10 +52,7 @@ export function Send(socket: WebSocket, data: Payload) { await fs.mkdir(path.join("dump", id), { recursive: true, }); - await fs.writeFile( - path.join("dump", id, `${Date.now()}.out.json`), - JSON.stringify(data, null, 2), - ); + await fs.writeFile(path.join("dump", id, `${Date.now()}.out.json`), JSON.stringify(data, null, 2)); })(); } @@ -68,8 +63,7 @@ export function Send(socket: WebSocket, data: Payload) { buffer = erlpack.pack(data); } // TODO: encode circular object - else if (socket.encoding === "json") - buffer = JSON.stringify(data, JSONReplacer); + else if (socket.encoding === "json") buffer = JSON.stringify(data, JSONReplacer); else return; // TODO: compression if (socket.deflate) { diff --git a/src/gateway/util/SessionUtils.ts b/src/gateway/util/SessionUtils.ts index 9238db72..26e7bdd6 100644 --- a/src/gateway/util/SessionUtils.ts +++ b/src/gateway/util/SessionUtils.ts @@ -25,7 +25,5 @@ export function genVoiceToken() { } function genRanHex(size: number) { - return [...Array(size)] - .map(() => Math.floor(Math.random() * 16).toString(16)) - .join(""); + return [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join(""); } diff --git a/src/util/config/Config.ts b/src/util/config/Config.ts index 90b98b7a..cc23b87b 100644 --- a/src/util/config/Config.ts +++ b/src/util/config/Config.ts @@ -59,6 +59,5 @@ export class ConfigValue { defaults: DefaultsConfiguration = new DefaultsConfiguration(); external: ExternalTokensConfiguration = new ExternalTokensConfiguration(); email: EmailConfiguration = new EmailConfiguration(); - passwordReset: PasswordResetConfiguration = - new PasswordResetConfiguration(); + passwordReset: PasswordResetConfiguration = new PasswordResetConfiguration(); } diff --git a/src/util/config/types/EmailConfiguration.ts b/src/util/config/types/EmailConfiguration.ts index ae9d53ba..3d1c01d2 100644 --- a/src/util/config/types/EmailConfiguration.ts +++ b/src/util/config/types/EmailConfiguration.ts @@ -16,11 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - MailGunConfiguration, - MailJetConfiguration, - SMTPConfiguration, -} from "./subconfigurations/email"; +import { MailGunConfiguration, MailJetConfiguration, SMTPConfiguration } from "./subconfigurations/email"; import { SendGridConfiguration } from "./subconfigurations/email/SendGrid"; export class EmailConfiguration { diff --git a/src/util/config/types/GeneralConfiguration.ts b/src/util/config/types/GeneralConfiguration.ts index cff8c527..1548b527 100644 --- a/src/util/config/types/GeneralConfiguration.ts +++ b/src/util/config/types/GeneralConfiguration.ts @@ -20,8 +20,7 @@ import { Snowflake } from "@spacebar/util"; export class GeneralConfiguration { instanceName: string = "Spacebar Instance"; - instanceDescription: string | null = - "This is a Spacebar instance made in the pre-release days"; + instanceDescription: string | null = "This is a Spacebar instance made in the pre-release days"; frontPage: string | null = null; tosPage: string | null = null; correspondenceEmail: string | null = null; diff --git a/src/util/config/types/LimitConfigurations.ts b/src/util/config/types/LimitConfigurations.ts index dfcb9765..a9f9c297 100644 --- a/src/util/config/types/LimitConfigurations.ts +++ b/src/util/config/types/LimitConfigurations.ts @@ -16,14 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - ChannelLimits, - GlobalRateLimits, - GuildLimits, - MessageLimits, - RateLimits, - UserLimits, -} from "."; +import { ChannelLimits, GlobalRateLimits, GuildLimits, MessageLimits, RateLimits, UserLimits } from "."; export class LimitsConfiguration { user: UserLimits = new UserLimits(); diff --git a/src/util/config/types/RegisterConfiguration.ts b/src/util/config/types/RegisterConfiguration.ts index 9d8e036e..5e9ff374 100644 --- a/src/util/config/types/RegisterConfiguration.ts +++ b/src/util/config/types/RegisterConfiguration.ts @@ -16,15 +16,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - DateOfBirthConfiguration, - PasswordConfiguration, - RegistrationEmailConfiguration, -} from "."; +import { DateOfBirthConfiguration, PasswordConfiguration, RegistrationEmailConfiguration } from "."; export class RegisterConfiguration { - email: RegistrationEmailConfiguration = - new RegistrationEmailConfiguration(); + email: RegistrationEmailConfiguration = new RegistrationEmailConfiguration(); dateOfBirth: DateOfBirthConfiguration = new DateOfBirthConfiguration(); password: PasswordConfiguration = new PasswordConfiguration(); disabled: boolean = false; diff --git a/src/util/config/types/SecurityConfiguration.ts b/src/util/config/types/SecurityConfiguration.ts index 35776642..234abc2f 100644 --- a/src/util/config/types/SecurityConfiguration.ts +++ b/src/util/config/types/SecurityConfiguration.ts @@ -29,8 +29,7 @@ export class SecurityConfiguration { // X-Forwarded-For for nginx/reverse proxies // CF-Connecting-IP for cloudflare forwardedFor: string | null = null; - ipdataApiKey: string | null = - "eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9"; + ipdataApiKey: string | null = "eca677b284b3bac29eb72f5e496aa9047f26543605efe99ff2ce35c9"; mfaBackupCodeCount: number = 10; statsWorldReadable: boolean = true; defaultRegistrationTokenExpiration: number = 1000 * 60 * 60 * 24 * 7; //1 week diff --git a/src/util/config/types/SentryConfiguration.ts b/src/util/config/types/SentryConfiguration.ts index 3d92c62a..70ff9aac 100644 --- a/src/util/config/types/SentryConfiguration.ts +++ b/src/util/config/types/SentryConfiguration.ts @@ -20,8 +20,7 @@ import { hostname } from "os"; export class SentryConfiguration { enabled: boolean = false; - endpoint: string = - "https://05e8e3d005f34b7d97e920ae5870a5e5@sentry.thearcanebrony.net/6"; + endpoint: string = "https://05e8e3d005f34b7d97e920ae5870a5e5@sentry.thearcanebrony.net/6"; traceSampleRate: number = 1.0; environment: string = hostname(); } diff --git a/src/util/connections/Connection.ts b/src/util/connections/Connection.ts index 5bdebd47..181234b7 100644 --- a/src/util/connections/Connection.ts +++ b/src/util/connections/Connection.ts @@ -42,8 +42,7 @@ export abstract class Connection { * @returns redirect_uri for this connection */ getRedirectUri() { - const endpointPublic = - Config.get().api.endpointPublic ?? "http://localhost:3001"; + const endpointPublic = Config.get().api.endpointPublic ?? "http://localhost:3001"; return `${endpointPublic}/connections/${this.id}/callback`; } @@ -51,9 +50,7 @@ export abstract class Connection { * Processes the callback * @param args Callback arguments */ - abstract handleCallback( - params: ConnectionCallbackSchema, - ): Promise<ConnectedAccount | null>; + abstract handleCallback(params: ConnectionCallbackSchema): Promise<ConnectedAccount | null>; /** * Gets a user id from state @@ -91,9 +88,7 @@ export abstract class Connection { * @param data connected account data * @returns the new connected account */ - async createConnection( - data: ConnectedAccountSchema, - ): Promise<ConnectedAccount> { + async createConnection(data: ConnectedAccountSchema): Promise<ConnectedAccount> { const ca = ConnectedAccount.create({ ...data }); await ca.save(); return ca; diff --git a/src/util/connections/ConnectionConfig.ts b/src/util/connections/ConnectionConfig.ts index 5a2239a0..0610b43e 100644 --- a/src/util/connections/ConnectionConfig.ts +++ b/src/util/connections/ConnectionConfig.ts @@ -49,11 +49,7 @@ export const ConnectionConfig = { function applyConfig(val: any) { async function apply(obj: any, key = ""): Promise<any> { if (typeof obj === "object" && obj !== null && !(obj instanceof Date)) - return Promise.all( - Object.keys(obj).map((k) => - apply(obj[k], key ? `${key}_${k}` : k), - ), - ); + return Promise.all(Object.keys(obj).map((k) => apply(obj[k], key ? `${key}_${k}` : k))); let pair = pairs.find((x) => x.key === key); if (!pair) pair = new ConnectionConfigEntity(); @@ -83,8 +79,7 @@ function pairsToConfig(pairs: ConnectionConfigEntity[]) { let i = 0; for (const key of keys) { - if (!isNaN(Number(key)) && !prevObj[prev]?.length) - prevObj[prev] = obj = []; + if (!isNaN(Number(key)) && !prevObj[prev]?.length) prevObj[prev] = obj = []; if (i++ === keys.length - 1) obj[key] = p.value; else if (!obj[key]) obj[key] = {}; diff --git a/src/util/connections/ConnectionLoader.ts b/src/util/connections/ConnectionLoader.ts index e9dc6973..9507d5f6 100644 --- a/src/util/connections/ConnectionLoader.ts +++ b/src/util/connections/ConnectionLoader.ts @@ -67,18 +67,11 @@ export class ConnectionLoader { return cfg; } - public static async setConnectionConfig( - id: string, - config: Partial<unknown>, - ): Promise<void> { - if (!config) - console.warn(`[Connections/WARN] ${id} tried to set config=null!`); + public static async setConnectionConfig(id: string, config: Partial<unknown>): Promise<void> { + if (!config) console.warn(`[Connections/WARN] ${id} tried to set config=null!`); await ConnectionConfig.set({ - [id]: Object.assign( - config, - ConnectionLoader.getConnectionConfig(id) || {}, - ), + [id]: Object.assign(config, ConnectionLoader.getConnectionConfig(id) || {}), }); } } diff --git a/src/util/connections/ConnectionStore.ts b/src/util/connections/ConnectionStore.ts index 95e54fd9..0cc4dc31 100644 --- a/src/util/connections/ConnectionStore.ts +++ b/src/util/connections/ConnectionStore.ts @@ -20,6 +20,5 @@ import { Connection } from "./Connection"; import { RefreshableConnection } from "./RefreshableConnection"; export class ConnectionStore { - public static connections: Map<string, Connection | RefreshableConnection> = - new Map(); + public static connections: Map<string, Connection | RefreshableConnection> = new Map(); } diff --git a/src/util/connections/RefreshableConnection.ts b/src/util/connections/RefreshableConnection.ts index 88ad8dab..08b01c68 100644 --- a/src/util/connections/RefreshableConnection.ts +++ b/src/util/connections/RefreshableConnection.ts @@ -30,17 +30,13 @@ export abstract class RefreshableConnection extends Connection { * Refreshes the token for a connected account. * @param connectedAccount The connected account to refresh */ - abstract refreshToken( - connectedAccount: ConnectedAccount, - ): Promise<ConnectedAccountCommonOAuthTokenResponse>; + abstract refreshToken(connectedAccount: ConnectedAccount): Promise<ConnectedAccountCommonOAuthTokenResponse>; /** * Refreshes the token for a connected account and saves it to the database. * @param connectedAccount The connected account to refresh */ - async refresh( - connectedAccount: ConnectedAccount, - ): Promise<ConnectedAccountCommonOAuthTokenResponse> { + async refresh(connectedAccount: ConnectedAccount): Promise<ConnectedAccountCommonOAuthTokenResponse> { const tokenData = await this.refreshToken(connectedAccount); connectedAccount.token_data = { ...tokenData, fetched_at: Date.now() }; await connectedAccount.save(); diff --git a/src/util/dtos/ConnectedAccountDTO.ts b/src/util/dtos/ConnectedAccountDTO.ts index f9efd980..043c1e73 100644 --- a/src/util/dtos/ConnectedAccountDTO.ts +++ b/src/util/dtos/ConnectedAccountDTO.ts @@ -34,16 +34,11 @@ export class ConnectedAccountDTO { metadata_visibility?: number; two_way_link?: boolean; - constructor( - connectedAccount: ConnectedAccount, - with_token: boolean = false, - ) { + constructor(connectedAccount: ConnectedAccount, with_token: boolean = false) { this.id = connectedAccount.external_id; this.user_id = connectedAccount.user_id; this.access_token = - connectedAccount.token_data && with_token - ? connectedAccount.token_data.access_token - : undefined; + connectedAccount.token_data && with_token ? connectedAccount.token_data.access_token : undefined; this.friend_sync = connectedAccount.friend_sync; this.name = connectedAccount.name; this.revoked = connectedAccount.revoked; @@ -53,9 +48,7 @@ export class ConnectedAccountDTO { this.visibility = +(connectedAccount.visibility || false); this.integrations = connectedAccount.integrations; this.metadata_ = connectedAccount.metadata_; - this.metadata_visibility = +( - connectedAccount.metadata_visibility || false - ); + this.metadata_visibility = +(connectedAccount.metadata_visibility || false); this.two_way_link = connectedAccount.two_way_link; } } diff --git a/src/util/dtos/DmChannelDTO.ts b/src/util/dtos/DmChannelDTO.ts index 8427f7f9..26829d58 100644 --- a/src/util/dtos/DmChannelDTO.ts +++ b/src/util/dtos/DmChannelDTO.ts @@ -29,11 +29,7 @@ export class DmChannelDTO { recipients: MinimalPublicUserDTO[]; type: number; - static async from( - channel: Channel, - excluded_recipients: string[] = [], - origin_channel_id?: string, - ) { + static async from(channel: Channel, excluded_recipients: string[] = [], origin_channel_id?: string) { const obj = new DmChannelDTO(); obj.icon = channel.icon || null; obj.id = channel.id; @@ -51,7 +47,7 @@ export class DmChannelDTO { where: { id: r.user_id }, select: PublicUserProjection, }); - }) || [], + }) || [] ) ).map((u) => new MinimalPublicUserDTO(u)); return obj; @@ -60,9 +56,7 @@ export class DmChannelDTO { excludedRecipients(excluded_recipients: string[]): DmChannelDTO { return { ...this, - recipients: this.recipients.filter( - (r) => !excluded_recipients.includes(r.id), - ), + recipients: this.recipients.filter((r) => !excluded_recipients.includes(r.id)), }; } } diff --git a/src/util/dtos/ReadyGuildDTO.ts b/src/util/dtos/ReadyGuildDTO.ts index 061959a6..a0f291dd 100644 --- a/src/util/dtos/ReadyGuildDTO.ts +++ b/src/util/dtos/ReadyGuildDTO.ts @@ -30,10 +30,7 @@ import { } from "../entities"; // TODO: this is not the best place for this type -export type ReadyUserGuildSettingsEntries = Omit< - UserGuildSettings, - "channel_overrides" -> & { +export type ReadyUserGuildSettingsEntries = Omit<UserGuildSettings, "channel_overrides"> & { channel_overrides: (ChannelOverride & { channel_id: string })[]; }; @@ -51,9 +48,7 @@ export type GuildOrUnavailable = | { id: string; unavailable: boolean } | (Guild & { joined_at?: Date; unavailable: undefined }); -const guildIsAvailable = ( - guild: GuildOrUnavailable, -): guild is Guild & { joined_at: Date; unavailable: false } => { +const guildIsAvailable = (guild: GuildOrUnavailable): guild is Guild & { joined_at: Date; unavailable: false } => { return guild.unavailable != true; }; diff --git a/src/util/entities/Attachment.ts b/src/util/entities/Attachment.ts index d60ac41c..946318dd 100644 --- a/src/util/entities/Attachment.ts +++ b/src/util/entities/Attachment.ts @@ -16,14 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - BeforeRemove, - Column, - Entity, - JoinColumn, - ManyToOne, - RelationId, -} from "typeorm"; +import { BeforeRemove, Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { URL } from "url"; import { deleteFile } from "../util/cdn"; import { BaseClass } from "./BaseClass"; @@ -56,13 +49,9 @@ export class Attachment extends BaseClass { message_id: string; @JoinColumn({ name: "message_id" }) - @ManyToOne( - () => require("./Message").Message, - (message: import("./Message").Message) => message.attachments, - { - onDelete: "CASCADE", - }, - ) + @ManyToOne(() => require("./Message").Message, (message: import("./Message").Message) => message.attachments, { + onDelete: "CASCADE", + }) message: import("./Message").Message; @BeforeRemove() diff --git a/src/util/entities/BaseClass.ts b/src/util/entities/BaseClass.ts index f4b3cf59..2c19d197 100644 --- a/src/util/entities/BaseClass.ts +++ b/src/util/entities/BaseClass.ts @@ -16,14 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - BaseEntity, - BeforeInsert, - BeforeUpdate, - FindOptionsWhere, - ObjectIdColumn, - PrimaryColumn, -} from "typeorm"; +import { BaseEntity, BeforeInsert, BeforeUpdate, FindOptionsWhere, ObjectIdColumn, PrimaryColumn } from "typeorm"; import { Snowflake } from "../util/Snowflake"; import { getDatabase } from "../util/Database"; import { OrmUtils } from "../imports/OrmUtils"; @@ -57,15 +50,15 @@ export class BaseClassWithoutId extends BaseEntity { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore this[x.propertyName], - ]), - ), + ]) + ) ); } static increment<T extends BaseClass>( conditions: FindOptionsWhere<T>, propertyPath: string, - value: number | string, + value: number | string ) { const repository = this.getRepository(); return repository.increment(conditions, propertyPath, value); @@ -74,16 +67,14 @@ export class BaseClassWithoutId extends BaseEntity { static decrement<T extends BaseClass>( conditions: FindOptionsWhere<T>, propertyPath: string, - value: number | string, + value: number | string ) { const repository = this.getRepository(); return repository.decrement(conditions, propertyPath, value); } } -export const PrimaryIdColumn = process.env.DATABASE?.startsWith("mongodb") - ? ObjectIdColumn - : PrimaryColumn; +export const PrimaryIdColumn = process.env.DATABASE?.startsWith("mongodb") ? ObjectIdColumn : PrimaryColumn; export class BaseClass extends BaseClassWithoutId { @PrimaryIdColumn() diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts index 169eab3d..6672d03d 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts @@ -17,24 +17,10 @@ */ import { HTTPError } from "lambert-server"; -import { - Column, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - RelationId, -} from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm"; import { DmChannelDTO } from "../dtos"; import { ChannelCreateEvent, ChannelRecipientRemoveEvent } from "../interfaces"; -import { - InvisibleCharacters, - Snowflake, - containsAll, - emitEvent, - getPermission, - trimSpecial, -} from "../util"; +import { InvisibleCharacters, Snowflake, containsAll, emitEvent, getPermission, trimSpecial } from "../util"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Invite } from "./Invite"; @@ -162,14 +148,10 @@ export class Channel extends BaseClass { }) messages?: Message[]; - @OneToMany( - () => VoiceState, - (voice_state: VoiceState) => voice_state.channel, - { - cascade: true, - orphanedRowAction: "delete", - }, - ) + @OneToMany(() => VoiceState, (voice_state: VoiceState) => voice_state.channel, { + cascade: true, + orphanedRowAction: "delete", + }) voice_states?: VoiceState[]; @OneToMany(() => ReadState, (read_state: ReadState) => read_state.channel, { @@ -203,7 +185,7 @@ export class Channel extends BaseClass { skipPermissionCheck?: boolean; skipEventEmit?: boolean; skipNameChecks?: boolean; - }, + } ) { if (!opts?.skipPermissionCheck) { // Always check if user has permission first @@ -221,48 +203,26 @@ export class Channel extends BaseClass { }); if (!opts?.skipNameChecks) { - if ( - !guild.features.includes("ALLOW_INVALID_CHANNEL_NAMES") && - channel.name - ) { + if (!guild.features.includes("ALLOW_INVALID_CHANNEL_NAMES") && channel.name) { for (const character of InvisibleCharacters) if (channel.name.includes(character)) - throw new HTTPError( - "Channel name cannot include invalid characters", - 403, - ); + throw new HTTPError("Channel name cannot include invalid characters", 403); // Categories skip these checks on discord.com - if ( - channel.type !== ChannelType.GUILD_CATEGORY || - guild.features.includes("IRC_LIKE_CATEGORY_NAMES") - ) { + if (channel.type !== ChannelType.GUILD_CATEGORY || guild.features.includes("IRC_LIKE_CATEGORY_NAMES")) { if (channel.name.includes(" ")) - throw new HTTPError( - "Channel name cannot include invalid characters", - 403, - ); + throw new HTTPError("Channel name cannot include invalid characters", 403); if (channel.name.match(/--+/g)) - throw new HTTPError( - "Channel name cannot include multiple adjacent dashes.", - 403, - ); - - if ( - channel.name.charAt(0) === "-" || - channel.name.charAt(channel.name.length - 1) === "-" - ) - throw new HTTPError( - "Channel name cannot start/end with dash.", - 403, - ); + throw new HTTPError("Channel name cannot include multiple adjacent dashes.", 403); + + if (channel.name.charAt(0) === "-" || channel.name.charAt(channel.name.length - 1) === "-") + throw new HTTPError("Channel name cannot start/end with dash.", 403); } else channel.name = channel.name.trim(); //category names are trimmed client side on discord.com } if (!guild.features.includes("ALLOW_UNNAMED_CHANNELS")) { - if (!channel.name) - throw new HTTPError("Channel name cannot be empty.", 403); + if (!channel.name) throw new HTTPError("Channel name cannot be empty.", 403); } } @@ -274,15 +234,9 @@ export class Channel extends BaseClass { const exists = await Channel.findOneOrFail({ where: { id: channel.parent_id }, }); - if (!exists) - throw new HTTPError( - "Parent id channel doesn't exist", - 400, - ); + if (!exists) throw new HTTPError("Parent id channel doesn't exist", 400); if (exists.guild_id !== channel.guild_id) - throw new HTTPError( - "The category channel needs to be in the guild", - ); + throw new HTTPError("The category channel needs to be in the guild"); } break; case ChannelType.GUILD_CATEGORY: @@ -299,9 +253,7 @@ export class Channel extends BaseClass { if (!channel.permission_overwrites) channel.permission_overwrites = []; // TODO: eagerly auto generate position of all guild channels - const position = - (channel.type === ChannelType.UNHANDLED ? 0 : channel.position) || - 0; + const position = (channel.type === ChannelType.UNHANDLED ? 0 : channel.position) || 0; channel = { ...channel, @@ -327,11 +279,7 @@ export class Channel extends BaseClass { return ret; } - static async createDMChannel( - recipients: string[], - creator_user_id: string, - name?: string, - ) { + static async createDMChannel(recipients: string[], creator_user_id: string, name?: string) { recipients = recipients.unique().filter((x) => x !== creator_user_id); // TODO: check config for max number of recipients /** if you want to disallow note to self channels, uncomment the conditional below @@ -342,8 +290,7 @@ export class Channel extends BaseClass { } **/ - const type = - recipients.length > 1 ? ChannelType.GROUP_DM : ChannelType.DM; + const type = recipients.length > 1 ? ChannelType.GROUP_DM : ChannelType.DM; let channel = null; @@ -379,11 +326,8 @@ export class Channel extends BaseClass { recipients: channelRecipients.map((x) => Recipient.create({ user_id: x, - closed: !( - type === ChannelType.GROUP_DM || - x === creator_user_id - ), - }), + closed: !(type === ChannelType.GROUP_DM || x === creator_user_id), + }) ), nsfw: false, }).save(); @@ -413,9 +357,7 @@ export class Channel extends BaseClass { static async removeRecipientFromChannel(channel: Channel, user_id: string) { await Recipient.delete({ channel_id: channel.id, user_id: user_id }); - channel.recipients = channel.recipients?.filter( - (r) => r.user_id !== user_id, - ); + channel.recipients = channel.recipients?.filter((r) => r.user_id !== user_id); if (channel.recipients?.length === 0) { await Channel.deleteChannel(channel); @@ -464,11 +406,7 @@ export class Channel extends BaseClass { await Channel.delete({ id: channel.id }); } - static async calculatePosition( - channel_id: string, - guild_id: string, - guild?: Guild, - ) { + static async calculatePosition(channel_id: string, guild_id: string, guild?: Guild) { if (!guild) guild = await Guild.findOneOrFail({ where: { id: guild_id }, @@ -486,9 +424,7 @@ export class Channel extends BaseClass { }); const channels = await Promise.all( - guild.channel_ordering.map((id) => - Channel.findOneOrFail({ where: { id } }), - ), + guild.channel_ordering.map((id) => Channel.findOneOrFail({ where: { id } })) ); return channels.reduce((r, v) => { @@ -499,9 +435,7 @@ export class Channel extends BaseClass { } isDm() { - return ( - this.type === ChannelType.DM || this.type === ChannelType.GROUP_DM - ); + return this.type === ChannelType.DM || this.type === ChannelType.GROUP_DM; } // Does the channel support sending messages ( eg categories do not ) diff --git a/src/util/entities/ConnectedAccount.ts b/src/util/entities/ConnectedAccount.ts index 6e089de1..4af72c14 100644 --- a/src/util/entities/ConnectedAccount.ts +++ b/src/util/entities/ConnectedAccount.ts @@ -21,10 +21,7 @@ import { ConnectedAccountTokenData } from "../interfaces"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; -export type PublicConnectedAccount = Pick< - ConnectedAccount, - "name" | "type" | "verified" ->; +export type PublicConnectedAccount = Pick<ConnectedAccount, "name" | "type" | "verified">; @Entity("connected_accounts") export class ConnectedAccount extends BaseClass { diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts index fcd6b729..1041e3cc 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts @@ -16,14 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - RelationId, -} from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm"; import { Config, GuildWelcomeScreen, Snowflake, handleFile } from ".."; import { Ban } from "./Ban"; import { BaseClass } from "./BaseClass"; @@ -330,15 +323,12 @@ export class Guild extends BaseClass { channel_ordering: [], afk_timeout: Config.get().defaults.guild.afkTimeout, - default_message_notifications: - Config.get().defaults.guild.defaultMessageNotifications, - explicit_content_filter: - Config.get().defaults.guild.explicitContentFilter, + default_message_notifications: Config.get().defaults.guild.defaultMessageNotifications, + explicit_content_filter: Config.get().defaults.guild.explicitContentFilter, features: Config.get().guild.defaultFeatures, max_members: Config.get().limits.guild.maxMembers, max_presences: Config.get().defaults.guild.maxPresences, - max_video_channel_users: - Config.get().defaults.guild.maxVideoChannelUsers, + max_video_channel_users: Config.get().defaults.guild.maxVideoChannelUsers, region: Config.get().regions.default, }).save(); @@ -361,9 +351,7 @@ export class Guild extends BaseClass { }).save(); if (!body.channels || !body.channels.length) - body.channels = [ - { id: "01", type: 0, name: "general", nsfw: false }, - ]; + body.channels = [{ id: "01", type: 0, name: "general", nsfw: false }]; const ids = new Map(); @@ -373,30 +361,19 @@ export class Guild extends BaseClass { } }); - for (const channel of body.channels.sort((a) => - a.parent_id ? 1 : -1, - )) { + for (const channel of body.channels.sort((a) => (a.parent_id ? 1 : -1))) { const id = ids.get(channel.id) || Snowflake.generate(); const parent_id = ids.get(channel.parent_id); - const saved = await Channel.createChannel( - { ...channel, guild_id, id, parent_id }, - body.owner_id, - { - keepId: true, - skipExistsCheck: true, - skipPermissionCheck: true, - skipEventEmit: true, - }, - ); - - await Guild.insertChannelInOrder( - guild.id, - saved.id, - parent_id ?? channel.position ?? 0, - guild, - ); + const saved = await Channel.createChannel({ ...channel, guild_id, id, parent_id }, body.owner_id, { + keepId: true, + skipExistsCheck: true, + skipPermissionCheck: true, + skipEventEmit: true, + }); + + await Guild.insertChannelInOrder(guild.id, saved.id, parent_id ?? channel.position ?? 0, guild); } return guild; @@ -407,25 +384,25 @@ export class Guild extends BaseClass { guild_id: string, channel_id: string, position: number, - guild?: Guild, + guild?: Guild ): Promise<number>; static async insertChannelInOrder( guild_id: string, channel_id: string, parent_id: string, - guild?: Guild, + guild?: Guild ): Promise<number>; static async insertChannelInOrder( guild_id: string, channel_id: string, insertPoint: string | number, - guild?: Guild, + guild?: Guild ): Promise<number>; static async insertChannelInOrder( guild_id: string, channel_id: string, insertPoint: string | number, - guild?: Guild, + guild?: Guild ): Promise<number> { if (!guild) guild = await Guild.findOneOrFail({ @@ -434,17 +411,13 @@ export class Guild extends BaseClass { }); let position; - if (typeof insertPoint == "string") - position = guild.channel_ordering.indexOf(insertPoint) + 1; + if (typeof insertPoint == "string") position = guild.channel_ordering.indexOf(insertPoint) + 1; else position = insertPoint; guild.channel_ordering.remove(channel_id); guild.channel_ordering.splice(position, 0, channel_id); - await Guild.update( - { id: guild_id }, - { channel_ordering: guild.channel_ordering }, - ); + await Guild.update({ id: guild_id }, { channel_ordering: guild.channel_ordering }); return position; } diff --git a/src/util/entities/Invite.ts b/src/util/entities/Invite.ts index f7e54fbe..7670a802 100644 --- a/src/util/entities/Invite.ts +++ b/src/util/entities/Invite.ts @@ -99,8 +99,7 @@ export class Invite extends BaseClassWithoutId { static async joinGuild(user_id: string, code: string) { const invite = await Invite.findOneOrFail({ where: { code } }); - if (invite.uses++ >= invite.max_uses && invite.max_uses !== 0) - await Invite.delete({ code }); + if (invite.uses++ >= invite.max_uses && invite.max_uses !== 0) await Invite.delete({ code }); else await invite.save(); await Member.addToGuild(user_id, invite.guild_id); diff --git a/src/util/entities/Member.ts b/src/util/entities/Member.ts index 65942816..275e748f 100644 --- a/src/util/entities/Member.ts +++ b/src/util/entities/Member.ts @@ -181,8 +181,7 @@ export class Member extends BaseClassWithoutId { select: ["owner_id"], where: { id: guild_id }, }); - if (guild.owner_id === user_id) - throw new Error("The owner cannot be removed of the guild"); + if (guild.owner_id === user_id) throw new Error("The owner cannot be removed of the guild"); const member = await Member.findOneOrFail({ where: { id: user_id, guild_id }, relations: ["user"], @@ -244,11 +243,7 @@ export class Member extends BaseClassWithoutId { ]); } - static async removeRole( - user_id: string, - guild_id: string, - role_id: string, - ) { + static async removeRole(user_id: string, guild_id: string, role_id: string) { const [member] = await Promise.all([ Member.findOneOrFail({ where: { id: user_id, guild_id }, @@ -278,11 +273,7 @@ export class Member extends BaseClassWithoutId { ]); } - static async changeNickname( - user_id: string, - guild_id: string, - nickname: string, - ) { + static async changeNickname(user_id: string, guild_id: string, nickname: string) { const member = await Member.findOneOrFail({ where: { id: user_id, @@ -316,10 +307,7 @@ export class Member extends BaseClassWithoutId { const { maxGuilds } = Config.get().limits.user; const guild_count = await Member.count({ where: { id: user_id } }); if (guild_count >= maxGuilds) { - throw new HTTPError( - `You are at the ${maxGuilds} server limit.`, - 403, - ); + throw new HTTPError(`You are at the ${maxGuilds} server limit.`, 403); } const guild = await Guild.findOneOrFail({ diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index b519099a..b56f719e 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -161,14 +161,10 @@ export class Message extends BaseClass { @ManyToMany(() => Sticker, { cascade: true, onDelete: "CASCADE" }) sticker_items?: Sticker[]; - @OneToMany( - () => Attachment, - (attachment: Attachment) => attachment.message, - { - cascade: true, - orphanedRowAction: "delete", - }, - ) + @OneToMany(() => Attachment, (attachment: Attachment) => attachment.message, { + cascade: true, + orphanedRowAction: "delete", + }) attachments?: Attachment[]; @Column({ type: "simple-json" }) diff --git a/src/util/entities/Migration.ts b/src/util/entities/Migration.ts index 5c4e951d..8f2fa190 100644 --- a/src/util/entities/Migration.ts +++ b/src/util/entities/Migration.ts @@ -16,17 +16,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - Column, - Entity, - ObjectIdColumn, - PrimaryGeneratedColumn, - BaseEntity, -} from "typeorm"; +import { Column, Entity, ObjectIdColumn, PrimaryGeneratedColumn, BaseEntity } from "typeorm"; -export const PrimaryIdAutoGenerated = process.env.DATABASE?.startsWith( - "mongodb", -) +export const PrimaryIdAutoGenerated = process.env.DATABASE?.startsWith("mongodb") ? ObjectIdColumn : PrimaryGeneratedColumn; diff --git a/src/util/entities/ReadState.ts b/src/util/entities/ReadState.ts index 1b280d12..7add44a2 100644 --- a/src/util/entities/ReadState.ts +++ b/src/util/entities/ReadState.ts @@ -16,14 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - Column, - Entity, - Index, - JoinColumn, - ManyToOne, - RelationId, -} from "typeorm"; +import { Column, Entity, Index, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { User } from "./User"; diff --git a/src/util/entities/Relationship.ts b/src/util/entities/Relationship.ts index 740095c2..fe40c782 100644 --- a/src/util/entities/Relationship.ts +++ b/src/util/entities/Relationship.ts @@ -16,14 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - Column, - Entity, - Index, - JoinColumn, - ManyToOne, - RelationId, -} from "typeorm"; +import { Column, Entity, Index, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; diff --git a/src/util/entities/StickerPack.ts b/src/util/entities/StickerPack.ts index 61ab1287..85a9fa77 100644 --- a/src/util/entities/StickerPack.ts +++ b/src/util/entities/StickerPack.ts @@ -16,14 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - RelationId, -} from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm"; import { Sticker } from "."; import { BaseClass } from "./BaseClass"; diff --git a/src/util/entities/Team.ts b/src/util/entities/Team.ts index 7bedc4af..abf68ee6 100644 --- a/src/util/entities/Team.ts +++ b/src/util/entities/Team.ts @@ -16,14 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - Column, - Entity, - JoinColumn, - ManyToOne, - OneToMany, - RelationId, -} from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { TeamMember } from "./TeamMember"; import { User } from "./User"; diff --git a/src/util/entities/TeamMember.ts b/src/util/entities/TeamMember.ts index 539da957..5e88e6a4 100644 --- a/src/util/entities/TeamMember.ts +++ b/src/util/entities/TeamMember.ts @@ -38,13 +38,9 @@ export class TeamMember extends BaseClass { team_id: string; @JoinColumn({ name: "team_id" }) - @ManyToOne( - () => require("./Team").Team, - (team: import("./Team").Team) => team.members, - { - onDelete: "CASCADE", - }, - ) + @ManyToOne(() => require("./Team").Team, (team: import("./Team").Team) => team.members, { + onDelete: "CASCADE", + }) team: import("./Team").Team; @Column({ nullable: true }) diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index c6582b00..45a7015c 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -17,14 +17,7 @@ */ import { Request } from "express"; -import { - Column, - Entity, - FindOneOptions, - JoinColumn, - OneToMany, - OneToOne, -} from "typeorm"; +import { Column, Entity, FindOneOptions, JoinColumn, OneToMany, OneToOne } from "typeorm"; import { Config, Email, FieldErrors, Snowflake, trimSpecial } from ".."; import { BitField } from "../util/BitField"; import { BaseClass } from "./BaseClass"; @@ -70,7 +63,7 @@ export enum PrivateUserEnum { export type PrivateUserKeys = keyof typeof PrivateUserEnum | PublicUserKeys; export const PublicUserProjection = Object.values(PublicUserEnum).filter( - (x) => typeof x === "string", + (x) => typeof x === "string" ) as PublicUserKeys[]; export const PrivateUserProjection = [ ...PublicUserProjection, @@ -186,25 +179,17 @@ export class User extends BaseClass { sessions: Session[]; @JoinColumn({ name: "relationship_ids" }) - @OneToMany( - () => Relationship, - (relationship: Relationship) => relationship.from, - { - cascade: true, - orphanedRowAction: "delete", - }, - ) + @OneToMany(() => Relationship, (relationship: Relationship) => relationship.from, { + cascade: true, + orphanedRowAction: "delete", + }) relationships: Relationship[]; @JoinColumn({ name: "connected_account_ids" }) - @OneToMany( - () => ConnectedAccount, - (account: ConnectedAccount) => account.user, - { - cascade: true, - orphanedRowAction: "delete", - }, - ) + @OneToMany(() => ConnectedAccount, (account: ConnectedAccount) => account.user, { + cascade: true, + orphanedRowAction: "delete", + }) connected_accounts: ConnectedAccount[]; @Column({ type: "simple-json", select: false }) @@ -281,9 +266,7 @@ export class User extends BaseClass { }); } - public static async generateDiscriminator( - username: string, - ): Promise<string | undefined> { + public static async generateDiscriminator(username: string): Promise<string | undefined> { if (Config.get().register.incrementingDiscriminators) { // discriminator will be incrementally generated @@ -292,10 +275,7 @@ export class User extends BaseClass { where: { username }, select: ["discriminator"], }); - const highestDiscriminator = Math.max( - 0, - ...users.map((u) => Number(u.discriminator)), - ); + const highestDiscriminator = Math.max(0, ...users.map((u) => Number(u.discriminator))); const discriminator = highestDiscriminator + 1; if (discriminator >= 10000) { @@ -309,9 +289,7 @@ export class User extends BaseClass { // randomly generates a discriminator between 1 and 9999 and checks max five times if it already exists // TODO: is there any better way to generate a random discriminator only once, without checking if it already exists in the database? for (let tries = 0; tries < 5; tries++) { - const discriminator = Math.randomIntBetween(1, 9999) - .toString() - .padStart(4, "0"); + const discriminator = Math.randomIntBetween(1, 9999).toString().padStart(4, "0"); const exists = await User.findOne({ where: { discriminator, username: username }, select: ["id"], @@ -346,8 +324,7 @@ export class User extends BaseClass { 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") || "", }, }); } @@ -355,8 +332,7 @@ export class User extends BaseClass { // TODO: save date_of_birth // appearently discord doesn't save the date of birth and just calculate if nsfw is allowed // if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false - const language = - req?.language === "en" ? "en-US" : req?.language || "en-US"; + const language = req?.language === "en" ? "en-US" : req?.language || "en-US"; const settings = UserSettings.create({ locale: language, @@ -374,9 +350,7 @@ export class User extends BaseClass { extended_settings: "{}", settings: settings, - premium_since: Config.get().defaults.user.premium - ? new Date() - : undefined, + premium_since: Config.get().defaults.user.premium ? new Date() : undefined, rights: Config.get().register.defaultRights, premium: Config.get().defaults.user.premium ?? false, premium_type: Config.get().defaults.user.premiumType ?? 0, @@ -390,18 +364,14 @@ export class User extends BaseClass { // send verification email if users aren't verified by default and we have an email if (!Config.get().defaults.user.verified && email) { await Email.sendVerifyEmail(user, email).catch((e) => { - console.error( - `Failed to send verification email to ${user.username}#${user.discriminator}: ${e}`, - ); + console.error(`Failed to send verification email to ${user.username}#${user.discriminator}: ${e}`); }); } setImmediate(async () => { if (Config.get().guild.autoJoin.enabled) { for (const guild of Config.get().guild.autoJoin.guilds || []) { - await Member.addToGuild(user.id, guild).catch((e) => - console.error("[Autojoin]", e), - ); + await Member.addToGuild(user.id, guild).catch((e) => console.error("[Autojoin]", e)); } } }); diff --git a/src/util/imports/OrmUtils.ts b/src/util/imports/OrmUtils.ts index 039c81fe..3a11be24 100644 --- a/src/util/imports/OrmUtils.ts +++ b/src/util/imports/OrmUtils.ts @@ -12,12 +12,7 @@ export class OrmUtils { return !item.constructor || item.constructor === Object; } - private static mergeArrayKey( - target: any, - key: number, - value: any, - memo: Map<any, any>, - ) { + private static mergeArrayKey(target: any, key: number, value: any, memo: Map<any, any>) { // Have we seen this before? Prevent infinite recursion. if (memo.has(value)) { target[key] = memo.get(value); @@ -46,12 +41,7 @@ export class OrmUtils { memo.delete(value); } - private static mergeObjectKey( - target: any, - key: string, - value: any, - memo: Map<any, any>, - ) { + private static mergeObjectKey(target: any, key: string, value: any, memo: Map<any, any>) { // Have we seen this before? Prevent infinite recursion. if (memo.has(value)) { Object.assign(target, { [key]: memo.get(value) }); @@ -80,11 +70,7 @@ export class OrmUtils { memo.delete(value); } - private static merge( - target: any, - source: any, - memo: Map<any, any> = new Map(), - ): any { + private static merge(target: any, source: any, memo: Map<any, any> = new Map()): any { if (Array.isArray(target) && Array.isArray(source)) { for (let key = 0; key < source.length; key++) { this.mergeArrayKey(target, key, source[key], memo); diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts index deb54428..ca6fac6d 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts @@ -94,7 +94,7 @@ export interface ReadyEventData { number, [[number, { e: number; s: number }[]]], [number, [[number, [number, number]]]], - { b: number; k: bigint[] }[], + { b: number; k: bigint[] }[] ][]; guild_join_requests?: unknown[]; // ? what is this? this is new shard?: [number, number]; diff --git a/src/util/migration/mariadb/1673609465036-templateDeleteCascade.ts b/src/util/migration/mariadb/1673609465036-templateDeleteCascade.ts index 483c5070..bfd1344b 100644 --- a/src/util/migration/mariadb/1673609465036-templateDeleteCascade.ts +++ b/src/util/migration/mariadb/1673609465036-templateDeleteCascade.ts @@ -22,20 +22,16 @@ export class templateDeleteCascade1673609465036 implements MigrationInterface { name = "templateDeleteCascade1673609465036"; public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE \`templates\` DROP FOREIGN KEY \`FK_445d00eaaea0e60a017a5ed0c11\``); await queryRunner.query( - `ALTER TABLE \`templates\` DROP FOREIGN KEY \`FK_445d00eaaea0e60a017a5ed0c11\``, - ); - await queryRunner.query( - `ALTER TABLE \`templates\` ADD CONSTRAINT \`FK_445d00eaaea0e60a017a5ed0c11\` FOREIGN KEY (\`source_guild_id\`) REFERENCES \`guilds\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`, + `ALTER TABLE \`templates\` ADD CONSTRAINT \`FK_445d00eaaea0e60a017a5ed0c11\` FOREIGN KEY (\`source_guild_id\`) REFERENCES \`guilds\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` ); } public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE \`templates\` DROP FOREIGN KEY \`FK_445d00eaaea0e60a017a5ed0c11\``); await queryRunner.query( - `ALTER TABLE \`templates\` DROP FOREIGN KEY \`FK_445d00eaaea0e60a017a5ed0c11\``, - ); - await queryRunner.query( - `ALTER TABLE \`templates\` ADD CONSTRAINT \`FK_445d00eaaea0e60a017a5ed0c11\` FOREIGN KEY (\`source_guild_id\`) REFERENCES \`guilds\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`, + `ALTER TABLE \`templates\` ADD CONSTRAINT \`FK_445d00eaaea0e60a017a5ed0c11\` FOREIGN KEY (\`source_guild_id\`) REFERENCES \`guilds\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION` ); } } diff --git a/src/util/migration/mariadb/1675045120206-webauthn.ts b/src/util/migration/mariadb/1675045120206-webauthn.ts index d58ac88d..f20060af 100644 --- a/src/util/migration/mariadb/1675045120206-webauthn.ts +++ b/src/util/migration/mariadb/1675045120206-webauthn.ts @@ -23,23 +23,17 @@ export class webauthn1675045120206 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query( - `CREATE TABLE \`security_keys\` (\`id\` varchar(255) NOT NULL, \`user_id\` varchar(255) NULL, \`key_id\` varchar(255) NOT NULL, \`public_key\` varchar(255) NOT NULL, \`counter\` int NOT NULL, \`name\` varchar(255) NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`, + `CREATE TABLE \`security_keys\` (\`id\` varchar(255) NOT NULL, \`user_id\` varchar(255) NULL, \`key_id\` varchar(255) NOT NULL, \`public_key\` varchar(255) NOT NULL, \`counter\` int NOT NULL, \`name\` varchar(255) NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB` ); + await queryRunner.query(`ALTER TABLE \`users\` ADD \`webauthn_enabled\` tinyint NOT NULL DEFAULT 0`); await queryRunner.query( - `ALTER TABLE \`users\` ADD \`webauthn_enabled\` tinyint NOT NULL DEFAULT 0`, - ); - await queryRunner.query( - `ALTER TABLE \`security_keys\` ADD CONSTRAINT \`FK_24c97d0771cafedce6d7163eaad\` FOREIGN KEY (\`user_id\`) REFERENCES \`users\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`, + `ALTER TABLE \`security_keys\` ADD CONSTRAINT \`FK_24c97d0771cafedce6d7163eaad\` FOREIGN KEY (\`user_id\`) REFERENCES \`users\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` ); } public async down(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query( - `ALTER TABLE \`security_keys\` DROP FOREIGN KEY \`FK_24c97d0771cafedce6d7163eaad\``, - ); - await queryRunner.query( - `ALTER TABLE \`users\` DROP COLUMN \`webauthn_enabled\``, - ); + await queryRunner.query(`ALTER TABLE \`security_keys\` DROP FOREIGN KEY \`FK_24c97d0771cafedce6d7163eaad\``); + await queryRunner.query(`ALTER TABLE \`users\` DROP COLUMN \`webauthn_enabled\``); await queryRunner.query(`DROP TABLE \`security_keys\``); } } diff --git a/src/util/migration/mariadb/1696420827239-guildChannelOrdering.ts b/src/util/migration/mariadb/1696420827239-guildChannelOrdering.ts index 083f3680..960feb9b 100644 --- a/src/util/migration/mariadb/1696420827239-guildChannelOrdering.ts +++ b/src/util/migration/mariadb/1696420827239-guildChannelOrdering.ts @@ -4,31 +4,21 @@ export class guildChannelOrdering1696420827239 implements MigrationInterface { name = "guildChannelOrdering1696420827239"; public async up(queryRunner: QueryRunner): Promise<void> { - const guilds = await queryRunner.query( - `SELECT id FROM guilds`, - undefined, - true, - ); + const guilds = await queryRunner.query(`SELECT id FROM guilds`, undefined, true); - await queryRunner.query( - `ALTER TABLE guilds ADD channel_ordering text NOT NULL DEFAULT '[]'`, - ); + await queryRunner.query(`ALTER TABLE guilds ADD channel_ordering text NOT NULL DEFAULT '[]'`); for (const guild_id of guilds.records.map((x) => x.id)) { const channels: Array<{ position: number; id: string }> = ( - await queryRunner.query( - `SELECT id, position FROM channels WHERE guild_id = ?`, - [guild_id], - true, - ) + await queryRunner.query(`SELECT id, position FROM channels WHERE guild_id = ?`, [guild_id], true) ).records; channels.sort((a, b) => a.position - b.position); - await queryRunner.query( - `UPDATE guilds SET channel_ordering = ? WHERE id = ?`, - [JSON.stringify(channels.map((x) => x.id)), guild_id], - ); + await queryRunner.query(`UPDATE guilds SET channel_ordering = ? WHERE id = ?`, [ + JSON.stringify(channels.map((x) => x.id)), + guild_id, + ]); } await queryRunner.query(`ALTER TABLE channels DROP COLUMN position`); diff --git a/src/util/migration/mysql/1673609465036-templateDeleteCascade.ts b/src/util/migration/mysql/1673609465036-templateDeleteCascade.ts index 483c5070..bfd1344b 100644 --- a/src/util/migration/mysql/1673609465036-templateDeleteCascade.ts +++ b/src/util/migration/mysql/1673609465036-templateDeleteCascade.ts @@ -22,20 +22,16 @@ export class templateDeleteCascade1673609465036 implements MigrationInterface { name = "templateDeleteCascade1673609465036"; public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE \`templates\` DROP FOREIGN KEY \`FK_445d00eaaea0e60a017a5ed0c11\``); await queryRunner.query( - `ALTER TABLE \`templates\` DROP FOREIGN KEY \`FK_445d00eaaea0e60a017a5ed0c11\``, - ); - await queryRunner.query( - `ALTER TABLE \`templates\` ADD CONSTRAINT \`FK_445d00eaaea0e60a017a5ed0c11\` FOREIGN KEY (\`source_guild_id\`) REFERENCES \`guilds\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`, + `ALTER TABLE \`templates\` ADD CONSTRAINT \`FK_445d00eaaea0e60a017a5ed0c11\` FOREIGN KEY (\`source_guild_id\`) REFERENCES \`guilds\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` ); } public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE \`templates\` DROP FOREIGN KEY \`FK_445d00eaaea0e60a017a5ed0c11\``); await queryRunner.query( - `ALTER TABLE \`templates\` DROP FOREIGN KEY \`FK_445d00eaaea0e60a017a5ed0c11\``, - ); - await queryRunner.query( - `ALTER TABLE \`templates\` ADD CONSTRAINT \`FK_445d00eaaea0e60a017a5ed0c11\` FOREIGN KEY (\`source_guild_id\`) REFERENCES \`guilds\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`, + `ALTER TABLE \`templates\` ADD CONSTRAINT \`FK_445d00eaaea0e60a017a5ed0c11\` FOREIGN KEY (\`source_guild_id\`) REFERENCES \`guilds\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION` ); } } diff --git a/src/util/migration/mysql/1675045120206-webauthn.ts b/src/util/migration/mysql/1675045120206-webauthn.ts index d58ac88d..f20060af 100644 --- a/src/util/migration/mysql/1675045120206-webauthn.ts +++ b/src/util/migration/mysql/1675045120206-webauthn.ts @@ -23,23 +23,17 @@ export class webauthn1675045120206 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query( - `CREATE TABLE \`security_keys\` (\`id\` varchar(255) NOT NULL, \`user_id\` varchar(255) NULL, \`key_id\` varchar(255) NOT NULL, \`public_key\` varchar(255) NOT NULL, \`counter\` int NOT NULL, \`name\` varchar(255) NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`, + `CREATE TABLE \`security_keys\` (\`id\` varchar(255) NOT NULL, \`user_id\` varchar(255) NULL, \`key_id\` varchar(255) NOT NULL, \`public_key\` varchar(255) NOT NULL, \`counter\` int NOT NULL, \`name\` varchar(255) NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB` ); + await queryRunner.query(`ALTER TABLE \`users\` ADD \`webauthn_enabled\` tinyint NOT NULL DEFAULT 0`); await queryRunner.query( - `ALTER TABLE \`users\` ADD \`webauthn_enabled\` tinyint NOT NULL DEFAULT 0`, - ); - await queryRunner.query( - `ALTER TABLE \`security_keys\` ADD CONSTRAINT \`FK_24c97d0771cafedce6d7163eaad\` FOREIGN KEY (\`user_id\`) REFERENCES \`users\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`, + `ALTER TABLE \`security_keys\` ADD CONSTRAINT \`FK_24c97d0771cafedce6d7163eaad\` FOREIGN KEY (\`user_id\`) REFERENCES \`users\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` ); } public async down(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query( - `ALTER TABLE \`security_keys\` DROP FOREIGN KEY \`FK_24c97d0771cafedce6d7163eaad\``, - ); - await queryRunner.query( - `ALTER TABLE \`users\` DROP COLUMN \`webauthn_enabled\``, - ); + await queryRunner.query(`ALTER TABLE \`security_keys\` DROP FOREIGN KEY \`FK_24c97d0771cafedce6d7163eaad\``); + await queryRunner.query(`ALTER TABLE \`users\` DROP COLUMN \`webauthn_enabled\``); await queryRunner.query(`DROP TABLE \`security_keys\``); } } diff --git a/src/util/migration/mysql/1696420827239-guildChannelOrdering.ts b/src/util/migration/mysql/1696420827239-guildChannelOrdering.ts index 083f3680..960feb9b 100644 --- a/src/util/migration/mysql/1696420827239-guildChannelOrdering.ts +++ b/src/util/migration/mysql/1696420827239-guildChannelOrdering.ts @@ -4,31 +4,21 @@ export class guildChannelOrdering1696420827239 implements MigrationInterface { name = "guildChannelOrdering1696420827239"; public async up(queryRunner: QueryRunner): Promise<void> { - const guilds = await queryRunner.query( - `SELECT id FROM guilds`, - undefined, - true, - ); + const guilds = await queryRunner.query(`SELECT id FROM guilds`, undefined, true); - await queryRunner.query( - `ALTER TABLE guilds ADD channel_ordering text NOT NULL DEFAULT '[]'`, - ); + await queryRunner.query(`ALTER TABLE guilds ADD channel_ordering text NOT NULL DEFAULT '[]'`); for (const guild_id of guilds.records.map((x) => x.id)) { const channels: Array<{ position: number; id: string }> = ( - await queryRunner.query( - `SELECT id, position FROM channels WHERE guild_id = ?`, - [guild_id], - true, - ) + await queryRunner.query(`SELECT id, position FROM channels WHERE guild_id = ?`, [guild_id], true) ).records; channels.sort((a, b) => a.position - b.position); - await queryRunner.query( - `UPDATE guilds SET channel_ordering = ? WHERE id = ?`, - [JSON.stringify(channels.map((x) => x.id)), guild_id], - ); + await queryRunner.query(`UPDATE guilds SET channel_ordering = ? WHERE id = ?`, [ + JSON.stringify(channels.map((x) => x.id)), + guild_id, + ]); } await queryRunner.query(`ALTER TABLE channels DROP COLUMN position`); diff --git a/src/util/migration/postgres/1673609867556-templateDeleteCascade.ts b/src/util/migration/postgres/1673609867556-templateDeleteCascade.ts index 504ed557..33c72dea 100644 --- a/src/util/migration/postgres/1673609867556-templateDeleteCascade.ts +++ b/src/util/migration/postgres/1673609867556-templateDeleteCascade.ts @@ -22,20 +22,16 @@ export class templateDeleteCascade1673609867556 implements MigrationInterface { name = "templateDeleteCascade1673609867556"; public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "templates" DROP CONSTRAINT "FK_445d00eaaea0e60a017a5ed0c11"`); await queryRunner.query( - `ALTER TABLE "templates" DROP CONSTRAINT "FK_445d00eaaea0e60a017a5ed0c11"`, - ); - await queryRunner.query( - `ALTER TABLE "templates" ADD CONSTRAINT "FK_445d00eaaea0e60a017a5ed0c11" FOREIGN KEY ("source_guild_id") REFERENCES "guilds"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + `ALTER TABLE "templates" ADD CONSTRAINT "FK_445d00eaaea0e60a017a5ed0c11" FOREIGN KEY ("source_guild_id") REFERENCES "guilds"("id") ON DELETE CASCADE ON UPDATE NO ACTION` ); } public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "templates" DROP CONSTRAINT "FK_445d00eaaea0e60a017a5ed0c11"`); await queryRunner.query( - `ALTER TABLE "templates" DROP CONSTRAINT "FK_445d00eaaea0e60a017a5ed0c11"`, - ); - await queryRunner.query( - `ALTER TABLE "templates" ADD CONSTRAINT "FK_445d00eaaea0e60a017a5ed0c11" FOREIGN KEY ("source_guild_id") REFERENCES "guilds"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + `ALTER TABLE "templates" ADD CONSTRAINT "FK_445d00eaaea0e60a017a5ed0c11" FOREIGN KEY ("source_guild_id") REFERENCES "guilds"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` ); } } diff --git a/src/util/migration/postgres/1675044825710-webauthn.ts b/src/util/migration/postgres/1675044825710-webauthn.ts index 4b32d6c7..749ff90a 100644 --- a/src/util/migration/postgres/1675044825710-webauthn.ts +++ b/src/util/migration/postgres/1675044825710-webauthn.ts @@ -23,23 +23,17 @@ export class webauthn1675044825710 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query( - `CREATE TABLE "security_keys" ("id" character varying NOT NULL, "user_id" character varying, "key_id" character varying NOT NULL, "public_key" character varying NOT NULL, "counter" integer NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_6e95cdd91779e7cca06d1fff89c" PRIMARY KEY ("id"))`, + `CREATE TABLE "security_keys" ("id" character varying NOT NULL, "user_id" character varying, "key_id" character varying NOT NULL, "public_key" character varying NOT NULL, "counter" integer NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_6e95cdd91779e7cca06d1fff89c" PRIMARY KEY ("id"))` ); + await queryRunner.query(`ALTER TABLE "users" ADD "webauthn_enabled" boolean NOT NULL DEFAULT false`); await queryRunner.query( - `ALTER TABLE "users" ADD "webauthn_enabled" boolean NOT NULL DEFAULT false`, - ); - await queryRunner.query( - `ALTER TABLE "security_keys" ADD CONSTRAINT "FK_24c97d0771cafedce6d7163eaad" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + `ALTER TABLE "security_keys" ADD CONSTRAINT "FK_24c97d0771cafedce6d7163eaad" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION` ); } public async down(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query( - `ALTER TABLE "security_keys" DROP CONSTRAINT "FK_24c97d0771cafedce6d7163eaad"`, - ); - await queryRunner.query( - `ALTER TABLE "users" DROP COLUMN "webauthn_enabled"`, - ); + await queryRunner.query(`ALTER TABLE "security_keys" DROP CONSTRAINT "FK_24c97d0771cafedce6d7163eaad"`); + await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "webauthn_enabled"`); await queryRunner.query(`DROP TABLE "security_keys"`); } } diff --git a/src/util/migration/postgres/1696420827239-guildChannelOrdering.ts b/src/util/migration/postgres/1696420827239-guildChannelOrdering.ts index 6fc80ffd..de876ff9 100644 --- a/src/util/migration/postgres/1696420827239-guildChannelOrdering.ts +++ b/src/util/migration/postgres/1696420827239-guildChannelOrdering.ts @@ -4,31 +4,21 @@ export class guildChannelOrdering1696420827239 implements MigrationInterface { name = "guildChannelOrdering1696420827239"; public async up(queryRunner: QueryRunner): Promise<void> { - const guilds = await queryRunner.query( - `SELECT id FROM guilds`, - undefined, - true, - ); + const guilds = await queryRunner.query(`SELECT id FROM guilds`, undefined, true); - await queryRunner.query( - `ALTER TABLE guilds ADD channel_ordering text NOT NULL DEFAULT '[]'`, - ); + await queryRunner.query(`ALTER TABLE guilds ADD channel_ordering text NOT NULL DEFAULT '[]'`); for (const guild_id of guilds.records.map((x) => x.id)) { const channels: Array<{ position: number; id: string }> = ( - await queryRunner.query( - `SELECT id, position FROM channels WHERE guild_id = $1`, - [guild_id], - true, - ) + await queryRunner.query(`SELECT id, position FROM channels WHERE guild_id = $1`, [guild_id], true) ).records; channels.sort((a, b) => a.position - b.position); - await queryRunner.query( - `UPDATE guilds SET channel_ordering = $1 WHERE id = $2`, - [JSON.stringify(channels.map((x) => x.id)), guild_id], - ); + await queryRunner.query(`UPDATE guilds SET channel_ordering = $1 WHERE id = $2`, [ + JSON.stringify(channels.map((x) => x.id)), + guild_id, + ]); } await queryRunner.query(`ALTER TABLE channels DROP COLUMN position`); diff --git a/src/util/schemas/GuildUpdateSchema.ts b/src/util/schemas/GuildUpdateSchema.ts index 19316380..4f33b888 100644 --- a/src/util/schemas/GuildUpdateSchema.ts +++ b/src/util/schemas/GuildUpdateSchema.ts @@ -18,8 +18,7 @@ import { GuildCreateSchema } from "@spacebar/util"; -export interface GuildUpdateSchema - extends Omit<GuildCreateSchema, "channels" | "name"> { +export interface GuildUpdateSchema extends Omit<GuildCreateSchema, "channels" | "name"> { name?: string; banner?: string | null; splash?: string | null; diff --git a/src/util/schemas/UserGuildSettingsSchema.ts b/src/util/schemas/UserGuildSettingsSchema.ts index 82edae9c..603dbb21 100644 --- a/src/util/schemas/UserGuildSettingsSchema.ts +++ b/src/util/schemas/UserGuildSettingsSchema.ts @@ -19,8 +19,7 @@ import { ChannelOverride, UserGuildSettings } from "@spacebar/util"; // This sucks. I would use a DeepPartial, my own or typeorms, but they both generate inncorect schema -export interface UserGuildSettingsSchema - extends Partial<Omit<UserGuildSettings, "channel_overrides">> { +export interface UserGuildSettingsSchema extends Partial<Omit<UserGuildSettings, "channel_overrides">> { channel_overrides?: { [channel_id: string]: ChannelOverride; }; diff --git a/src/util/schemas/Validator.ts b/src/util/schemas/Validator.ts index 1de511d3..cedc2e73 100644 --- a/src/util/schemas/Validator.ts +++ b/src/util/schemas/Validator.ts @@ -21,14 +21,7 @@ import addFormats from "ajv-formats"; import fs from "fs"; import path from "path"; -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({ diff --git a/src/util/schemas/WebAuthnSchema.ts b/src/util/schemas/WebAuthnSchema.ts index 3f5e0da7..ada20de1 100644 --- a/src/util/schemas/WebAuthnSchema.ts +++ b/src/util/schemas/WebAuthnSchema.ts @@ -28,9 +28,7 @@ export interface CreateWebAuthnCredentialSchema { ticket: string; } -export type WebAuthnPostSchema = - | GenerateWebAuthnCredentialsSchema - | CreateWebAuthnCredentialSchema; +export type WebAuthnPostSchema = GenerateWebAuthnCredentialsSchema | CreateWebAuthnCredentialSchema; export interface WebAuthnTotpSchema { code: string; diff --git a/src/util/schemas/responses/APIErrorOrCaptchaResponse.ts b/src/util/schemas/responses/APIErrorOrCaptchaResponse.ts index c9a0e5be..069a05e0 100644 --- a/src/util/schemas/responses/APIErrorOrCaptchaResponse.ts +++ b/src/util/schemas/responses/APIErrorOrCaptchaResponse.ts @@ -1,6 +1,4 @@ import { APIErrorResponse } from "./APIErrorResponse"; import { CaptchaRequiredResponse } from "./CaptchaRequiredResponse"; -export type APIErrorOrCaptchaResponse = - | CaptchaRequiredResponse - | APIErrorResponse; +export type APIErrorOrCaptchaResponse = CaptchaRequiredResponse | APIErrorResponse; diff --git a/src/util/schemas/responses/GuildMessagesSearchResponse.ts b/src/util/schemas/responses/GuildMessagesSearchResponse.ts index 0b6248b7..b8e9bcc4 100644 --- a/src/util/schemas/responses/GuildMessagesSearchResponse.ts +++ b/src/util/schemas/responses/GuildMessagesSearchResponse.ts @@ -1,10 +1,4 @@ -import { - Attachment, - Embed, - MessageType, - PublicUser, - Role, -} from "../../entities"; +import { Attachment, Embed, MessageType, PublicUser, Role } from "../../entities"; export interface GuildMessagesSearchMessage { id: string; diff --git a/src/util/schemas/responses/GuildVanityUrl.ts b/src/util/schemas/responses/GuildVanityUrl.ts index ff37bf4e..6d67a932 100644 --- a/src/util/schemas/responses/GuildVanityUrl.ts +++ b/src/util/schemas/responses/GuildVanityUrl.ts @@ -7,10 +7,7 @@ export interface GuildVanityUrlNoInvite { code: null; } -export type GuildVanityUrlResponse = - | GuildVanityUrl - | GuildVanityUrl[] - | GuildVanityUrlNoInvite; +export type GuildVanityUrlResponse = GuildVanityUrl | GuildVanityUrl[] | GuildVanityUrlNoInvite; export interface GuildVanityUrlCreateResponse { code: string; diff --git a/src/util/schemas/responses/UserProfileResponse.ts b/src/util/schemas/responses/UserProfileResponse.ts index eba7cbcc..0dc58629 100644 --- a/src/util/schemas/responses/UserProfileResponse.ts +++ b/src/util/schemas/responses/UserProfileResponse.ts @@ -1,27 +1,15 @@ -import { - Member, - PublicConnectedAccount, - PublicMember, - PublicUser, - User, -} from "@spacebar/util"; +import { Member, PublicConnectedAccount, PublicMember, PublicUser, User } from "@spacebar/util"; export type MutualGuild = { id: string; nick?: string; }; -export type PublicMemberProfile = Pick< - Member, - "banner" | "bio" | "guild_id" -> & { +export type PublicMemberProfile = Pick<Member, "banner" | "bio" | "guild_id"> & { accent_color: null; // TODO }; -export type UserProfile = Pick< - User, - "bio" | "accent_color" | "banner" | "pronouns" | "theme_colors" ->; +export type UserProfile = Pick<User, "bio" | "accent_color" | "banner" | "pronouns" | "theme_colors">; export interface UserProfileResponse { user: PublicUser; diff --git a/src/util/util/ApiError.ts b/src/util/util/ApiError.ts index 4c7b909f..e0621fba 100644 --- a/src/util/util/ApiError.ts +++ b/src/util/util/ApiError.ts @@ -21,34 +21,23 @@ export class ApiError extends Error { readonly message: string, public readonly code: number, public readonly httpStatus: number = 400, - public readonly defaultParams?: string[], + public readonly defaultParams?: string[] ) { super(message); } withDefaultParams(): ApiError { if (this.defaultParams) - return new ApiError( - applyParamsToString(this.message, this.defaultParams), - this.code, - this.httpStatus, - ); + return new ApiError(applyParamsToString(this.message, this.defaultParams), this.code, this.httpStatus); return this; } withParams(...params: (string | number)[]): ApiError { - return new ApiError( - applyParamsToString(this.message, params), - this.code, - this.httpStatus, - ); + return new ApiError(applyParamsToString(this.message, params), this.code, this.httpStatus); } } -export function applyParamsToString( - s: string, - params: (string | number)[], -): string { +export function applyParamsToString(s: string, params: (string | number)[]): string { let newString = s; params.forEach((a) => { newString = newString.replace("{}", "" + a); diff --git a/src/util/util/AutoUpdate.ts b/src/util/util/AutoUpdate.ts index 2af5cf1c..39173d94 100644 --- a/src/util/util/AutoUpdate.ts +++ b/src/util/util/AutoUpdate.ts @@ -37,17 +37,14 @@ export function enableAutoUpdate(opts: { }) { if (!opts.checkInterval) return; const interval = 1000 * 60 * 60 * 24; - if (typeof opts.checkInterval === "number") - opts.checkInterval = 1000 * interval; + if (typeof opts.checkInterval === "number") opts.checkInterval = 1000 * interval; const i = setInterval(async () => { const currentVersion = await getCurrentVersion(opts.path); const latestVersion = await getLatestVersion(opts.packageJsonLink); if (currentVersion !== latestVersion) { clearInterval(i); - console.log( - `[Auto Update] Current version (${currentVersion}) is out of date, updating ...`, - ); + console.log(`[Auto Update] Current version (${currentVersion}) is out of date, updating ...`); await download(opts.downloadUrl, opts.path); } }, interval); @@ -64,7 +61,7 @@ export function enableAutoUpdate(opts: { } else { console.log(`[Auto update] aborted`); } - }, + } ); } }); diff --git a/src/util/util/BitField.ts b/src/util/util/BitField.ts index d8758327..85de8fb7 100644 --- a/src/util/util/BitField.ts +++ b/src/util/util/BitField.ts @@ -4,12 +4,7 @@ // Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah // @fc-license-skip -export type BitFieldResolvable = - | number - | bigint - | BitField - | string - | BitFieldResolvable[]; +export type BitFieldResolvable = number | bigint | BitField | string | BitFieldResolvable[]; /** * Data structure that makes it easy to interact with a bitfield. @@ -97,8 +92,7 @@ export class BitField { */ serialize() { const serialized: Record<string, boolean> = {}; - for (const [flag, bit] of Object.entries(BitField.FLAGS)) - serialized[flag] = this.has(bit); + for (const [flag, bit] of Object.entries(BitField.FLAGS)) serialized[flag] = this.has(bit); return serialized; } @@ -144,11 +138,7 @@ export class BitField { else bit = BigInt(bit); } - if ( - (typeof bit === "number" || typeof bit === "bigint") && - bit >= BigInt(0) - ) - return BigInt(bit); + if ((typeof bit === "number" || typeof bit === "bigint") && bit >= BigInt(0)) return BigInt(bit); if (bit instanceof BitField) return bit.bitfield; @@ -156,9 +146,7 @@ export class BitField { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const resolve = this.constructor?.resolve || this.resolve; - return bit - .map((p) => resolve.call(this, p)) - .reduce((prev, p) => BigInt(prev) | BigInt(p), BigInt(0)); + return bit.map((p) => resolve.call(this, p)).reduce((prev, p) => BigInt(prev) | BigInt(p), BigInt(0)); } throw new RangeError("BITFIELD_INVALID: " + bit); diff --git a/src/util/util/Config.ts b/src/util/util/Config.ts index 2624a60a..c26a5ed5 100644 --- a/src/util/util/Config.ts +++ b/src/util/util/Config.ts @@ -41,9 +41,7 @@ export const Config = { } else { console.log(`[Config] Using CONFIG_PATH rather than database`); if (existsSync(process.env.CONFIG_PATH)) { - const file = JSON.parse( - (await fs.readFile(process.env.CONFIG_PATH)).toString(), - ); + const file = JSON.parse((await fs.readFile(process.env.CONFIG_PATH)).toString()); config = file; } else config = new ConfigValue(); pairs = generatePairs(config); @@ -82,7 +80,7 @@ const generatePairs = (obj: object | null, key = ""): ConfigEntity[] => { return Object.keys(obj) .map((k) => // eslint-disable-next-line @typescript-eslint/no-explicit-any - generatePairs((obj as any)[k], key ? `${key}_${k}` : k), + generatePairs((obj as any)[k], key ? `${key}_${k}` : k) ) .flat(); } @@ -94,8 +92,7 @@ const generatePairs = (obj: object | null, key = ""): ConfigEntity[] => { }; async function applyConfig(val: ConfigValue) { - if (process.env.CONFIG_PATH) - await fs.writeFile(overridePath, JSON.stringify(val, null, 4)); + if (process.env.CONFIG_PATH) await fs.writeFile(overridePath, JSON.stringify(val, null, 4)); else { const pairs = generatePairs(val); await Promise.all(pairs.map((pair) => pair.save())); @@ -116,8 +113,7 @@ function pairsToConfig(pairs: ConfigEntity[]) { let i = 0; for (const key of keys) { - if (!isNaN(Number(key)) && !prevObj[prev]?.length) - prevObj[prev] = obj = []; + if (!isNaN(Number(key)) && !prevObj[prev]?.length) prevObj[prev] = obj = []; if (i++ === keys.length - 1) obj[key] = p.value; else if (!obj[key]) obj[key] = {}; @@ -142,18 +138,14 @@ const validateConfig = async () => { if (!found) continue; config[row] = found; } catch (e) { - console.error( - `Config key '${config[row].key}' has invalid JSON value : ${ - (e as Error)?.message - }`, - ); + console.error(`Config key '${config[row].key}' has invalid JSON value : ${(e as Error)?.message}`); hasErrored = true; } } if (hasErrored) { console.error( - "Your config has invalid values. Fix them first https://docs.spacebar.chat/setup/server/configuration", + "Your config has invalid values. Fix them first https://docs.spacebar.chat/setup/server/configuration" ); process.exit(1); } diff --git a/src/util/util/Constants.ts b/src/util/util/Constants.ts index e68bb0b7..151c15f6 100644 --- a/src/util/util/Constants.ts +++ b/src/util/util/Constants.ts @@ -178,13 +178,7 @@ export const ShardEvents = { * sidebar for more information.</warn> * @typedef {string} PartialType */ -export const PartialTypes = keyMirror([ - "USER", - "CHANNEL", - "GUILD_MEMBER", - "MESSAGE", - "REACTION", -]); +export const PartialTypes = keyMirror(["USER", "CHANNEL", "GUILD_MEMBER", "MESSAGE", "REACTION"]); /** * The type of a websocket message event, e.g. `MESSAGE_CREATE`. Here are the available events: @@ -315,7 +309,7 @@ export const MessageTypes = [ * @typedef {string} SystemMessageType */ export const SystemMessageTypes = MessageTypes.filter( - (type: string | null) => type && type !== "DEFAULT" && type !== "REPLY", + (type: string | null) => type && type !== "DEFAULT" && type !== "REPLY" ); /** @@ -329,14 +323,7 @@ export const SystemMessageTypes = MessageTypes.filter( * * COMPETING * @typedef {string} ActivityType */ -export const ActivityTypes = [ - "PLAYING", - "STREAMING", - "LISTENING", - "WATCHING", - "CUSTOM_STATUS", - "COMPETING", -]; +export const ActivityTypes = ["PLAYING", "STREAMING", "LISTENING", "WATCHING", "CUSTOM_STATUS", "COMPETING"]; export const ChannelTypes = { TEXT: 0, @@ -392,11 +379,7 @@ export const Colors = { * * ALL_MEMBERS * @typedef {string} ExplicitContentFilterLevel */ -export const ExplicitContentFilterLevels = [ - "DISABLED", - "MEMBERS_WITHOUT_ROLES", - "ALL_MEMBERS", -]; +export const ExplicitContentFilterLevels = ["DISABLED", "MEMBERS_WITHOUT_ROLES", "ALL_MEMBERS"]; /** * The value set for the verification levels for a guild: @@ -407,13 +390,7 @@ export const ExplicitContentFilterLevels = [ * * VERY_HIGH * @typedef {string} VerificationLevel */ -export const VerificationLevels = [ - "NONE", - "LOW", - "MEDIUM", - "HIGH", - "VERY_HIGH", -]; +export const VerificationLevels = ["NONE", "LOW", "MEDIUM", "HIGH", "VERY_HIGH"]; /** * An error encountered while performing an API request. Here are the potential errors: @@ -558,10 +535,7 @@ export const VerificationLevels = [ */ export const DiscordApiErrors = { //https://discord.com/developers/docs/topics/opcodes-and-status-codes#json-json-error-codes - GENERAL_ERROR: new ApiError( - "General error (such as a malformed request body, amongst other things)", - 0, - ), + GENERAL_ERROR: new ApiError("General error (such as a malformed request body, amongst other things)", 0), UNKNOWN_ACCOUNT: new ApiError("Unknown account", 10001), UNKNOWN_APPLICATION: new ApiError("Unknown application", 10002), UNKNOWN_CHANNEL: new ApiError("Unknown channel", 10003), @@ -587,420 +561,188 @@ export const DiscordApiErrors = { UNKNOWN_BUILD: new ApiError("Unknown build", 10030), UNKNOWN_LOBBY: new ApiError("Unknown lobby", 10031), UNKNOWN_BRANCH: new ApiError("Unknown branch", 10032), - UNKNOWN_STORE_DIRECTORY_LAYOUT: new ApiError( - "Unknown store directory layout", - 10033, - ), + UNKNOWN_STORE_DIRECTORY_LAYOUT: new ApiError("Unknown store directory layout", 10033), UNKNOWN_REDISTRIBUTABLE: new ApiError("Unknown redistributable", 10036), UNKNOWN_GIFT_CODE: new ApiError("Unknown gift code", 10038), UNKNOWN_STREAM: new ApiError("Unknown stream", 10049), - UNKNOWN_PREMIUM_SERVER_SUBSCRIBE_COOLDOWN: new ApiError( - "Unknown premium server subscribe cooldown", - 10050, - ), + UNKNOWN_PREMIUM_SERVER_SUBSCRIBE_COOLDOWN: new ApiError("Unknown premium server subscribe cooldown", 10050), UNKNOWN_GUILD_TEMPLATE: new ApiError("Unknown guild template", 10057), - UNKNOWN_DISCOVERABLE_SERVER_CATEGORY: new ApiError( - "Unknown discoverable server category", - 10059, - ), + UNKNOWN_DISCOVERABLE_SERVER_CATEGORY: new ApiError("Unknown discoverable server category", 10059), UNKNOWN_STICKER: new ApiError("Unknown sticker", 10060), UNKNOWN_INTERACTION: new ApiError("Unknown interaction", 10062), - UNKNOWN_APPLICATION_COMMAND: new ApiError( - "Unknown application command", - 10063, - ), - UNKNOWN_APPLICATION_COMMAND_PERMISSIONS: new ApiError( - "Unknown application command permissions", - 10066, - ), + UNKNOWN_APPLICATION_COMMAND: new ApiError("Unknown application command", 10063), + UNKNOWN_APPLICATION_COMMAND_PERMISSIONS: new ApiError("Unknown application command permissions", 10066), UNKNOWN_STAGE_INSTANCE: new ApiError("Unknown Stage Instance", 10067), - UNKNOWN_GUILD_MEMBER_VERIFICATION_FORM: new ApiError( - "Unknown Guild Member Verification Form", - 10068, - ), - UNKNOWN_GUILD_WELCOME_SCREEN: new ApiError( - "Unknown Guild Welcome Screen", - 10069, - ), - UNKNOWN_GUILD_SCHEDULED_EVENT: new ApiError( - "Unknown Guild Scheduled Event", - 10070, - ), - UNKNOWN_GUILD_SCHEDULED_EVENT_USER: new ApiError( - "Unknown Guild Scheduled Event User", - 10071, - ), - BOT_PROHIBITED_ENDPOINT: new ApiError( - "Bots cannot use this endpoint", - 20001, - ), + UNKNOWN_GUILD_MEMBER_VERIFICATION_FORM: new ApiError("Unknown Guild Member Verification Form", 10068), + UNKNOWN_GUILD_WELCOME_SCREEN: new ApiError("Unknown Guild Welcome Screen", 10069), + UNKNOWN_GUILD_SCHEDULED_EVENT: new ApiError("Unknown Guild Scheduled Event", 10070), + UNKNOWN_GUILD_SCHEDULED_EVENT_USER: new ApiError("Unknown Guild Scheduled Event User", 10071), + BOT_PROHIBITED_ENDPOINT: new ApiError("Bots cannot use this endpoint", 20001), BOT_ONLY_ENDPOINT: new ApiError("Only bots can use this endpoint", 20002), EXPLICIT_CONTENT_CANNOT_BE_SENT_TO_RECIPIENT: new ApiError( "Explicit content cannot be sent to the desired recipient(s)", - 20009, + 20009 ), ACTION_NOT_AUTHORIZED_ON_APPLICATION: new ApiError( "You are not authorized to perform this action on this application", - 20012, - ), - SLOWMODE_RATE_LIMIT: new ApiError( - "This action cannot be performed due to slowmode rate limit", - 20016, - ), - ONLY_OWNER: new ApiError( - "Only the owner of this account can perform this action", - 20018, - ), - ANNOUNCEMENT_RATE_LIMITS: new ApiError( - "This message cannot be edited due to announcement rate limits", - 20022, - ), - CHANNEL_WRITE_RATELIMIT: new ApiError( - "The channel you are writing has hit the write rate limit", - 20028, + 20012 ), + SLOWMODE_RATE_LIMIT: new ApiError("This action cannot be performed due to slowmode rate limit", 20016), + ONLY_OWNER: new ApiError("Only the owner of this account can perform this action", 20018), + ANNOUNCEMENT_RATE_LIMITS: new ApiError("This message cannot be edited due to announcement rate limits", 20022), + CHANNEL_WRITE_RATELIMIT: new ApiError("The channel you are writing has hit the write rate limit", 20028), WORDS_NOT_ALLOWED: new ApiError( "Your Stage topic, server name, server description, or channel names contain words that are not allowed", - 20031, - ), - GUILD_PREMIUM_LEVEL_TOO_LOW: new ApiError( - "Guild premium subscription level too low", - 20035, - ), - MAXIMUM_GUILDS: new ApiError( - "Maximum number of guilds reached ({})", - 30001, - undefined, - ["100"], - ), - MAXIMUM_FRIENDS: new ApiError( - "Maximum number of friends reached ({})", - 30002, - undefined, - ["1000"], - ), - MAXIMUM_PINS: new ApiError( - "Maximum number of pins reached for the channel ({})", - 30003, - undefined, - ["50"], - ), - MAXIMUM_NUMBER_OF_RECIPIENTS_REACHED: new ApiError( - "Maximum number of recipients reached ({})", - 30004, - undefined, - ["10"], - ), - MAXIMUM_ROLES: new ApiError( - "Maximum number of guild roles reached ({})", - 30005, - undefined, - ["250"], - ), - MAXIMUM_WEBHOOKS: new ApiError( - "Maximum number of webhooks reached ({})", - 30007, - undefined, - ["10"], - ), - MAXIMUM_NUMBER_OF_EMOJIS_REACHED: new ApiError( - "Maximum number of emojis reached", - 30008, - ), - MAXIMUM_REACTIONS: new ApiError( - "Maximum number of reactions reached ({})", - 30010, - undefined, - ["20"], - ), - MAXIMUM_CHANNELS: new ApiError( - "Maximum number of guild channels reached ({})", - 30013, - undefined, - ["500"], - ), - MAXIMUM_ATTACHMENTS: new ApiError( - "Maximum number of attachments in a message reached ({})", - 30015, - undefined, - ["10"], - ), - MAXIMUM_INVITES: new ApiError( - "Maximum number of invites reached ({})", - 30016, - undefined, - ["1000"], - ), - MAXIMUM_ANIMATED_EMOJIS: new ApiError( - "Maximum number of animated emojis reached", - 30018, - ), - MAXIMUM_SERVER_MEMBERS: new ApiError( - "Maximum number of server members reached", - 30019, - ), + 20031 + ), + GUILD_PREMIUM_LEVEL_TOO_LOW: new ApiError("Guild premium subscription level too low", 20035), + MAXIMUM_GUILDS: new ApiError("Maximum number of guilds reached ({})", 30001, undefined, ["100"]), + MAXIMUM_FRIENDS: new ApiError("Maximum number of friends reached ({})", 30002, undefined, ["1000"]), + MAXIMUM_PINS: new ApiError("Maximum number of pins reached for the channel ({})", 30003, undefined, ["50"]), + MAXIMUM_NUMBER_OF_RECIPIENTS_REACHED: new ApiError("Maximum number of recipients reached ({})", 30004, undefined, [ + "10", + ]), + MAXIMUM_ROLES: new ApiError("Maximum number of guild roles reached ({})", 30005, undefined, ["250"]), + MAXIMUM_WEBHOOKS: new ApiError("Maximum number of webhooks reached ({})", 30007, undefined, ["10"]), + MAXIMUM_NUMBER_OF_EMOJIS_REACHED: new ApiError("Maximum number of emojis reached", 30008), + MAXIMUM_REACTIONS: new ApiError("Maximum number of reactions reached ({})", 30010, undefined, ["20"]), + MAXIMUM_CHANNELS: new ApiError("Maximum number of guild channels reached ({})", 30013, undefined, ["500"]), + MAXIMUM_ATTACHMENTS: new ApiError("Maximum number of attachments in a message reached ({})", 30015, undefined, [ + "10", + ]), + MAXIMUM_INVITES: new ApiError("Maximum number of invites reached ({})", 30016, undefined, ["1000"]), + MAXIMUM_ANIMATED_EMOJIS: new ApiError("Maximum number of animated emojis reached", 30018), + MAXIMUM_SERVER_MEMBERS: new ApiError("Maximum number of server members reached", 30019), MAXIMUM_SERVER_CATEGORIES: new ApiError( "Maximum number of server categories has been reached ({})", 30030, undefined, - ["5"], - ), - GUILD_ALREADY_HAS_TEMPLATE: new ApiError( - "Guild already has a template", - 30031, - ), - MAXIMUM_THREAD_PARTICIPANTS: new ApiError( - "Max number of thread participants has been reached", - 30033, + ["5"] ), + GUILD_ALREADY_HAS_TEMPLATE: new ApiError("Guild already has a template", 30031), + MAXIMUM_THREAD_PARTICIPANTS: new ApiError("Max number of thread participants has been reached", 30033), MAXIMUM_BANS_FOR_NON_GUILD_MEMBERS: new ApiError( "Maximum number of bans for non-guild members have been exceeded", - 30035, - ), - MAXIMUM_BANS_FETCHES: new ApiError( - "Maximum number of bans fetches has been reached", - 30037, + 30035 ), + MAXIMUM_BANS_FETCHES: new ApiError("Maximum number of bans fetches has been reached", 30037), MAXIMUM_STICKERS: new ApiError("Maximum number of stickers reached", 30039), - MAXIMUM_PRUNE_REQUESTS: new ApiError( - "Maximum number of prune requests has been reached. Try again later", - 30040, - ), - UNAUTHORIZED: new ApiError( - "Unauthorized. Provide a valid token and try again", - 40001, - ), + MAXIMUM_PRUNE_REQUESTS: new ApiError("Maximum number of prune requests has been reached. Try again later", 30040), + UNAUTHORIZED: new ApiError("Unauthorized. Provide a valid token and try again", 40001), ACCOUNT_VERIFICATION_REQUIRED: new ApiError( "You need to verify your account in order to perform this action", - 40002, - ), - OPENING_DIRECT_MESSAGES_TOO_FAST: new ApiError( - "You are opening direct messages too fast", - 40003, - ), - REQUEST_ENTITY_TOO_LARGE: new ApiError( - "Request entity too large. Try sending something smaller in size", - 40005, - ), - FEATURE_TEMPORARILY_DISABLED: new ApiError( - "This feature has been temporarily disabled server-side", - 40006, + 40002 ), + OPENING_DIRECT_MESSAGES_TOO_FAST: new ApiError("You are opening direct messages too fast", 40003), + REQUEST_ENTITY_TOO_LARGE: new ApiError("Request entity too large. Try sending something smaller in size", 40005), + FEATURE_TEMPORARILY_DISABLED: new ApiError("This feature has been temporarily disabled server-side", 40006), USER_BANNED: new ApiError("The user is banned from this guild", 40007), - CONNECTION_REVOKED: new ApiError( - "The connection has been revoked", - 40012, - 400, - ), - TARGET_USER_IS_NOT_CONNECTED_TO_VOICE: new ApiError( - "Target user is not connected to voice", - 40032, - ), - ALREADY_CROSSPOSTED: new ApiError( - "This message has already been crossposted", - 40033, - ), - APPLICATION_COMMAND_ALREADY_EXISTS: new ApiError( - "An application command with that name already exists", - 40041, - ), + CONNECTION_REVOKED: new ApiError("The connection has been revoked", 40012, 400), + TARGET_USER_IS_NOT_CONNECTED_TO_VOICE: new ApiError("Target user is not connected to voice", 40032), + ALREADY_CROSSPOSTED: new ApiError("This message has already been crossposted", 40033), + APPLICATION_COMMAND_ALREADY_EXISTS: new ApiError("An application command with that name already exists", 40041), MISSING_ACCESS: new ApiError("Missing access", 50001), INVALID_ACCOUNT_TYPE: new ApiError("Invalid account type", 50002), - CANNOT_EXECUTE_ON_DM: new ApiError( - "Cannot execute action on a DM channel", - 50003, - ), + CANNOT_EXECUTE_ON_DM: new ApiError("Cannot execute action on a DM channel", 50003), EMBED_DISABLED: new ApiError("Guild widget disabled", 50004), - CANNOT_EDIT_MESSAGE_BY_OTHER: new ApiError( - "Cannot edit a message authored by another user", - 50005, - ), - CANNOT_SEND_EMPTY_MESSAGE: new ApiError( - "Cannot send an empty message", - 50006, - ), - CANNOT_MESSAGE_USER: new ApiError( - "Cannot send messages to this user", - 50007, - ), - CANNOT_SEND_MESSAGES_IN_VOICE_CHANNEL: new ApiError( - "Cannot send messages in a voice channel", - 50008, - ), + CANNOT_EDIT_MESSAGE_BY_OTHER: new ApiError("Cannot edit a message authored by another user", 50005), + CANNOT_SEND_EMPTY_MESSAGE: new ApiError("Cannot send an empty message", 50006), + CANNOT_MESSAGE_USER: new ApiError("Cannot send messages to this user", 50007), + CANNOT_SEND_MESSAGES_IN_VOICE_CHANNEL: new ApiError("Cannot send messages in a voice channel", 50008), CHANNEL_VERIFICATION_LEVEL_TOO_HIGH: new ApiError( "Channel verification level is too high for you to gain access", - 50009, - ), - OAUTH2_APPLICATION_BOT_ABSENT: new ApiError( - "OAuth2 application does not have a bot", - 50010, - ), - MAXIMUM_OAUTH2_APPLICATIONS: new ApiError( - "OAuth2 application limit reached", - 50011, + 50009 ), + OAUTH2_APPLICATION_BOT_ABSENT: new ApiError("OAuth2 application does not have a bot", 50010), + MAXIMUM_OAUTH2_APPLICATIONS: new ApiError("OAuth2 application limit reached", 50011), INVALID_OAUTH_STATE: new ApiError("Invalid OAuth2 state", 50012), - MISSING_PERMISSIONS: new ApiError( - "You lack permissions to perform that action ({})", - 50013, - undefined, - [""], - ), - INVALID_AUTHENTICATION_TOKEN: new ApiError( - "Invalid authentication token provided", - 50014, - ), + MISSING_PERMISSIONS: new ApiError("You lack permissions to perform that action ({})", 50013, undefined, [""]), + INVALID_AUTHENTICATION_TOKEN: new ApiError("Invalid authentication token provided", 50014), NOTE_TOO_LONG: new ApiError("Note was too long", 50015), INVALID_BULK_DELETE_QUANTITY: new ApiError( "Provided too few or too many messages to delete. Must provide at least {} and fewer than {} messages to delete", 50016, undefined, - ["2", "100"], + ["2", "100"] ), CANNOT_PIN_MESSAGE_IN_OTHER_CHANNEL: new ApiError( "A message can only be pinned to the channel it was sent in", - 50019, - ), - INVALID_OR_TAKEN_INVITE_CODE: new ApiError( - "Invite code was either invalid or taken", - 50020, - ), - CANNOT_EXECUTE_ON_SYSTEM_MESSAGE: new ApiError( - "Cannot execute action on a system message", - 50021, - ), - CANNOT_EXECUTE_ON_THIS_CHANNEL_TYPE: new ApiError( - "Cannot execute action on this channel type", - 50024, - ), - INVALID_OAUTH_TOKEN: new ApiError( - "Invalid OAuth2 access token provided", - 50025, - ), - MISSING_REQUIRED_OAUTH2_SCOPE: new ApiError( - "Missing required OAuth2 scope", - 50026, - ), - INVALID_WEBHOOK_TOKEN_PROVIDED: new ApiError( - "Invalid webhook token provided", - 50027, - ), + 50019 + ), + INVALID_OR_TAKEN_INVITE_CODE: new ApiError("Invite code was either invalid or taken", 50020), + CANNOT_EXECUTE_ON_SYSTEM_MESSAGE: new ApiError("Cannot execute action on a system message", 50021), + CANNOT_EXECUTE_ON_THIS_CHANNEL_TYPE: new ApiError("Cannot execute action on this channel type", 50024), + INVALID_OAUTH_TOKEN: new ApiError("Invalid OAuth2 access token provided", 50025), + MISSING_REQUIRED_OAUTH2_SCOPE: new ApiError("Missing required OAuth2 scope", 50026), + INVALID_WEBHOOK_TOKEN_PROVIDED: new ApiError("Invalid webhook token provided", 50027), INVALID_ROLE: new ApiError("Invalid role", 50028), INVALID_RECIPIENT: new ApiError("Invalid Recipient(s)", 50033), - BULK_DELETE_MESSAGE_TOO_OLD: new ApiError( - "A message provided was too old to bulk delete", - 50034, - ), + BULK_DELETE_MESSAGE_TOO_OLD: new ApiError("A message provided was too old to bulk delete", 50034), INVALID_FORM_BODY: new ApiError( "Invalid form body (returned for both application/json and multipart/form-data bodies), or invalid Content-Type provided", - 50035, + 50035 ), INVITE_ACCEPTED_TO_GUILD_NOT_CONTAINING_BOT: new ApiError( "An invite was accepted to a guild the application's bot is not in", - 50036, + 50036 ), INVALID_API_VERSION: new ApiError("Invalid API version provided", 50041), - FILE_EXCEEDS_MAXIMUM_SIZE: new ApiError( - "File uploaded exceeds the maximum size", - 50045, - ), + FILE_EXCEEDS_MAXIMUM_SIZE: new ApiError("File uploaded exceeds the maximum size", 50045), INVALID_FILE_UPLOADED: new ApiError("Invalid file uploaded", 50046), - CANNOT_SELF_REDEEM_GIFT: new ApiError( - "Cannot self-redeem this gift", - 50054, - ), - PAYMENT_SOURCE_REQUIRED: new ApiError( - "Payment source required to redeem gift", - 50070, - ), + CANNOT_SELF_REDEEM_GIFT: new ApiError("Cannot self-redeem this gift", 50054), + PAYMENT_SOURCE_REQUIRED: new ApiError("Payment source required to redeem gift", 50070), CANNOT_DELETE_COMMUNITY_REQUIRED_CHANNEL: new ApiError( "Cannot delete a channel required for Community guilds", - 50074, + 50074 ), INVALID_STICKER_SENT: new ApiError("Invalid sticker sent", 50081), CANNOT_EDIT_ARCHIVED_THREAD: new ApiError( "Tried to perform an operation on an archived thread, such as editing a message or adding a user to the thread", - 50083, - ), - INVALID_THREAD_NOTIFICATION_SETTINGS: new ApiError( - "Invalid thread notification settings", - 50084, + 50083 ), + INVALID_THREAD_NOTIFICATION_SETTINGS: new ApiError("Invalid thread notification settings", 50084), BEFORE_EARLIER_THAN_THREAD_CREATION_DATE: new ApiError( "before value is earlier than the thread creation date", - 50085, - ), - SERVER_NOT_AVAILABLE_IN_YOUR_LOCATION: new ApiError( - "This server is not available in your location", - 50095, + 50085 ), + SERVER_NOT_AVAILABLE_IN_YOUR_LOCATION: new ApiError("This server is not available in your location", 50095), SERVER_NEEDS_MONETIZATION_ENABLED: new ApiError( "This server needs monetization enabled in order to perform this action", - 50097, - ), - TWO_FACTOR_REQUIRED: new ApiError( - "Two factor is required for this operation", - 60003, - ), - NO_USERS_WITH_DISCORDTAG_EXIST: new ApiError( - "No users with DiscordTag exist", - 80004, + 50097 ), + TWO_FACTOR_REQUIRED: new ApiError("Two factor is required for this operation", 60003), + NO_USERS_WITH_DISCORDTAG_EXIST: new ApiError("No users with DiscordTag exist", 80004), REACTION_BLOCKED: new ApiError("Reaction was blocked", 90001), - RESOURCE_OVERLOADED: new ApiError( - "API resource is currently overloaded. Try again a little later", - 130000, - ), + RESOURCE_OVERLOADED: new ApiError("API resource is currently overloaded. Try again a little later", 130000), STAGE_ALREADY_OPEN: new ApiError("The Stage is already open", 150006), - THREAD_ALREADY_CREATED_FOR_THIS_MESSAGE: new ApiError( - "A thread has already been created for this message", - 160004, - ), + THREAD_ALREADY_CREATED_FOR_THIS_MESSAGE: new ApiError("A thread has already been created for this message", 160004), THREAD_IS_LOCKED: new ApiError("Thread is locked", 160005), - MAXIMUM_NUMBER_OF_ACTIVE_THREADS: new ApiError( - "Maximum number of active threads reached", - 160006, - ), + MAXIMUM_NUMBER_OF_ACTIVE_THREADS: new ApiError("Maximum number of active threads reached", 160006), MAXIMUM_NUMBER_OF_ACTIVE_ANNOUNCEMENT_THREADS: new ApiError( "Maximum number of active announcement threads reached", - 160007, - ), - INVALID_JSON_FOR_UPLOADED_LOTTIE_FILE: new ApiError( - "Invalid JSON for uploaded Lottie file", - 170001, + 160007 ), + INVALID_JSON_FOR_UPLOADED_LOTTIE_FILE: new ApiError("Invalid JSON for uploaded Lottie file", 170001), LOTTIES_CANNOT_CONTAIN_RASTERIZED_IMAGES: new ApiError( "Uploaded Lotties cannot contain rasterized images such as PNG or JPEG", - 170002, - ), - STICKER_MAXIMUM_FRAMERATE: new ApiError( - "Sticker maximum framerate exceeded", - 170003, - ), - STICKER_MAXIMUM_FRAME_COUNT: new ApiError( - "Sticker frame count exceeds maximum of {} frames", - 170004, - undefined, - ["1000"], - ), - LOTTIE_ANIMATION_MAXIMUM_DIMENSIONS: new ApiError( - "Lottie animation maximum dimensions exceeded", - 170005, + 170002 ), + STICKER_MAXIMUM_FRAMERATE: new ApiError("Sticker maximum framerate exceeded", 170003), + STICKER_MAXIMUM_FRAME_COUNT: new ApiError("Sticker frame count exceeds maximum of {} frames", 170004, undefined, [ + "1000", + ]), + LOTTIE_ANIMATION_MAXIMUM_DIMENSIONS: new ApiError("Lottie animation maximum dimensions exceeded", 170005), STICKER_FRAME_RATE_TOO_SMALL_OR_TOO_LARGE: new ApiError( "Sticker frame rate is either too small or too large", - 170006, + 170006 ), STICKER_ANIMATION_DURATION_MAXIMUM: new ApiError( "Sticker animation duration exceeds maximum of {} seconds", 170007, undefined, - ["5"], - ), - AUTOMODERATOR_BLOCK: new ApiError( - "Message was blocked by automatic moderation", - 200000, + ["5"] ), + AUTOMODERATOR_BLOCK: new ApiError("Message was blocked by automatic moderation", 200000), //Other errors UNKNOWN_VOICE_STATE: new ApiError("Unknown Voice State", 10065, 404), @@ -1010,97 +752,30 @@ export const DiscordApiErrors = { * An error encountered while performing an API request (Spacebar only). Here are the potential errors: */ export const SpacebarApiErrors = { - MANUALLY_TRIGGERED_ERROR: new ApiError( - "This is an artificial error", - 1, - 500, - ), - PREMIUM_DISABLED_FOR_GUILD: new ApiError( - "This guild cannot be boosted", - 25001, - ), - NO_FURTHER_PREMIUM: new ApiError( - "This guild does not receive further boosts", - 25002, - ), - GUILD_PREMIUM_DISABLED_FOR_YOU: new ApiError( - "This guild cannot be boosted by you", - 25003, - 403, - ), + MANUALLY_TRIGGERED_ERROR: new ApiError("This is an artificial error", 1, 500), + PREMIUM_DISABLED_FOR_GUILD: new ApiError("This guild cannot be boosted", 25001), + NO_FURTHER_PREMIUM: new ApiError("This guild does not receive further boosts", 25002), + GUILD_PREMIUM_DISABLED_FOR_YOU: new ApiError("This guild cannot be boosted by you", 25003, 403), CANNOT_FRIEND_SELF: new ApiError("Cannot friend oneself", 25009), - USER_SPECIFIC_INVITE_WRONG_RECIPIENT: new ApiError( - "This invite is not meant for you", - 25010, - ), + USER_SPECIFIC_INVITE_WRONG_RECIPIENT: new ApiError("This invite is not meant for you", 25010), USER_SPECIFIC_INVITE_FAILED: new ApiError("Failed to invite user", 25011), - CANNOT_MODIFY_USER_GROUP: new ApiError( - "This user cannot manipulate this group", - 25050, - 403, - ), - CANNOT_REMOVE_SELF_FROM_GROUP: new ApiError( - "This user cannot remove oneself from user group", - 25051, - ), - CANNOT_BAN_OPERATOR: new ApiError( - "Non-OPERATOR cannot ban OPERATOR from instance", - 25052, - ), - CANNOT_LEAVE_GUILD: new ApiError( - "You are not allowed to leave guilds that you joined by yourself", - 25059, - 403, - ), - EDITS_DISABLED: new ApiError( - "You are not allowed to edit your own messages", - 25060, - 403, - ), - DELETE_MESSAGE_DISABLED: new ApiError( - "You are not allowed to delete your own messages", - 25061, - 403, - ), - FEATURE_PERMANENTLY_DISABLED: new ApiError( - "This feature has been disabled server-side", - 45006, - 501, - ), - FEATURE_IS_IMMUTABLE: new ApiError( - "The feature ({}) cannot be edited.", - 45007, - 403, - ), - MISSING_RIGHTS: new ApiError( - "You lack rights to perform that action ({})", - 50013, - undefined, - [""], - ), - CANNOT_REPLACE_BY_BACKFILL: new ApiError( - "Cannot backfill to message ID that already exists", - 55002, - 409, - ), - CANNOT_BACKFILL_TO_THE_FUTURE: new ApiError( - "You cannot backfill messages in the future", - 55003, - ), + CANNOT_MODIFY_USER_GROUP: new ApiError("This user cannot manipulate this group", 25050, 403), + CANNOT_REMOVE_SELF_FROM_GROUP: new ApiError("This user cannot remove oneself from user group", 25051), + CANNOT_BAN_OPERATOR: new ApiError("Non-OPERATOR cannot ban OPERATOR from instance", 25052), + CANNOT_LEAVE_GUILD: new ApiError("You are not allowed to leave guilds that you joined by yourself", 25059, 403), + EDITS_DISABLED: new ApiError("You are not allowed to edit your own messages", 25060, 403), + DELETE_MESSAGE_DISABLED: new ApiError("You are not allowed to delete your own messages", 25061, 403), + FEATURE_PERMANENTLY_DISABLED: new ApiError("This feature has been disabled server-side", 45006, 501), + FEATURE_IS_IMMUTABLE: new ApiError("The feature ({}) cannot be edited.", 45007, 403), + MISSING_RIGHTS: new ApiError("You lack rights to perform that action ({})", 50013, undefined, [""]), + CANNOT_REPLACE_BY_BACKFILL: new ApiError("Cannot backfill to message ID that already exists", 55002, 409), + CANNOT_BACKFILL_TO_THE_FUTURE: new ApiError("You cannot backfill messages in the future", 55003), CANNOT_GRANT_PERMISSIONS_EXCEEDING_RIGHTS: new ApiError( "You cannot grant permissions exceeding your own rights", - 50050, - ), - ROUTES_LOOPING: new ApiError( - "Loops in the route definition ({})", - 50060, - undefined, - [""], - ), - CANNOT_REMOVE_ROUTE: new ApiError( - "Cannot remove message route while it is in effect and being used", - 50061, + 50050 ), + ROUTES_LOOPING: new ApiError("Loops in the route definition ({})", 50060, undefined, [""]), + CANNOT_REMOVE_ROUTE: new ApiError("Cannot remove message route while it is in effect and being used", 50061), }; /** diff --git a/src/util/util/Database.ts b/src/util/util/Database.ts index 3a45eea0..3b0024d0 100644 --- a/src/util/util/Database.ts +++ b/src/util/util/Database.ts @@ -33,8 +33,7 @@ if (!process.env) { config(); } -const dbConnectionString = - process.env.DATABASE || path.join(process.cwd(), "database.db"); +const dbConnectionString = process.env.DATABASE || path.join(process.cwd(), "database.db"); const DatabaseType = dbConnectionString.includes("://") ? dbConnectionString.split(":")[0]?.replace("+srv", "") @@ -71,8 +70,8 @@ export async function initDatabase(): Promise<DataSource> { if (isSqlite) { console.log( `[Database] ${red( - `You are running sqlite! Please keep in mind that we recommend setting up a dedicated database!`, - )}`, + `You are running sqlite! Please keep in mind that we recommend setting up a dedicated database!` + )}` ); } @@ -83,8 +82,8 @@ export async function initDatabase(): Promise<DataSource> { "[Database]" + red( ` We don't have migrations for DB type '${DatabaseType}'` + - ` To ignore, set DB_SYNC=true in your env. https://docs.spacebar.chat/setup/server/configuration/env/`, - ), + ` To ignore, set DB_SYNC=true in your env. https://docs.spacebar.chat/setup/server/configuration/env/` + ) ); process.exit(); } @@ -104,9 +103,7 @@ export async function initDatabase(): Promise<DataSource> { } }; if (!(await dbExists())) { - console.log( - "[Database] This appears to be a fresh database. Synchronising.", - ); + console.log("[Database] This appears to be a fresh database. Synchronising."); await dbConnection.synchronize(); // On next start, typeorm will try to run all the migrations again from beginning. @@ -116,8 +113,8 @@ export async function initDatabase(): Promise<DataSource> { Migration.insert({ name: migration.name, timestamp: Date.now(), - }), - ), + }) + ) ); } else { console.log("[Database] Applying missing migrations, if any."); diff --git a/src/util/util/Event.ts b/src/util/util/Event.ts index 01f4911a..9556c755 100644 --- a/src/util/util/Event.ts +++ b/src/util/util/Event.ts @@ -23,27 +23,17 @@ import { EVENT, Event } from "../interfaces"; export const events = new EventEmitter(); export async function emitEvent(payload: Omit<Event, "created_at">) { - const id = (payload.channel_id || - payload.user_id || - payload.guild_id) as string; + const id = (payload.channel_id || payload.user_id || payload.guild_id) as string; if (!id) return console.error("event doesn't contain any id", payload); if (RabbitMQ.connection) { - const data = - typeof payload.data === "object" - ? JSON.stringify(payload.data) - : payload.data; // use rabbitmq for event transmission + const data = typeof payload.data === "object" ? JSON.stringify(payload.data) : payload.data; // use rabbitmq for event transmission await RabbitMQ.channel?.assertExchange(id, "fanout", { durable: false, }); // assertQueue isn't needed, because a queue will automatically created if it doesn't exist - const successful = RabbitMQ.channel?.publish( - id, - "", - Buffer.from(`${data}`), - { type: payload.event }, - ); + const successful = RabbitMQ.channel?.publish(id, "", Buffer.from(`${data}`), { type: payload.event }); if (!successful) throw new Error("failed to send event"); } else if (process.env.EVENT_TRANSMISSION === "process") { process.send?.({ type: "event", event: payload, id } as ProcessEvent); @@ -79,17 +69,10 @@ export interface ProcessEvent { id: string; } -export async function listenEvent( - event: string, - callback: (event: EventOpts) => unknown, - opts?: ListenEventOpts, -) { +export async function listenEvent(event: string, callback: (event: EventOpts) => unknown, opts?: ListenEventOpts) { if (RabbitMQ.connection) { const channel = opts?.channel || RabbitMQ.channel; - if (!channel) - throw new Error( - "[Events] An event was sent without an associated channel", - ); + if (!channel) throw new Error("[Events] An event was sent without an associated channel"); return await rabbitListen(channel, event, callback, { acknowledge: opts?.acknowledge, }); @@ -100,9 +83,7 @@ export async function listenEvent( }; const listener = (msg: ProcessEvent) => { - msg.type === "event" && - msg.id === event && - callback({ ...msg.event, cancel }); + msg.type === "event" && msg.id === event && callback({ ...msg.event, cancel }); }; // TODO: assert the type is correct? @@ -127,7 +108,7 @@ async function rabbitListen( channel: Channel, id: string, callback: (event: EventOpts) => unknown, - opts?: { acknowledge?: boolean }, + opts?: { acknowledge?: boolean } ) { await channel.assertExchange(id, "fanout", { durable: false }); const q = await channel.assertQueue("", { @@ -162,7 +143,7 @@ async function rabbitListen( }, { noAck: !opts?.acknowledge, - }, + } ); return cancel; diff --git a/src/util/util/FieldError.ts b/src/util/util/FieldError.ts index 28e7e378..f6d53298 100644 --- a/src/util/util/FieldError.ts +++ b/src/util/util/FieldError.ts @@ -18,9 +18,7 @@ import "missing-native-js-functions"; -export function FieldErrors( - fields: Record<string, { code?: string; message: string }>, -) { +export function FieldErrors(fields: Record<string, { code?: string; message: string }>) { return new FieldError( 50035, "Invalid Form Body", @@ -31,7 +29,7 @@ export function FieldErrors( code: code || "BASE_TYPE_INVALID", }, ], - })), + })) ); } @@ -42,7 +40,7 @@ export class FieldError extends Error { constructor( public code: string | number, public message: string, - public errors?: object, // TODO: I don't like this typing. + public errors?: object // TODO: I don't like this typing. ) { super(message); } diff --git a/src/util/util/Gifs.ts b/src/util/util/Gifs.ts index a5a5e64c..94792877 100644 --- a/src/util/util/Gifs.ts +++ b/src/util/util/Gifs.ts @@ -18,8 +18,7 @@ export function parseGifResult(result: TenorGif) { export function getGifApiKey() { const { enabled, provider, apiKey } = Config.get().gif; if (!enabled) throw new HTTPError(`Gifs are disabled`); - if (provider !== "tenor" || !apiKey) - throw new HTTPError(`${provider} gif provider not supported`); + if (provider !== "tenor" || !apiKey) throw new HTTPError(`${provider} gif provider not supported`); return apiKey; } diff --git a/src/util/util/JSON.ts b/src/util/util/JSON.ts index c7dcf47e..767fc618 100644 --- a/src/util/util/JSON.ts +++ b/src/util/util/JSON.ts @@ -18,11 +18,7 @@ // Discord.com sends ISO strings with +00:00 extension, not Z // This causes issues with Python bot libs -const JSONReplacer = function ( - this: { [key: string]: unknown }, - key: string, - value: unknown, -) { +const JSONReplacer = function (this: { [key: string]: unknown }, key: string, value: unknown) { if (this[key] instanceof Date) { return (this[key] as Date).toISOString().replace("Z", "+00:00"); } diff --git a/src/util/util/Permissions.ts b/src/util/util/Permissions.ts index 996c72ea..a030cc97 100644 --- a/src/util/util/Permissions.ts +++ b/src/util/util/Permissions.ts @@ -2,24 +2,13 @@ // Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah // @fc-license-skip -import { - Channel, - ChannelPermissionOverwrite, - Guild, - Member, - Role, -} from "../entities"; +import { Channel, ChannelPermissionOverwrite, Guild, Member, Role } from "../entities"; import { BitField } from "./BitField"; import "missing-native-js-functions"; import { BitFieldResolvable, BitFlag } from "./BitField"; import { HTTPError } from "lambert-server"; -export type PermissionResolvable = - | bigint - | number - | Permissions - | PermissionResolvable[] - | PermissionString; +export type PermissionResolvable = bigint | number | Permissions | PermissionResolvable[] | PermissionString; type PermissionString = keyof typeof Permissions.FLAGS; @@ -86,20 +75,14 @@ export class Permissions extends BitField { }; any(permission: PermissionResolvable, checkAdmin = true) { - return ( - (checkAdmin && super.any(Permissions.FLAGS.ADMINISTRATOR)) || - super.any(permission) - ); + return (checkAdmin && super.any(Permissions.FLAGS.ADMINISTRATOR)) || super.any(permission); } /** * Checks whether the bitfield has a permission, or multiple permissions. */ has(permission: PermissionResolvable, checkAdmin = true) { - return ( - (checkAdmin && super.has(Permissions.FLAGS.ADMINISTRATOR)) || - super.has(permission) - ); + return (checkAdmin && super.has(Permissions.FLAGS.ADMINISTRATOR)) || super.has(permission); } /** @@ -107,39 +90,28 @@ export class Permissions extends BitField { */ hasThrow(permission: PermissionResolvable) { if (this.has(permission) && this.has("VIEW_CHANNEL")) return true; - throw new HTTPError( - `You are missing the following permissions ${permission}`, - 403, - ); + throw new HTTPError(`You are missing the following permissions ${permission}`, 403); } overwriteChannel(overwrites: ChannelPermissionOverwrite[]) { if (!overwrites) return this; if (!this.cache) throw new Error("permission chache not available"); overwrites = overwrites.filter((x) => { - if (x.type === 0 && this.cache.roles?.some((r) => r.id === x.id)) - return true; + if (x.type === 0 && this.cache.roles?.some((r) => r.id === x.id)) return true; if (x.type === 1 && x.id == this.cache.user_id) return true; return false; }); - return new Permissions( - Permissions.channelPermission(overwrites, this.bitfield), - ); + return new Permissions(Permissions.channelPermission(overwrites, this.bitfield)); } - static channelPermission( - overwrites: ChannelPermissionOverwrite[], - init?: bigint, - ) { + static channelPermission(overwrites: ChannelPermissionOverwrite[], init?: bigint) { // TODO: do not deny any permissions if admin return overwrites.reduce((permission, overwrite) => { // apply disallowed permission // * permission: current calculated permission (e.g. 010) // * deny contains all denied permissions (e.g. 011) // * allow contains all explicitly allowed permisions (e.g. 100) - return ( - (permission & ~BigInt(overwrite.deny)) | BigInt(overwrite.allow) - ); + return (permission & ~BigInt(overwrite.deny)) | BigInt(overwrite.allow); // ~ operator inverts deny (e.g. 011 -> 100) // & operator only allows 1 for both ~deny and permission (e.g. 010 & 100 -> 000) // | operators adds both together (e.g. 000 + 100 -> 100) @@ -148,10 +120,7 @@ export class Permissions extends BitField { static rolePermission(roles: Role[]) { // adds all permissions of all roles together (Bit OR) - return roles.reduce( - (permission, role) => permission | BigInt(role.permissions), - BigInt(0), - ); + return roles.reduce((permission, role) => permission | BigInt(role.permissions), BigInt(0)); } static finalPermission({ @@ -182,8 +151,7 @@ export class Permissions extends BitField { } if (channel?.recipient_ids) { - if (channel?.owner_id === user.id) - return new Permissions("ADMINISTRATOR"); + if (channel?.owner_id === user.id) return new Permissions("ADMINISTRATOR"); if (channel.recipient_ids.includes(user.id)) { // Default dm permissions return new Permissions([ @@ -209,10 +177,7 @@ export class Permissions extends BitField { } } -const ALL_PERMISSIONS = Object.values(Permissions.FLAGS).reduce( - (total, val) => total | val, - BigInt(0), -); +const ALL_PERMISSIONS = Object.values(Permissions.FLAGS).reduce((total, val) => total | val, BigInt(0)); export type PermissionCache = { channel?: Channel | undefined; @@ -233,7 +198,7 @@ export async function getPermission( channel_relations?: string[]; member_select?: (keyof Member)[]; member_relations?: string[]; - } = {}, + } = {} ) { if (!user_id) throw new HTTPError("User not found"); let channel: Channel | undefined; @@ -262,8 +227,7 @@ export async function getPermission( select: ["id", "owner_id", ...(opts.guild_select || [])], relations: opts.guild_relations, }); - if (guild.owner_id === user_id) - return new Permissions(Permissions.FLAGS.ADMINISTRATOR); + if (guild.owner_id === user_id) return new Permissions(Permissions.FLAGS.ADMINISTRATOR); member = await Member.findOneOrFail({ where: { guild_id, id: user_id }, diff --git a/src/util/util/Rights.ts b/src/util/util/Rights.ts index 90ebe242..e154d927 100644 --- a/src/util/util/Rights.ts +++ b/src/util/util/Rights.ts @@ -22,12 +22,7 @@ import { BitFieldResolvable, BitFlag } from "./BitField"; import { User } from "../entities"; import { HTTPError } from "lambert-server"; -export type RightResolvable = - | bigint - | number - | Rights - | RightResolvable[] - | RightString; +export type RightResolvable = bigint | number | Rights | RightResolvable[] | RightString; type RightString = keyof typeof Rights.FLAGS; // TODO: just like roles for members, users should have privilidges which combine multiple rights into one and make it easy to assign @@ -97,35 +92,23 @@ export class Rights extends BitField { }; any(permission: RightResolvable, checkOperator = true) { - return ( - (checkOperator && super.any(Rights.FLAGS.OPERATOR)) || - super.any(permission) - ); + return (checkOperator && super.any(Rights.FLAGS.OPERATOR)) || super.any(permission); } has(permission: RightResolvable, checkOperator = true) { - return ( - (checkOperator && super.has(Rights.FLAGS.OPERATOR)) || - super.has(permission) - ); + return (checkOperator && super.has(Rights.FLAGS.OPERATOR)) || super.has(permission); } hasThrow(permission: RightResolvable) { if (this.has(permission)) return true; - throw new HTTPError( - `You are missing the following rights ${permission}`, - 403, - ); + throw new HTTPError(`You are missing the following rights ${permission}`, 403); } } -const ALL_RIGHTS = Object.values(Rights.FLAGS).reduce( - (total, val) => total | val, - BigInt(0), -); +const ALL_RIGHTS = Object.values(Rights.FLAGS).reduce((total, val) => total | val, BigInt(0)); export async function getRights( - user_id: string, + user_id: string /**, opts: { in_behalf?: (keyof User)[]; } = {} **/ diff --git a/src/util/util/Sentry.ts b/src/util/util/Sentry.ts index 74a23a1e..b51100f1 100644 --- a/src/util/util/Sentry.ts +++ b/src/util/util/Sentry.ts @@ -29,8 +29,7 @@ let errorHandlersUsed = false; export const Sentry = { /** Call BEFORE registering your routes */ init: async (app?: express.Application) => { - const { enabled, endpoint, traceSampleRate, environment } = - Config.get().sentry; + const { enabled, endpoint, traceSampleRate, environment } = Config.get().sentry; if (!enabled) return; if (SentryNode.getCurrentHub().getClient()) return; // we've already initialised sentry @@ -40,8 +39,8 @@ export const Sentry = { if (traceSampleRate >= 0.8) { console.log( `[Sentry] ${yellow( - "Your sentry trace sampling rate is >= 80%. For large loads, this may degrade performance.", - )}`, + "Your sentry trace sampling rate is >= 80%. For large loads, this may degrade performance." + )}` ); } @@ -61,7 +60,7 @@ export const Sentry = { integrations.push( new SentryNode.Integrations.Express({ app, - }), + }) ); SentryNode.init({ @@ -94,14 +93,8 @@ export const Sentry = { // Filter breadcrumbs that we don't care about if (x.message?.includes("identified as")) return false; if (x.message?.includes("[WebSocket] closed")) return false; - if ( - x.message?.includes( - "Got Resume -> cancel not implemented", - ) - ) - return false; - if (x.message?.includes("[Gateway] New connection from")) - return false; + if (x.message?.includes("Got Resume -> cancel not implemented")) return false; + if (x.message?.includes("[Gateway] New connection from")) return false; return true; }); diff --git a/src/util/util/Snowflake.ts b/src/util/util/Snowflake.ts index 93898fbb..8af567c7 100644 --- a/src/util/util/Snowflake.ts +++ b/src/util/util/Snowflake.ts @@ -18,9 +18,7 @@ export class Snowflake { static workerId = BigInt((cluster.worker?.id || 0) % 31); // max 31 constructor() { - throw new Error( - `The ${this.constructor.name} class may not be instantiated.`, - ); + throw new Error(`The ${this.constructor.name} class may not be instantiated.`); } /** @@ -115,9 +113,7 @@ export class Snowflake { * @returns {DeconstructedSnowflake} Deconstructed snowflake */ static deconstruct(snowflake) { - const BINARY = Snowflake.idToBinary(snowflake) - .toString(2) - .padStart(64, "0"); + const BINARY = Snowflake.idToBinary(snowflake).toString(2).padStart(64, "0"); const res = { timestamp: parseInt(BINARY.substring(0, 42), 2) + Snowflake.EPOCH, workerID: parseInt(BINARY.substring(42, 47), 2), diff --git a/src/util/util/Token.ts b/src/util/util/Token.ts index 97bdec74..d2f5ac35 100644 --- a/src/util/util/Token.ts +++ b/src/util/util/Token.ts @@ -20,10 +20,7 @@ import jwt, { VerifyOptions } from "jsonwebtoken"; import { Config } from "./Config"; import { User } from "../entities"; // TODO: dont use deprecated APIs lol -import { - FindOptionsRelationByString, - FindOptionsSelectByString, -} from "typeorm"; +import { FindOptionsRelationByString, FindOptionsSelectByString } from "typeorm"; export const JWTOptions: VerifyOptions = { algorithms: ["HS256"] }; @@ -37,50 +34,33 @@ export const checkToken = ( opts?: { select?: FindOptionsSelectByString<User>; relations?: FindOptionsRelationByString; - }, + } ): Promise<UserTokenData> => new Promise((resolve, reject) => { token = token.replace("Bot ", ""); // there is no bot distinction in sb token = token.replace("Bearer ", ""); // allow bearer tokens - jwt.verify( - token, - Config.get().security.jwtSecret, - JWTOptions, - async (err, out) => { - const decoded = out as UserTokenData["decoded"]; - if (err || !decoded) return reject("Invalid Token"); + jwt.verify(token, Config.get().security.jwtSecret, JWTOptions, async (err, out) => { + const decoded = out as UserTokenData["decoded"]; + if (err || !decoded) return reject("Invalid Token"); - const user = await User.findOne({ - where: decoded.email - ? { email: decoded.email } - : { id: decoded.id }, - select: [ - ...(opts?.select || []), - "bot", - "disabled", - "deleted", - "rights", - "data", - ], - relations: opts?.relations, - }); + const user = await User.findOne({ + where: decoded.email ? { email: decoded.email } : { id: decoded.id }, + select: [...(opts?.select || []), "bot", "disabled", "deleted", "rights", "data"], + relations: opts?.relations, + }); - if (!user) return reject("User not found"); + if (!user) return reject("User not found"); - // we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds - if ( - decoded.iat * 1000 < - new Date(user.data.valid_tokens_since).setSeconds(0, 0) - ) - return reject("Invalid Token"); + // we need to round it to seconds as it saved as seconds in jwt iat and valid_tokens_since is stored in milliseconds + if (decoded.iat * 1000 < new Date(user.data.valid_tokens_since).setSeconds(0, 0)) + return reject("Invalid Token"); - if (user.disabled) return reject("User disabled"); - if (user.deleted) return reject("User not found"); + if (user.disabled) return reject("User disabled"); + if (user.deleted) return reject("User not found"); - return resolve({ decoded, user }); - }, - ); + return resolve({ decoded, user }); + }); }); export async function generateToken(id: string, email?: string) { @@ -97,7 +77,7 @@ export async function generateToken(id: string, email?: string) { (err, token) => { if (err) return rej(err); return res(token); - }, + } ); }); } diff --git a/src/util/util/TraverseDirectory.ts b/src/util/util/TraverseDirectory.ts index cc4442fc..41ba6e9f 100644 --- a/src/util/util/TraverseDirectory.ts +++ b/src/util/util/TraverseDirectory.ts @@ -19,14 +19,13 @@ import { Server, traverseDirectory } from "lambert-server"; //if we're using ts-node, use ts files instead of js -const extension = - Symbol.for("ts-node.register.instance") in process ? "ts" : "js"; +const extension = Symbol.for("ts-node.register.instance") in process ? "ts" : "js"; const DEFAULT_FILTER = new RegExp("^([^.].*)(?<!\\.d).(" + extension + ")$"); export function registerRoutes(server: Server, root: string) { return traverseDirectory( { dirname: root, recursive: true, filter: DEFAULT_FILTER }, - server.registerRoute.bind(server, root), + server.registerRoute.bind(server, root) ); } diff --git a/src/util/util/WebAuthn.ts b/src/util/util/WebAuthn.ts index b0027b13..ec9e1760 100644 --- a/src/util/util/WebAuthn.ts +++ b/src/util/util/WebAuthn.ts @@ -37,32 +37,20 @@ export const WebAuthn: { }, }; -export async function generateWebAuthnTicket( - challenge: string, -): Promise<string> { +export async function generateWebAuthnTicket(challenge: string): Promise<string> { return new Promise((res, rej) => { - jwt.sign( - { challenge }, - Config.get().security.jwtSecret, - JWTOptions, - (err, token) => { - if (err || !token) return rej(err || "no token"); - return res(token); - }, - ); + jwt.sign({ challenge }, Config.get().security.jwtSecret, JWTOptions, (err, token) => { + if (err || !token) return rej(err || "no token"); + return res(token); + }); }); } export async function verifyWebAuthnToken(token: string) { return new Promise((res, rej) => { - jwt.verify( - token, - Config.get().security.jwtSecret, - JWTOptions, - async (err, decoded) => { - if (err) return rej(err); - return res(decoded); - }, - ); + jwt.verify(token, Config.get().security.jwtSecret, JWTOptions, async (err, decoded) => { + if (err) return rej(err); + return res(decoded); + }); }); } diff --git a/src/util/util/cdn.ts b/src/util/util/cdn.ts index f7d310a3..ff4e8527 100644 --- a/src/util/util/cdn.ts +++ b/src/util/util/cdn.ts @@ -25,7 +25,7 @@ import { Config } from "./Config"; export async function uploadFile( path: string, // These are the only props we use, don't need to enforce the full type. - file?: Pick<Express.Multer.File, "mimetype" | "originalname" | "buffer">, + file?: Pick<Express.Multer.File, "mimetype" | "originalname" | "buffer"> ): Promise<Attachment> { if (!file?.buffer) throw new HTTPError("Missing file in body"); @@ -35,27 +35,21 @@ export async function uploadFile( filename: file.originalname, }); - const response = await fetch( - `${Config.get().cdn.endpointPrivate || "http://localhost:3001"}${path}`, - { - headers: { - signature: Config.get().security.requestSignature, - ...form.getHeaders(), - }, - method: "POST", - body: form, + const response = await fetch(`${Config.get().cdn.endpointPrivate || "http://localhost:3001"}${path}`, { + headers: { + signature: Config.get().security.requestSignature, + ...form.getHeaders(), }, - ); + method: "POST", + body: form, + }); const result = (await response.json()) as Attachment; if (response.status !== 200) throw result; return result; } -export async function handleFile( - path: string, - body?: string, -): Promise<string | undefined> { +export async function handleFile(path: string, body?: string): Promise<string | undefined> { if (!body || !body.startsWith("data:")) return undefined; try { const mimetype = body.split(":")[1].split(";")[0]; @@ -74,15 +68,12 @@ export async function handleFile( } export async function deleteFile(path: string) { - const response = await fetch( - `${Config.get().cdn.endpointPrivate || "http://localhost:3001"}${path}`, - { - headers: { - signature: Config.get().security.requestSignature, - }, - method: "DELETE", + const response = await fetch(`${Config.get().cdn.endpointPrivate || "http://localhost:3001"}${path}`, { + headers: { + signature: Config.get().security.requestSignature, }, - ); + method: "DELETE", + }); const result = await response.json(); if (response.status !== 200) throw result; diff --git a/src/util/util/email/index.ts b/src/util/util/email/index.ts index 619cc5c3..514e3f1b 100644 --- a/src/util/util/email/index.ts +++ b/src/util/util/email/index.ts @@ -27,14 +27,7 @@ import MailJet from "./transports/MailJet"; import SMTP from "./transports/SMTP"; import SendGrid from "./transports/SendGrid"; -const ASSET_FOLDER_PATH = path.join( - __dirname, - "..", - "..", - "..", - "..", - "assets", -); +const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "..", "assets"); enum MailTypes { verify = "verify", @@ -54,22 +47,11 @@ const transporters: { export const Email: { transporter: Transporter | null; init: () => Promise<void>; - generateLink: ( - type: Omit<MailTypes, "pwchange">, - id: string, - email: string, - ) => Promise<string>; - sendMail: ( - type: MailTypes, - user: User, - email: string, - ) => Promise<SentMessageInfo>; + generateLink: (type: Omit<MailTypes, "pwchange">, id: string, email: string) => Promise<string>; + sendMail: (type: MailTypes, user: User, email: string) => Promise<SentMessageInfo>; sendVerifyEmail: (user: User, email: string) => Promise<SentMessageInfo>; sendResetPassword: (user: User, email: string) => Promise<SentMessageInfo>; - sendPasswordChanged: ( - user: User, - email: string, - ) => Promise<SentMessageInfo>; + sendPasswordChanged: (user: User, email: string) => Promise<SentMessageInfo>; doReplacements: ( template: string, user: User, @@ -79,7 +61,7 @@ export const Email: { city: string; region: string; country_name: string; - }, + } ) => string; } = { transporter: null, @@ -88,8 +70,7 @@ export const Email: { if (!provider) return; const transporterFn = transporters[provider]; - if (!transporterFn) - return console.error(`[Email] Invalid provider: ${provider}`); + if (!transporterFn) return console.error(`[Email] Invalid provider: ${provider}`); console.log(`[Email] Initializing ${provider} transport...`); const transporter = await transporterFn(); if (!transporter) return; @@ -108,7 +89,7 @@ export const Email: { city: string; region: string; country_name: string; - }, + } ) { const { instanceName } = Config.get().general; @@ -141,8 +122,7 @@ export const Email: { */ generateLink: async function (type, id, email) { const token = (await generateToken(id, email)) as string; - const instanceUrl = - Config.get().general.frontPage || "http://localhost:3001"; + const instanceUrl = Config.get().general.frontPage || "http://localhost:3001"; const link = `${instanceUrl}/${type}#token=${token}`; return link; }, @@ -163,31 +143,23 @@ export const Email: { pwchange: "password_changed.html", }; - const template = await fs.readFile( - path.join( - ASSET_FOLDER_PATH, - "email_templates", - templateNames[type], - ), - { encoding: "utf-8" }, - ); + const template = await fs.readFile(path.join(ASSET_FOLDER_PATH, "email_templates", templateNames[type]), { + encoding: "utf-8", + }); // replace email template placeholders const html = this.doReplacements( template, user, // password change emails don't have links - type != MailTypes.pwchange - ? await this.generateLink(type, user.id, email) - : undefined, + type != MailTypes.pwchange ? await this.generateLink(type, user.id, email) : undefined ); // extract the title from the email template to use as the email subject const subject = html.match(/<title>(.*)<\/title>/)?.[1] || ""; const message = { - from: - Config.get().general.correspondenceEmail || "noreply@localhost", + from: Config.get().general.correspondenceEmail || "noreply@localhost", to: email, subject, html, diff --git a/src/util/util/email/transports/MailGun.ts b/src/util/util/email/transports/MailGun.ts index badc9880..f961e80b 100644 --- a/src/util/util/email/transports/MailGun.ts +++ b/src/util/util/email/transports/MailGun.ts @@ -24,10 +24,7 @@ export default async function () { const { apiKey, domain } = Config.get().email.mailgun; // ensure all required configuration values are set - if (!apiKey || !domain) - return console.error( - "[Email] Mailgun has not been configured correctly.", - ); + if (!apiKey || !domain) return console.error("[Email] Mailgun has not been configured correctly."); let mg; try { @@ -36,7 +33,7 @@ export default async function () { } catch { // if the package is not installed, log an error and return void so we don't set the transporter console.error( - "[Email] Mailgun transport is not installed. Please run `npm install nodemailer-mailgun-transport --save-optional` to install it.", + "[Email] Mailgun transport is not installed. Please run `npm install nodemailer-mailgun-transport --save-optional` to install it." ); return; } diff --git a/src/util/util/email/transports/MailJet.ts b/src/util/util/email/transports/MailJet.ts index 69be7acf..b786c1db 100644 --- a/src/util/util/email/transports/MailJet.ts +++ b/src/util/util/email/transports/MailJet.ts @@ -24,10 +24,7 @@ export default async function () { const { apiKey, apiSecret } = Config.get().email.mailjet; // ensure all required configuration values are set - if (!apiKey || !apiSecret) - return console.error( - "[Email] Mailjet has not been configured correctly.", - ); + if (!apiKey || !apiSecret) return console.error("[Email] Mailjet has not been configured correctly."); let mj; try { @@ -36,7 +33,7 @@ export default async function () { } catch { // if the package is not installed, log an error and return void so we don't set the transporter console.error( - "[Email] Mailjet transport is not installed. Please run `npm install n0script22/nodemailer-mailjet-transport --save-optional` to install it.", + "[Email] Mailjet transport is not installed. Please run `npm install n0script22/nodemailer-mailjet-transport --save-optional` to install it." ); return; } diff --git a/src/util/util/email/transports/SMTP.ts b/src/util/util/email/transports/SMTP.ts index 5b43a870..cb32f6b4 100644 --- a/src/util/util/email/transports/SMTP.ts +++ b/src/util/util/email/transports/SMTP.ts @@ -29,7 +29,7 @@ export default async function () { if (!Config.get().general.correspondenceEmail) return console.error( - "[Email] Correspondence email has not been configured! This is used as the sender email address.", + "[Email] Correspondence email has not been configured! This is used as the sender email address." ); // construct the transporter diff --git a/src/util/util/email/transports/SendGrid.ts b/src/util/util/email/transports/SendGrid.ts index a863d3ec..7f4d13fb 100644 --- a/src/util/util/email/transports/SendGrid.ts +++ b/src/util/util/email/transports/SendGrid.ts @@ -24,10 +24,7 @@ export default async function () { const { apiKey } = Config.get().email.sendgrid; // ensure all required configuration values are set - if (!apiKey) - return console.error( - "[Email] SendGrid has not been configured correctly.", - ); + if (!apiKey) return console.error("[Email] SendGrid has not been configured correctly."); let sg; try { @@ -36,7 +33,7 @@ export default async function () { } catch { // if the package is not installed, log an error and return void so we don't set the transporter console.error( - "[Email] SendGrid transport is not installed. Please run `npm install Maria-Golomb/nodemailer-sendgrid-transport --save-optional` to install it.", + "[Email] SendGrid transport is not installed. Please run `npm install Maria-Golomb/nodemailer-sendgrid-transport --save-optional` to install it." ); return; } diff --git a/src/webrtc/Server.ts b/src/webrtc/Server.ts index 0ba2e41b..2e1b98d5 100644 --- a/src/webrtc/Server.ts +++ b/src/webrtc/Server.ts @@ -29,15 +29,7 @@ export class Server { public server: http.Server; public production: boolean; - constructor({ - port, - server, - production, - }: { - port: number; - server?: http.Server; - production?: boolean; - }) { + constructor({ port, server, production }: { port: number; server?: http.Server; production?: boolean }) { this.port = port; this.production = production || false; diff --git a/src/webrtc/events/Connection.ts b/src/webrtc/events/Connection.ts index 6c5bab03..799e092e 100644 --- a/src/webrtc/events/Connection.ts +++ b/src/webrtc/events/Connection.ts @@ -28,11 +28,7 @@ import { onMessage } from "./Message"; // TODO: specify rate limit in config // TODO: check msg max size -export async function Connection( - this: WS.Server, - socket: WebSocket, - request: IncomingMessage, -) { +export async function Connection(this: WS.Server, socket: WebSocket, request: IncomingMessage) { try { socket.on("close", onClose.bind(socket)); socket.on("message", onMessage.bind(socket)); @@ -57,8 +53,7 @@ export async function Connection( socket.encoding = "json"; socket.version = Number(searchParams.get("v")) || 5; - if (socket.version < 3) - return socket.close(CLOSECODES.Unknown_error, "invalid version"); + if (socket.version < 3) return socket.close(CLOSECODES.Unknown_error, "invalid version"); setHeartbeat(socket); diff --git a/src/webrtc/events/Message.ts b/src/webrtc/events/Message.ts index 22189e95..5145d640 100644 --- a/src/webrtc/events/Message.ts +++ b/src/webrtc/events/Message.ts @@ -31,8 +31,7 @@ const PayloadSchema = { export async function onMessage(this: WebSocket, buffer: Buffer) { try { var data: Payload = JSON.parse(buffer.toString()); - if (data.op !== VoiceOPCodes.IDENTIFY && !this.user_id) - return this.close(CLOSECODES.Not_authenticated); + if (data.op !== VoiceOPCodes.IDENTIFY && !this.user_id) return this.close(CLOSECODES.Not_authenticated); // @ts-ignore const OPCodeHandler = OPCodeHandlers[data.op]; @@ -44,11 +43,7 @@ export async function onMessage(this: WebSocket, buffer: Buffer) { return; } - if ( - ![VoiceOPCodes.HEARTBEAT, VoiceOPCodes.SPEAKING].includes( - data.op as VoiceOPCodes, - ) - ) { + if (![VoiceOPCodes.HEARTBEAT, VoiceOPCodes.SPEAKING].includes(data.op as VoiceOPCodes)) { // @ts-ignore console.log("[WebRTC] Opcode " + VoiceOPCodes[data.op]); } diff --git a/src/webrtc/opcodes/Heartbeat.ts b/src/webrtc/opcodes/Heartbeat.ts index 3d8e187b..9914568d 100644 --- a/src/webrtc/opcodes/Heartbeat.ts +++ b/src/webrtc/opcodes/Heartbeat.ts @@ -16,13 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { - CLOSECODES, - Payload, - Send, - setHeartbeat, - WebSocket, -} from "@spacebar/gateway"; +import { CLOSECODES, Payload, Send, setHeartbeat, WebSocket } from "@spacebar/gateway"; import { VoiceOPCodes } from "../util"; export async function onHeartbeat(this: WebSocket, data: Payload) { diff --git a/src/webrtc/opcodes/Identify.ts b/src/webrtc/opcodes/Identify.ts index 3f65127e..9481a3d6 100644 --- a/src/webrtc/opcodes/Identify.ts +++ b/src/webrtc/opcodes/Identify.ts @@ -17,19 +17,17 @@ */ import { CLOSECODES, Payload, Send, WebSocket } from "@spacebar/gateway"; -import { - validateSchema, - VoiceIdentifySchema, - VoiceState, -} from "@spacebar/util"; +import { validateSchema, VoiceIdentifySchema, VoiceState } from "@spacebar/util"; import { endpoint, getClients, VoiceOPCodes, PublicIP } from "@spacebar/webrtc"; import SemanticSDP from "semantic-sdp"; const defaultSDP = require("./sdp.json"); export async function onIdentify(this: WebSocket, data: Payload) { clearTimeout(this.readyTimeout); - const { server_id, user_id, session_id, token, streams, video } = - validateSchema("VoiceIdentifySchema", data.d) as VoiceIdentifySchema; + const { server_id, user_id, session_id, token, streams, video } = validateSchema( + "VoiceIdentifySchema", + data.d + ) as VoiceIdentifySchema; const voiceState = await VoiceState.findOne({ where: { guild_id: server_id, user_id, token, session_id }, @@ -44,7 +42,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { setup: "actpass", hash: "sha-256", fingerprint: endpoint.getDTLSFingerprint(), - }), + }) ); this.client = { diff --git a/src/webrtc/opcodes/SelectProtocol.ts b/src/webrtc/opcodes/SelectProtocol.ts index 0a06e722..0ae5eea7 100644 --- a/src/webrtc/opcodes/SelectProtocol.ts +++ b/src/webrtc/opcodes/SelectProtocol.ts @@ -24,10 +24,7 @@ import SemanticSDP, { MediaInfo, SDPInfo } from "semantic-sdp"; export async function onSelectProtocol(this: WebSocket, payload: Payload) { if (!this.client) return; - const data = validateSchema( - "SelectProtocolSchema", - payload.d, - ) as SelectProtocolSchema; + const data = validateSchema("SelectProtocolSchema", payload.d) as SelectProtocolSchema; const offer = SemanticSDP.SDPInfo.parse("m=audio\n" + data.sdp!); this.client.sdp!.setICE(offer.getICE()); diff --git a/src/webrtc/opcodes/Video.ts b/src/webrtc/opcodes/Video.ts index 3228d4ee..88722602 100644 --- a/src/webrtc/opcodes/Video.ts +++ b/src/webrtc/opcodes/Video.ts @@ -40,7 +40,7 @@ export async function onVideo(this: WebSocket, payload: Payload) { id, // @ts-ignore tracks: [], - }), + }) ); this.client.in.stream = stream; @@ -65,7 +65,7 @@ export async function onVideo(this: WebSocket, payload: Payload) { id: "out" + this.user_id, // @ts-ignore tracks: [], - }), + }) ); this.client.out.stream = out; @@ -95,22 +95,14 @@ export async function onVideo(this: WebSocket, payload: Payload) { } } -function attachTrack( - this: WebSocket, - track: IncomingStreamTrack, - user_id: string, -) { +function attachTrack(this: WebSocket, track: IncomingStreamTrack, user_id: string) { if (!this.client) return; - const outTrack = this.client.transport!.createOutgoingStreamTrack( - track.getMedia(), - ); + const outTrack = this.client.transport!.createOutgoingStreamTrack(track.getMedia()); outTrack.attachTo(track); this.client.out.stream!.addTrack(outTrack); var ssrcs = this.client.out.tracks.get(user_id)!; if (!ssrcs) - ssrcs = this.client.out.tracks - .set(user_id, { audio_ssrc: 0, rtx_ssrc: 0, video_ssrc: 0 }) - .get(user_id)!; + ssrcs = this.client.out.tracks.set(user_id, { audio_ssrc: 0, rtx_ssrc: 0, video_ssrc: 0 }).get(user_id)!; if (track.getMedia() === "audio") { ssrcs.audio_ssrc = outTrack.getSSRCs().media!; diff --git a/src/webrtc/opcodes/sdp.json b/src/webrtc/opcodes/sdp.json index 5f7eba38..7fdcc389 100644 --- a/src/webrtc/opcodes/sdp.json +++ b/src/webrtc/opcodes/sdp.json @@ -1,420 +1,420 @@ { - "version": 0, - "streams": [], - "medias": [ - { - "id": "0", - "type": "audio", - "direction": "sendrecv", - "codecs": [ - { - "codec": "opus", - "type": 111, - "channels": 2, - "params": { - "minptime": "10", - "useinbandfec": "1" - }, - "rtcpfbs": [ - { - "id": "transport-cc" - } - ] - } - ], - "extensions": { - "1": "urn:ietf:params:rtp-hdrext:ssrc-audio-level", - "2": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", - "3": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01", - "4": "urn:ietf:params:rtp-hdrext:sdes:mid" - } - }, - { - "id": "1", - "type": "video", - "direction": "sendrecv", - "codecs": [ - { - "codec": "VP8", - "type": 96, - "rtx": 97, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "VP9", - "type": 98, - "rtx": 99, - "params": { - "profile-id": "0" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "VP9", - "type": 100, - "rtx": 101, - "params": { - "profile-id": "2" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "VP9", - "type": 102, - "rtx": 122, - "params": { - "profile-id": "1" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "H264", - "type": 127, - "rtx": 121, - "params": { - "level-asymmetry-allowed": "1", - "packetization-mode": "1", - "profile-level-id": "42001f" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "H264", - "type": 125, - "rtx": 107, - "params": { - "level-asymmetry-allowed": "1", - "packetization-mode": "0", - "profile-level-id": "42001f" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "H264", - "type": 108, - "rtx": 109, - "params": { - "level-asymmetry-allowed": "1", - "packetization-mode": "1", - "profile-level-id": "42e01f" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "H264", - "type": 124, - "rtx": 120, - "params": { - "level-asymmetry-allowed": "1", - "packetization-mode": "0", - "profile-level-id": "42e01f" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "H264", - "type": 123, - "rtx": 119, - "params": { - "level-asymmetry-allowed": "1", - "packetization-mode": "1", - "profile-level-id": "4d001f" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "H264", - "type": 35, - "rtx": 36, - "params": { - "level-asymmetry-allowed": "1", - "packetization-mode": "0", - "profile-level-id": "4d001f" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "H264", - "type": 37, - "rtx": 38, - "params": { - "level-asymmetry-allowed": "1", - "packetization-mode": "1", - "profile-level-id": "f4001f" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "H264", - "type": 39, - "rtx": 40, - "params": { - "level-asymmetry-allowed": "1", - "packetization-mode": "0", - "profile-level-id": "f4001f" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - }, - { - "codec": "H264", - "type": 114, - "rtx": 115, - "params": { - "level-asymmetry-allowed": "1", - "packetization-mode": "1", - "profile-level-id": "64001f" - }, - "rtcpfbs": [ - { - "id": "goog-remb" - }, - { - "id": "transport-cc" - }, - { - "id": "ccm", - "params": ["fir"] - }, - { - "id": "nack" - }, - { - "id": "nack", - "params": ["pli"] - } - ] - } - ], - "extensions": { - "2": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", - "3": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01", - "4": "urn:ietf:params:rtp-hdrext:sdes:mid", - "5": "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay", - "6": "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type", - "7": "http://www.webrtc.org/experiments/rtp-hdrext/video-timing", - "8": "http://www.webrtc.org/experiments/rtp-hdrext/color-space", - "10": "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id", - "11": "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id", - "13": "urn:3gpp:video-orientation", - "14": "urn:ietf:params:rtp-hdrext:toffset" - } - } - ], - "candidates": [] + "version": 0, + "streams": [], + "medias": [ + { + "id": "0", + "type": "audio", + "direction": "sendrecv", + "codecs": [ + { + "codec": "opus", + "type": 111, + "channels": 2, + "params": { + "minptime": "10", + "useinbandfec": "1" + }, + "rtcpfbs": [ + { + "id": "transport-cc" + } + ] + } + ], + "extensions": { + "1": "urn:ietf:params:rtp-hdrext:ssrc-audio-level", + "2": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", + "3": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01", + "4": "urn:ietf:params:rtp-hdrext:sdes:mid" + } + }, + { + "id": "1", + "type": "video", + "direction": "sendrecv", + "codecs": [ + { + "codec": "VP8", + "type": 96, + "rtx": 97, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "VP9", + "type": 98, + "rtx": 99, + "params": { + "profile-id": "0" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "VP9", + "type": 100, + "rtx": 101, + "params": { + "profile-id": "2" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "VP9", + "type": 102, + "rtx": 122, + "params": { + "profile-id": "1" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "H264", + "type": 127, + "rtx": 121, + "params": { + "level-asymmetry-allowed": "1", + "packetization-mode": "1", + "profile-level-id": "42001f" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "H264", + "type": 125, + "rtx": 107, + "params": { + "level-asymmetry-allowed": "1", + "packetization-mode": "0", + "profile-level-id": "42001f" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "H264", + "type": 108, + "rtx": 109, + "params": { + "level-asymmetry-allowed": "1", + "packetization-mode": "1", + "profile-level-id": "42e01f" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "H264", + "type": 124, + "rtx": 120, + "params": { + "level-asymmetry-allowed": "1", + "packetization-mode": "0", + "profile-level-id": "42e01f" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "H264", + "type": 123, + "rtx": 119, + "params": { + "level-asymmetry-allowed": "1", + "packetization-mode": "1", + "profile-level-id": "4d001f" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "H264", + "type": 35, + "rtx": 36, + "params": { + "level-asymmetry-allowed": "1", + "packetization-mode": "0", + "profile-level-id": "4d001f" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "H264", + "type": 37, + "rtx": 38, + "params": { + "level-asymmetry-allowed": "1", + "packetization-mode": "1", + "profile-level-id": "f4001f" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "H264", + "type": 39, + "rtx": 40, + "params": { + "level-asymmetry-allowed": "1", + "packetization-mode": "0", + "profile-level-id": "f4001f" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + }, + { + "codec": "H264", + "type": 114, + "rtx": 115, + "params": { + "level-asymmetry-allowed": "1", + "packetization-mode": "1", + "profile-level-id": "64001f" + }, + "rtcpfbs": [ + { + "id": "goog-remb" + }, + { + "id": "transport-cc" + }, + { + "id": "ccm", + "params": ["fir"] + }, + { + "id": "nack" + }, + { + "id": "nack", + "params": ["pli"] + } + ] + } + ], + "extensions": { + "2": "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time", + "3": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01", + "4": "urn:ietf:params:rtp-hdrext:sdes:mid", + "5": "http://www.webrtc.org/experiments/rtp-hdrext/playout-delay", + "6": "http://www.webrtc.org/experiments/rtp-hdrext/video-content-type", + "7": "http://www.webrtc.org/experiments/rtp-hdrext/video-timing", + "8": "http://www.webrtc.org/experiments/rtp-hdrext/color-space", + "10": "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id", + "11": "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id", + "13": "urn:3gpp:video-orientation", + "14": "urn:ietf:params:rtp-hdrext:toffset" + } + } + ], + "candidates": [] } diff --git a/src/webrtc/util/MediaServer.ts b/src/webrtc/util/MediaServer.ts index 0c12876c..f222db7b 100644 --- a/src/webrtc/util/MediaServer.ts +++ b/src/webrtc/util/MediaServer.ts @@ -17,11 +17,7 @@ */ import { WebSocket } from "@spacebar/gateway"; -import MediaServer, { - IncomingStream, - OutgoingStream, - Transport, -} from "medooze-media-server"; +import MediaServer, { IncomingStream, OutgoingStream, Transport } from "medooze-media-server"; import SemanticSDP from "semantic-sdp"; MediaServer.enableLog(true); @@ -35,11 +31,7 @@ try { MediaServer.setPortRange(min, max); } catch (error) { - console.error( - "Invalid env var: WEBRTC_PORT_RANGE", - process.env.WEBRTC_PORT_RANGE, - error, - ); + console.error("Invalid env var: WEBRTC_PORT_RANGE", process.env.WEBRTC_PORT_RANGE, error); process.exit(1); } |