mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-25 11:43:07 +01:00
Add ESLint (#941)
* Add eslint, switch to lint-staged for precommit * Fix all ESLint errors * Update GH workflow to check prettier and eslint
This commit is contained in:
parent
071cf6c5f2
commit
71082eb918
7
.eslintignore
Normal file
7
.eslintignore
Normal file
@ -0,0 +1,7 @@
|
||||
node_modules
|
||||
dist
|
||||
README.md
|
||||
COPYING
|
||||
src/webrtc
|
||||
scripts/
|
||||
assets
|
11
.eslintrc
Normal file
11
.eslintrc
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"root": true,
|
||||
"rules": {
|
||||
"no-mixed-spaces-and-tabs": "off",
|
||||
"@typescript-eslint/no-inferrable-types": "off", // Required by typeorm
|
||||
"@typescript-eslint/no-var-requires": "off" // Sometimes requred by typeorm to resolve circular deps
|
||||
}
|
||||
}
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -24,5 +24,7 @@ jobs:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- run: npx eslint .
|
||||
- run: npx prettier --check .
|
||||
- run: npm run build --if-present
|
||||
- run: npm run test --if-present
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx pretty-quick --staged
|
||||
npx -y lint-staged
|
3
.lintstagedrc
Normal file
3
.lintstagedrc
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"*.ts": ["eslint", "prettier --write"]
|
||||
}
|
1170
package-lock.json
generated
1170
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,7 @@
|
||||
"start:cdn": "node dist/cdn/start.js",
|
||||
"start:gateway": "node dist/gateway/start.js",
|
||||
"build": "tsc -p .",
|
||||
"lint": "eslint .",
|
||||
"setup": "npm run build && npm run generate:schema",
|
||||
"sync:db": "npm run build && node scripts/syncronise.js",
|
||||
"generate:rights": "node scripts/rights.js",
|
||||
@ -53,11 +54,14 @@
|
||||
"@types/probe-image-size": "^7.2.0",
|
||||
"@types/sharp": "^0.31.0",
|
||||
"@types/ws": "^8.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.48.2",
|
||||
"@typescript-eslint/parser": "^5.48.2",
|
||||
"eslint": "^8.32.0",
|
||||
"express": "^4.18.1",
|
||||
"husky": "^8.0.0",
|
||||
"prettier": "^2.7.1",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"typescript": "^4.8.3"
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.178.0",
|
||||
|
@ -32,7 +32,7 @@ function registerPath(file, method, prefix, path, ...args) {
|
||||
const sourceFile = file.replace("/dist/", "/src/").replace(".js", ".ts");
|
||||
const opts = args.find((x) => typeof x === "object");
|
||||
if (opts) {
|
||||
routes.set(urlPath + "|" + method, opts); // @ts-ignore
|
||||
routes.set(urlPath + "|" + method, opts);
|
||||
opts.file = sourceFile;
|
||||
// console.log(method, urlPath, opts);
|
||||
} else {
|
||||
@ -46,7 +46,6 @@ function routeOptions(opts) {
|
||||
return opts;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
RouteUtility.route = routeOptions;
|
||||
|
||||
express.Router = (opts) => {
|
||||
|
@ -22,7 +22,7 @@ import { Authentication, CORS } from "./middlewares/";
|
||||
import { Config, initDatabase, initEvent, Sentry } from "@fosscord/util";
|
||||
import { ErrorHandler } from "./middlewares/ErrorHandler";
|
||||
import { BodyParser } from "./middlewares/BodyParser";
|
||||
import { Router, Request, Response, NextFunction } from "express";
|
||||
import { Router, Request, Response } from "express";
|
||||
import path from "path";
|
||||
import { initRateLimits } from "./middlewares/RateLimit";
|
||||
import TestClient from "./middlewares/TestClient";
|
||||
@ -32,12 +32,12 @@ import { initInstance } from "./util/handlers/Instance";
|
||||
import { registerRoutes } from "@fosscord/util";
|
||||
import { red } from "picocolors";
|
||||
|
||||
export interface FosscordServerOptions extends ServerOptions {}
|
||||
export type FosscordServerOptions = ServerOptions;
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Express {
|
||||
interface Request {
|
||||
// @ts-ignore
|
||||
server: FosscordServer;
|
||||
}
|
||||
}
|
||||
@ -47,6 +47,7 @@ export class FosscordServer extends Server {
|
||||
public declare options: FosscordServerOptions;
|
||||
|
||||
constructor(opts?: Partial<FosscordServerOptions>) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
super({ ...opts, errorHandler: false, jsonBody: false });
|
||||
}
|
||||
@ -58,12 +59,12 @@ export class FosscordServer extends Server {
|
||||
await initInstance();
|
||||
await Sentry.init(this.app);
|
||||
|
||||
let logRequests = process.env["LOG_REQUESTS"] != undefined;
|
||||
const logRequests = process.env["LOG_REQUESTS"] != undefined;
|
||||
if (logRequests) {
|
||||
this.app.use(
|
||||
morgan("combined", {
|
||||
skip: (req, res) => {
|
||||
var skip = !(
|
||||
let skip = !(
|
||||
process.env["LOG_REQUESTS"]?.includes(
|
||||
res.statusCode.toString(),
|
||||
) ?? false
|
||||
@ -80,7 +81,9 @@ export class FosscordServer extends Server {
|
||||
this.app.use(BodyParser({ inflate: true, limit: "10mb" }));
|
||||
|
||||
const app = this.app;
|
||||
const api = Router(); // @ts-ignore
|
||||
const api = Router();
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
this.app = api;
|
||||
|
||||
api.use(Authentication);
|
||||
@ -95,7 +98,7 @@ export class FosscordServer extends Server {
|
||||
// 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
|
||||
// and since its not an error middleware, our error handler below still works.
|
||||
api.use("*", (req: Request, res: Response, next: NextFunction) => {
|
||||
api.use("*", (req: Request, res: Response) => {
|
||||
res.status(404).json({
|
||||
message: "404 endpoint not found",
|
||||
code: 0,
|
||||
|
@ -54,11 +54,12 @@ export const API_PREFIX = /^\/api(\/v\d+)?/;
|
||||
export const API_PREFIX_TRAILING_SLASH = /^\/api(\/v\d+)?\//;
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Express {
|
||||
interface Request {
|
||||
user_id: string;
|
||||
user_bot: boolean;
|
||||
token: string;
|
||||
token: { id: string; iat: number };
|
||||
rights: Rights;
|
||||
}
|
||||
}
|
||||
@ -87,7 +88,7 @@ export async function Authentication(
|
||||
try {
|
||||
const { jwtSecret } = Config.get().security;
|
||||
|
||||
const { decoded, user }: any = await checkToken(
|
||||
const { decoded, user } = await checkToken(
|
||||
req.headers.authorization,
|
||||
jwtSecret,
|
||||
);
|
||||
@ -97,7 +98,8 @@ export async function Authentication(
|
||||
req.user_bot = user.bot;
|
||||
req.rights = new Rights(Number(user.rights));
|
||||
return next();
|
||||
} catch (error: any) {
|
||||
return next(new HTTPError(error?.toString(), 400));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return next(new HTTPError(error!.toString(), 400));
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ type RateLimit = {
|
||||
expires_at: Date;
|
||||
};
|
||||
|
||||
let Cache = new Map<string, RateLimit>();
|
||||
const Cache = new Map<string, RateLimit>();
|
||||
const EventRateLimit = "RATELIMIT";
|
||||
|
||||
export default function rateLimit(opts: {
|
||||
@ -57,12 +57,8 @@ export default function rateLimit(opts: {
|
||||
error?: boolean;
|
||||
success?: boolean;
|
||||
onlyIp?: boolean;
|
||||
}): any {
|
||||
return async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction,
|
||||
): Promise<any> => {
|
||||
}) {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
// exempt user? if so, immediately short circuit
|
||||
if (req.user_id) {
|
||||
const rights = await getRights(req.user_id);
|
||||
@ -85,7 +81,7 @@ export default function rateLimit(opts: {
|
||||
)
|
||||
max_hits = opts.MODIFY;
|
||||
|
||||
let offender = Cache.get(executor_id + bucket_id);
|
||||
const offender = Cache.get(executor_id + bucket_id);
|
||||
|
||||
if (offender) {
|
||||
let reset = offender.expires_at.getTime();
|
||||
|
@ -19,7 +19,7 @@
|
||||
import express, { Application } from "express";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import fetch, { Response as FetchResponse, Headers } from "node-fetch";
|
||||
import fetch, { Response as FetchResponse } from "node-fetch";
|
||||
import ProxyAgent from "proxy-agent";
|
||||
import { Config } from "@fosscord/util";
|
||||
|
||||
|
@ -64,8 +64,8 @@ router.post("/", route({}), async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
router.post("/reset", route({}), async (req: Request, res: Response) => {
|
||||
let bot = await User.findOneOrFail({ where: { id: req.params.id } });
|
||||
let owner = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||
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;
|
||||
@ -80,7 +80,7 @@ router.post("/reset", route({}), async (req: Request, res: Response) => {
|
||||
|
||||
await bot.save();
|
||||
|
||||
let token = await generateToken(bot.id);
|
||||
const token = await generateToken(bot.id);
|
||||
|
||||
res.json({ token }).status(200);
|
||||
});
|
||||
|
@ -20,10 +20,8 @@ import { Request, Response, Router } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
import {
|
||||
Application,
|
||||
OrmUtils,
|
||||
DiscordApiErrors,
|
||||
ApplicationModifySchema,
|
||||
User,
|
||||
} from "@fosscord/util";
|
||||
import { verifyToken } from "node-2fa";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
|
@ -28,7 +28,7 @@ import {
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
let results = await Application.find({
|
||||
const results = await Application.find({
|
||||
where: { owner: { id: req.user_id } },
|
||||
relations: ["owner", "bot"],
|
||||
});
|
||||
|
@ -32,7 +32,7 @@ router.get(
|
||||
? parseInt(req.query.length as string)
|
||||
: 255;
|
||||
|
||||
let tokens: ValidRegistrationToken[] = [];
|
||||
const tokens: ValidRegistrationToken[] = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const token = ValidRegistrationToken.create({
|
||||
|
@ -74,7 +74,7 @@ router.post(
|
||||
"totp_secret",
|
||||
"mfa_enabled",
|
||||
],
|
||||
}).catch((e) => {
|
||||
}).catch(() => {
|
||||
throw FieldErrors({
|
||||
login: {
|
||||
message: req.t("auth:login.INVALID_LOGIN"),
|
||||
|
@ -27,8 +27,8 @@ router.post(
|
||||
"/",
|
||||
route({ body: "TotpSchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
const { code, ticket, gift_code_sku_id, login_source } =
|
||||
req.body as TotpSchema;
|
||||
// const { code, ticket, gift_code_sku_id, login_source } =
|
||||
const { code, ticket } = req.body as TotpSchema;
|
||||
|
||||
const user = await User.findOneOrFail({
|
||||
where: {
|
||||
@ -47,7 +47,7 @@ router.post(
|
||||
});
|
||||
|
||||
if (!backup) {
|
||||
const ret = verifyToken(user.totp_secret!, code);
|
||||
const ret = verifyToken(user.totp_secret || "", code);
|
||||
if (!ret || ret.delta != 0)
|
||||
throw new HTTPError(
|
||||
req.t("auth:login.INVALID_TOTP_CODE"),
|
||||
|
@ -36,7 +36,7 @@ import {
|
||||
} from "@fosscord/api";
|
||||
import bcrypt from "bcrypt";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { LessThan, MoreThan } from "typeorm";
|
||||
import { MoreThan } from "typeorm";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
@ -53,12 +53,12 @@ router.post(
|
||||
let regTokenUsed = false;
|
||||
if (req.get("Referrer") && req.get("Referrer")?.includes("token=")) {
|
||||
// eg theyre on https://staging.fosscord.com/register?token=whatever
|
||||
const token = req.get("Referrer")!.split("token=")[1].split("&")[0];
|
||||
const token = req.get("Referrer")?.split("token=")[1].split("&")[0];
|
||||
if (token) {
|
||||
const regToken = await ValidRegistrationToken.findOne({
|
||||
const regToken = await ValidRegistrationToken.findOneOrFail({
|
||||
where: { token, expires_at: MoreThan(new Date()) },
|
||||
});
|
||||
await ValidRegistrationToken.delete({ token });
|
||||
await regToken.remove();
|
||||
regTokenUsed = true;
|
||||
console.log(
|
||||
`[REGISTER] Registration token ${token} used for registration!`,
|
||||
@ -71,7 +71,7 @@ router.post(
|
||||
}
|
||||
|
||||
// email will be slightly modified version of the user supplied email -> e.g. protection against GMail Trick
|
||||
let email = adjustEmail(body.email);
|
||||
const email = adjustEmail(body.email);
|
||||
|
||||
// check if registration is allowed
|
||||
if (!regTokenUsed && !register.allowNewRegistration) {
|
||||
|
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { Router } from "express";
|
||||
const router: Router = Router();
|
||||
// TODO:
|
||||
|
||||
|
@ -92,7 +92,7 @@ router.patch(
|
||||
"/",
|
||||
route({ body: "ChannelModifySchema", permission: "MANAGE_CHANNELS" }),
|
||||
async (req: Request, res: Response) => {
|
||||
var payload = req.body as ChannelModifySchema;
|
||||
const payload = req.body as ChannelModifySchema;
|
||||
const { channel_id } = req.params;
|
||||
if (payload.icon)
|
||||
payload.icon = await handleFile(
|
||||
|
@ -86,7 +86,6 @@ router.get(
|
||||
"/",
|
||||
route({ permission: "MANAGE_CHANNELS" }),
|
||||
async (req: Request, res: Response) => {
|
||||
const { user_id } = req;
|
||||
const { channel_id } = req.params;
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
|
@ -30,7 +30,6 @@ import {
|
||||
Snowflake,
|
||||
uploadFile,
|
||||
MessageCreateSchema,
|
||||
DiscordApiErrors,
|
||||
} from "@fosscord/util";
|
||||
import { Router, Response, Request } from "express";
|
||||
import multer from "multer";
|
||||
@ -59,7 +58,7 @@ router.patch(
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { message_id, channel_id } = req.params;
|
||||
var body = req.body as MessageCreateSchema;
|
||||
let body = req.body as MessageCreateSchema;
|
||||
|
||||
const message = await Message.findOneOrFail({
|
||||
where: { id: message_id, channel_id },
|
||||
@ -85,6 +84,7 @@ router.patch(
|
||||
const new_message = await handleMessage({
|
||||
...message,
|
||||
// TODO: should message_reference be overridable?
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
message_reference: message.message_reference,
|
||||
...body,
|
||||
@ -127,7 +127,7 @@ router.put(
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id, message_id } = req.params;
|
||||
var body = req.body as MessageCreateSchema;
|
||||
const body = req.body as MessageCreateSchema;
|
||||
const attachments: Attachment[] = [];
|
||||
|
||||
const rights = await getRights(req.user_id);
|
||||
@ -171,7 +171,7 @@ router.put(
|
||||
|
||||
const embeds = body.embeds || [];
|
||||
if (body.embed) embeds.push(body.embed);
|
||||
let message = await handleMessage({
|
||||
const message = await handleMessage({
|
||||
...body,
|
||||
type: 0,
|
||||
pinned: false,
|
||||
@ -197,7 +197,10 @@ router.put(
|
||||
channel.save(),
|
||||
]);
|
||||
|
||||
postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error
|
||||
// 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),
|
||||
);
|
||||
|
||||
return res.json(message);
|
||||
},
|
||||
|
@ -165,13 +165,13 @@ router.put(
|
||||
x.emoji.name === emoji.name,
|
||||
);
|
||||
|
||||
if (!already_added) req.permission!.hasThrow("ADD_REACTIONS");
|
||||
if (!already_added) req.permission?.hasThrow("ADD_REACTIONS");
|
||||
|
||||
if (emoji.id) {
|
||||
const external_emoji = await Emoji.findOneOrFail({
|
||||
where: { id: emoji.id },
|
||||
});
|
||||
if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS");
|
||||
if (!already_added) req.permission?.hasThrow("USE_EXTERNAL_EMOJIS");
|
||||
emoji.animated = external_emoji.animated;
|
||||
emoji.name = external_emoji.name;
|
||||
}
|
||||
@ -214,7 +214,8 @@ router.delete(
|
||||
"/:emoji/:user_id",
|
||||
route({}),
|
||||
async (req: Request, res: Response) => {
|
||||
var { message_id, channel_id, user_id } = req.params;
|
||||
let { user_id } = req.params;
|
||||
const { message_id, channel_id } = req.params;
|
||||
|
||||
const emoji = getEmoji(req.params.emoji);
|
||||
|
||||
|
@ -50,7 +50,7 @@ router.post(
|
||||
const rights = await getRights(req.user_id);
|
||||
rights.hasThrow("SELF_DELETE_MESSAGES");
|
||||
|
||||
let superuser = rights.has("MANAGE_MESSAGES");
|
||||
const superuser = rights.has("MANAGE_MESSAGES");
|
||||
const permission = await getPermission(
|
||||
req.user_id,
|
||||
channel?.guild_id,
|
||||
|
@ -31,23 +31,16 @@ import {
|
||||
Snowflake,
|
||||
uploadFile,
|
||||
Member,
|
||||
Role,
|
||||
MessageCreateSchema,
|
||||
ReadState,
|
||||
DiscordApiErrors,
|
||||
getRights,
|
||||
Rights,
|
||||
Reaction,
|
||||
User,
|
||||
} from "@fosscord/util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import {
|
||||
handleMessage,
|
||||
postHandleMessage,
|
||||
route,
|
||||
getIpAdress,
|
||||
} from "@fosscord/api";
|
||||
import { handleMessage, postHandleMessage, route } from "@fosscord/api";
|
||||
import multer from "multer";
|
||||
import { yellow } from "picocolors";
|
||||
import { FindManyOptions, LessThan, MoreThan } from "typeorm";
|
||||
import { FindManyOptions, FindOperator, LessThan, MoreThan } from "typeorm";
|
||||
import { URL } from "url";
|
||||
|
||||
const router: Router = Router();
|
||||
@ -93,7 +86,7 @@ router.get("/", async (req: Request, res: Response) => {
|
||||
if (limit < 1 || limit > 100)
|
||||
throw new HTTPError("limit must be between 1 and 100", 422);
|
||||
|
||||
var halfLimit = Math.floor(limit / 2);
|
||||
const halfLimit = Math.floor(limit / 2);
|
||||
|
||||
const permissions = await getPermission(
|
||||
req.user_id,
|
||||
@ -103,7 +96,9 @@ router.get("/", async (req: Request, res: Response) => {
|
||||
permissions.hasThrow("VIEW_CHANNEL");
|
||||
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
|
||||
|
||||
var query: FindManyOptions<Message> & { where: { id?: any } } = {
|
||||
const query: FindManyOptions<Message> & {
|
||||
where: { id?: FindOperator<string> | FindOperator<string>[] };
|
||||
} = {
|
||||
order: { timestamp: "DESC" },
|
||||
take: limit,
|
||||
where: { channel_id },
|
||||
@ -140,23 +135,21 @@ router.get("/", async (req: Request, res: Response) => {
|
||||
const endpoint = Config.get().cdn.endpointPublic;
|
||||
|
||||
return res.json(
|
||||
messages.map((x: any) => {
|
||||
(x.reactions || []).forEach((x: any) => {
|
||||
messages.map((x: Partial<Message>) => {
|
||||
(x.reactions || []).forEach((y: Partial<Reaction>) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
if ((x.user_ids || []).includes(req.user_id)) x.me = true;
|
||||
// @ts-ignore
|
||||
delete x.user_ids;
|
||||
if ((y.user_ids || []).includes(req.user_id)) y.me = true;
|
||||
delete y.user_ids;
|
||||
});
|
||||
// @ts-ignore
|
||||
if (!x.author)
|
||||
x.author = {
|
||||
x.author = User.create({
|
||||
id: "4",
|
||||
discriminator: "0000",
|
||||
username: "Fosscord Ghost",
|
||||
public_flags: "0",
|
||||
avatar: null,
|
||||
};
|
||||
x.attachments?.forEach((y: any) => {
|
||||
public_flags: 0,
|
||||
});
|
||||
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
|
||||
@ -168,7 +161,7 @@ router.get("/", async (req: Request, res: Response) => {
|
||||
|
||||
/**
|
||||
Some clients ( discord.js ) only check if a property exists within the response,
|
||||
which causes erorrs when, say, the `application` property is `null`.
|
||||
which causes errors when, say, the `application` property is `null`.
|
||||
**/
|
||||
|
||||
// for (var curr in x) {
|
||||
@ -216,7 +209,7 @@ router.post(
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id } = req.params;
|
||||
var body = req.body as MessageCreateSchema;
|
||||
const body = req.body as MessageCreateSchema;
|
||||
const attachments: Attachment[] = [];
|
||||
|
||||
const channel = await Channel.findOneOrFail({
|
||||
@ -244,7 +237,7 @@ router.post(
|
||||
}
|
||||
|
||||
if (!req.rights.has(Rights.FLAGS.BYPASS_RATE_LIMITS)) {
|
||||
var limits = Config.get().limits;
|
||||
const limits = Config.get().limits;
|
||||
if (limits.absoluteRate.register.enabled) {
|
||||
const count = await Message.count({
|
||||
where: {
|
||||
@ -269,7 +262,7 @@ router.post(
|
||||
}
|
||||
|
||||
const files = (req.files as Express.Multer.File[]) ?? [];
|
||||
for (var currFile of files) {
|
||||
for (const currFile of files) {
|
||||
try {
|
||||
const file = await uploadFile(
|
||||
`/attachments/${channel.id}`,
|
||||
@ -279,13 +272,13 @@ router.post(
|
||||
Attachment.create({ ...file, proxy_url: file.url }),
|
||||
);
|
||||
} catch (error) {
|
||||
return res.status(400).json({ message: error!.toString() });
|
||||
return res.status(400).json({ message: error?.toString() });
|
||||
}
|
||||
}
|
||||
|
||||
const embeds = body.embeds || [];
|
||||
if (body.embed) embeds.push(body.embed);
|
||||
let message = await handleMessage({
|
||||
const message = await handleMessage({
|
||||
...body,
|
||||
type: 0,
|
||||
pinned: false,
|
||||
@ -304,7 +297,7 @@ router.post(
|
||||
|
||||
// Only one recipients should be closed here, since in group DMs the recipient is deleted not closed
|
||||
await Promise.all(
|
||||
channel.recipients!.map((recipient) => {
|
||||
channel.recipients?.map((recipient) => {
|
||||
if (recipient.closed) {
|
||||
recipient.closed = false;
|
||||
return Promise.all([
|
||||
@ -318,7 +311,7 @@ router.post(
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}),
|
||||
}) || [],
|
||||
);
|
||||
}
|
||||
|
||||
@ -332,6 +325,7 @@ 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)
|
||||
@ -362,7 +356,10 @@ router.post(
|
||||
channel.save(),
|
||||
]);
|
||||
|
||||
postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error
|
||||
// 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),
|
||||
);
|
||||
|
||||
return res.json(message);
|
||||
},
|
||||
|
@ -43,7 +43,7 @@ router.put(
|
||||
const { channel_id, overwrite_id } = req.params;
|
||||
const body = req.body as ChannelPermissionOverwriteSchema;
|
||||
|
||||
var channel = await Channel.findOneOrFail({
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
});
|
||||
if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
|
||||
@ -56,22 +56,24 @@ router.put(
|
||||
throw new HTTPError("user not found", 404);
|
||||
} else throw new HTTPError("type not supported", 501);
|
||||
|
||||
//@ts-ignore
|
||||
var overwrite: ChannelPermissionOverwrite =
|
||||
let overwrite: ChannelPermissionOverwrite | undefined =
|
||||
channel.permission_overwrites?.find((x) => x.id === overwrite_id);
|
||||
if (!overwrite) {
|
||||
// @ts-ignore
|
||||
overwrite = {
|
||||
id: overwrite_id,
|
||||
type: body.type,
|
||||
allow: "0",
|
||||
deny: "0",
|
||||
};
|
||||
channel.permission_overwrites!.push(overwrite);
|
||||
channel.permission_overwrites?.push(overwrite);
|
||||
}
|
||||
overwrite.allow = String(
|
||||
req.permission!.bitfield & (BigInt(body.allow) || BigInt("0")),
|
||||
(req.permission?.bitfield || 0n) &
|
||||
(BigInt(body.allow) || BigInt("0")),
|
||||
);
|
||||
overwrite.deny = String(
|
||||
req.permission!.bitfield & (BigInt(body.deny) || BigInt("0")),
|
||||
(req.permission?.bitfield || 0n) &
|
||||
(BigInt(body.deny) || BigInt("0")),
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
@ -99,7 +101,7 @@ router.delete(
|
||||
});
|
||||
if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
|
||||
|
||||
channel.permission_overwrites = channel.permission_overwrites!.filter(
|
||||
channel.permission_overwrites = channel.permission_overwrites?.filter(
|
||||
(x) => x.id === overwrite_id,
|
||||
);
|
||||
|
||||
|
@ -21,13 +21,11 @@ import {
|
||||
ChannelPinsUpdateEvent,
|
||||
Config,
|
||||
emitEvent,
|
||||
getPermission,
|
||||
Message,
|
||||
MessageUpdateEvent,
|
||||
DiscordApiErrors,
|
||||
} from "@fosscord/util";
|
||||
import { Router, Request, Response } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@fosscord/api";
|
||||
|
||||
const router: Router = Router();
|
||||
@ -43,7 +41,7 @@ router.put(
|
||||
});
|
||||
|
||||
// * in dm channels anyone can pin messages -> only check for guilds
|
||||
if (message.guild_id) req.permission!.hasThrow("MANAGE_MESSAGES");
|
||||
if (message.guild_id) req.permission?.hasThrow("MANAGE_MESSAGES");
|
||||
|
||||
const pinned_count = await Message.count({
|
||||
where: { channel: { id: channel_id }, pinned: true },
|
||||
@ -83,7 +81,7 @@ router.delete(
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
});
|
||||
if (channel.guild_id) req.permission!.hasThrow("MANAGE_MESSAGES");
|
||||
if (channel.guild_id) req.permission?.hasThrow("MANAGE_MESSAGES");
|
||||
|
||||
const message = await Message.findOneOrFail({
|
||||
where: { id: message_id },
|
||||
@ -120,7 +118,7 @@ router.get(
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id } = req.params;
|
||||
|
||||
let pins = await Message.find({
|
||||
const pins = await Message.find({
|
||||
where: { channel_id: channel_id, pinned: true },
|
||||
});
|
||||
|
||||
|
@ -19,10 +19,9 @@
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@fosscord/api";
|
||||
import { isTextChannel } from "./messages";
|
||||
import { FindManyOptions, Between, Not } from "typeorm";
|
||||
import { FindManyOptions, Between, Not, FindOperator } from "typeorm";
|
||||
import {
|
||||
Channel,
|
||||
Config,
|
||||
emitEvent,
|
||||
getPermission,
|
||||
getRights,
|
||||
@ -69,7 +68,9 @@ router.post(
|
||||
|
||||
// TODO: send the deletion event bite-by-bite to prevent client stress
|
||||
|
||||
var query: FindManyOptions<Message> & { where: { id?: any } } = {
|
||||
const query: FindManyOptions<Message> & {
|
||||
where: { id?: FindOperator<string> };
|
||||
} = {
|
||||
order: { id: "ASC" },
|
||||
// take: limit,
|
||||
where: {
|
||||
@ -93,7 +94,6 @@ router.post(
|
||||
};
|
||||
|
||||
const messages = await Message.find(query);
|
||||
const endpoint = Config.get().cdn.endpointPublic;
|
||||
|
||||
if (messages.length == 0) {
|
||||
res.sendStatus(304);
|
||||
|
@ -41,7 +41,7 @@ router.put("/:user_id", route({}), async (req: Request, res: Response) => {
|
||||
|
||||
if (channel.type !== ChannelType.GROUP_DM) {
|
||||
const recipients = [
|
||||
...channel.recipients!.map((r) => r.user_id),
|
||||
...(channel.recipients?.map((r) => r.user_id) || []),
|
||||
user_id,
|
||||
].unique();
|
||||
|
||||
@ -51,11 +51,11 @@ router.put("/:user_id", route({}), async (req: Request, res: Response) => {
|
||||
);
|
||||
return res.status(201).json(new_channel);
|
||||
} else {
|
||||
if (channel.recipients!.map((r) => r.user_id).includes(user_id)) {
|
||||
if (channel.recipients?.map((r) => r.user_id).includes(user_id)) {
|
||||
throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
|
||||
}
|
||||
|
||||
channel.recipients!.push(
|
||||
channel.recipients?.push(
|
||||
Recipient.create({ channel_id: channel_id, user_id: user_id }),
|
||||
);
|
||||
await channel.save();
|
||||
@ -95,7 +95,7 @@ router.delete("/:user_id", route({}), async (req: Request, res: Response) => {
|
||||
)
|
||||
throw DiscordApiErrors.MISSING_PERMISSIONS;
|
||||
|
||||
if (!channel.recipients!.map((r) => r.user_id).includes(user_id)) {
|
||||
if (!channel.recipients?.map((r) => r.user_id).includes(user_id)) {
|
||||
throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
|
||||
}
|
||||
|
||||
|
@ -55,10 +55,10 @@ router.post(
|
||||
|
||||
const webhook_count = await Webhook.count({ where: { channel_id } });
|
||||
const { maxWebhooks } = Config.get().limits.channel;
|
||||
if (webhook_count > maxWebhooks)
|
||||
if (maxWebhooks && webhook_count > maxWebhooks)
|
||||
throw DiscordApiErrors.MAXIMUM_WEBHOOKS.withParams(maxWebhooks);
|
||||
|
||||
var { avatar, name } = req.body as WebhookCreateSchema;
|
||||
let { avatar, name } = req.body as WebhookCreateSchema;
|
||||
name = trimSpecial(name);
|
||||
|
||||
// TODO: move this
|
||||
|
@ -26,8 +26,8 @@ const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { offset, limit, categories } = req.query;
|
||||
var showAllGuilds = Config.get().guild.discovery.showAllGuilds;
|
||||
var configLimit = Config.get().guild.discovery.limit;
|
||||
const showAllGuilds = Config.get().guild.discovery.showAllGuilds;
|
||||
const configLimit = Config.get().guild.discovery.limit;
|
||||
let guilds;
|
||||
if (categories == undefined) {
|
||||
guilds = showAllGuilds
|
||||
|
@ -26,7 +26,8 @@ router.get("/categories", route({}), async (req: Request, res: Response) => {
|
||||
// TODO:
|
||||
// Get locale instead
|
||||
|
||||
const { locale, primary_only } = req.query;
|
||||
// const { locale, primary_only } = req.query;
|
||||
const { primary_only } = req.query;
|
||||
|
||||
const out = primary_only
|
||||
? await Categories.find()
|
||||
|
@ -22,11 +22,6 @@ import { FieldErrors, Release } from "@fosscord/util";
|
||||
|
||||
const router = Router();
|
||||
|
||||
/*
|
||||
TODO: Putting the download route in /routes/download.ts doesn't register the route, for some reason
|
||||
But putting it here *does*
|
||||
*/
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { platform } = req.query;
|
||||
|
@ -41,7 +41,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
},
|
||||
);
|
||||
|
||||
const { results } = (await response.json()) as any; // TODO: types
|
||||
const { results } = await response.json();
|
||||
|
||||
res.json(results.map(parseGifResult)).status(200);
|
||||
});
|
||||
|
@ -41,7 +41,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
},
|
||||
);
|
||||
|
||||
const { results } = (await response.json()) as any; // TODO: types
|
||||
const { results } = await response.json();
|
||||
|
||||
res.json(results.map(parseGifResult)).status(200);
|
||||
});
|
||||
|
@ -25,7 +25,57 @@ import { HTTPError } from "lambert-server";
|
||||
|
||||
const router = Router();
|
||||
|
||||
export function parseGifResult(result: any) {
|
||||
// TODO: Move somewhere else
|
||||
enum TENOR_GIF_TYPES {
|
||||
gif,
|
||||
mediumgif,
|
||||
tinygif,
|
||||
nanogif,
|
||||
mp4,
|
||||
loopedmp4,
|
||||
tinymp4,
|
||||
nanomp4,
|
||||
webm,
|
||||
tinywebm,
|
||||
nanowebm,
|
||||
}
|
||||
|
||||
type TENOR_MEDIA = {
|
||||
preview: string;
|
||||
url: string;
|
||||
dims: number[];
|
||||
size: number;
|
||||
};
|
||||
|
||||
type TENOR_GIF = {
|
||||
created: number;
|
||||
hasaudio: boolean;
|
||||
id: string;
|
||||
media: { [type in keyof typeof TENOR_GIF_TYPES]: TENOR_MEDIA }[];
|
||||
tags: string[];
|
||||
title: string;
|
||||
itemurl: string;
|
||||
hascaption: boolean;
|
||||
url: string;
|
||||
};
|
||||
|
||||
type TENOR_CATEGORY = {
|
||||
searchterm: string;
|
||||
path: string;
|
||||
image: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
type TENOR_CATEGORIES_RESULTS = {
|
||||
tags: TENOR_CATEGORY[];
|
||||
};
|
||||
|
||||
type TENOR_TRENDING_RESULTS = {
|
||||
next: string;
|
||||
results: TENOR_GIF[];
|
||||
};
|
||||
|
||||
export function parseGifResult(result: TENOR_GIF) {
|
||||
return {
|
||||
id: result.id,
|
||||
title: result.title,
|
||||
@ -50,7 +100,8 @@ export function getGifApiKey() {
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
// TODO: Custom providers
|
||||
// TODO: return gifs as mp4
|
||||
const { media_format, locale } = req.query;
|
||||
// const { media_format, locale } = req.query;
|
||||
const { locale } = req.query;
|
||||
|
||||
const apiKey = getGifApiKey();
|
||||
|
||||
@ -75,11 +126,11 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
),
|
||||
]);
|
||||
|
||||
const { tags } = (await responseSource.json()) as any; // TODO: types
|
||||
const { results } = (await trendGifSource.json()) as any; //TODO: types;
|
||||
const { tags } = (await responseSource.json()) as TENOR_CATEGORIES_RESULTS;
|
||||
const { results } = (await trendGifSource.json()) as TENOR_TRENDING_RESULTS;
|
||||
|
||||
res.json({
|
||||
categories: tags.map((x: any) => ({
|
||||
categories: tags.map((x) => ({
|
||||
name: x.searchterm,
|
||||
src: x.image,
|
||||
})),
|
||||
|
@ -25,10 +25,11 @@ import { Like } from "typeorm";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { limit, personalization_disabled } = req.query;
|
||||
var showAllGuilds = Config.get().guild.discovery.showAllGuilds;
|
||||
// const { limit, personalization_disabled } = req.query;
|
||||
const { limit } = req.query;
|
||||
const showAllGuilds = Config.get().guild.discovery.showAllGuilds;
|
||||
|
||||
const genLoadId = (size: Number) =>
|
||||
const genLoadId = (size: number) =>
|
||||
[...Array(size)]
|
||||
.map(() => Math.floor(Math.random() * 16).toString(16))
|
||||
.join("");
|
||||
|
@ -41,8 +41,8 @@ router.get(
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
let bans = await Ban.find({ where: { guild_id: guild_id } });
|
||||
let promisesToAwait: object[] = [];
|
||||
const bans = await Ban.find({ where: { guild_id: guild_id } });
|
||||
const promisesToAwait: object[] = [];
|
||||
const bansObj: object[] = [];
|
||||
|
||||
bans.filter((ban) => ban.user_id !== ban.executor_id); // pretend self-bans don't exist to prevent victim chasing
|
||||
@ -104,14 +104,14 @@ router.put(
|
||||
|
||||
if (
|
||||
req.user_id === banned_user_id &&
|
||||
banned_user_id === req.permission!.cache.guild?.owner_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)
|
||||
if (req.permission?.cache.guild?.owner_id === banned_user_id)
|
||||
throw new HTTPError("You can't ban the owner", 400);
|
||||
|
||||
const banned_user = await User.getPublicUser(banned_user_id);
|
||||
@ -149,7 +149,7 @@ router.put(
|
||||
|
||||
const banned_user = await User.getPublicUser(req.params.user_id);
|
||||
|
||||
if (req.permission!.cache.guild?.owner_id === req.params.user_id)
|
||||
if (req.permission?.cache.guild?.owner_id === req.params.user_id)
|
||||
throw new HTTPError(
|
||||
"You are the guild owner, hence can't ban yourself",
|
||||
403,
|
||||
@ -186,7 +186,7 @@ router.delete(
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, user_id } = req.params;
|
||||
|
||||
let ban = await Ban.findOneOrFail({
|
||||
const ban = await Ban.findOneOrFail({
|
||||
where: { guild_id: guild_id, user_id: user_id },
|
||||
});
|
||||
|
||||
|
@ -68,7 +68,7 @@ router.patch(
|
||||
400,
|
||||
);
|
||||
|
||||
const opts: any = {};
|
||||
const opts: Partial<Channel> = {};
|
||||
if (x.position != null) opts.position = x.position;
|
||||
|
||||
if (x.parent_id) {
|
||||
|
@ -16,17 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
Channel,
|
||||
emitEvent,
|
||||
GuildDeleteEvent,
|
||||
Guild,
|
||||
Member,
|
||||
Message,
|
||||
Role,
|
||||
Invite,
|
||||
Emoji,
|
||||
} from "@fosscord/util";
|
||||
import { emitEvent, GuildDeleteEvent, Guild } from "@fosscord/util";
|
||||
import { Router, Request, Response } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@fosscord/api";
|
||||
@ -36,7 +26,7 @@ const router = Router();
|
||||
// discord prefixes this route with /delete instead of using the delete method
|
||||
// docs are wrong https://discord.com/developers/docs/resources/guild#delete-guild
|
||||
router.post("/", route({}), async (req: Request, res: Response) => {
|
||||
var { guild_id } = req.params;
|
||||
const { guild_id } = req.params;
|
||||
|
||||
const guild = await Guild.findOneOrFail({
|
||||
where: { id: guild_id },
|
||||
|
@ -16,8 +16,6 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Guild, Config } from "@fosscord/util";
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
|
||||
|
@ -47,10 +47,10 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
401,
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
guild.joined_at = member?.joined_at;
|
||||
|
||||
return res.send(guild);
|
||||
return res.send({
|
||||
...guild,
|
||||
joined_at: member?.joined_at,
|
||||
});
|
||||
});
|
||||
|
||||
router.patch(
|
||||
@ -68,7 +68,7 @@ router.patch(
|
||||
"MANAGE_GUILDS",
|
||||
);
|
||||
|
||||
var guild = await Guild.findOneOrFail({
|
||||
const guild = await Guild.findOneOrFail({
|
||||
where: { id: guild_id },
|
||||
relations: ["emojis", "roles", "stickers"],
|
||||
});
|
||||
@ -110,7 +110,7 @@ router.patch(
|
||||
"DISCOVERABLE",
|
||||
];
|
||||
|
||||
for (var feature of diff) {
|
||||
for (const feature of diff) {
|
||||
if (MUTABLE_FEATURES.includes(feature)) continue;
|
||||
|
||||
throw FosscordApiErrors.FEATURE_IS_IMMUTABLE.withParams(
|
||||
|
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { getPermission, Invite, PublicInviteRelation } from "@fosscord/util";
|
||||
import { Invite, PublicInviteRelation } from "@fosscord/util";
|
||||
import { route } from "@fosscord/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
|
@ -49,11 +49,12 @@ router.patch(
|
||||
"/",
|
||||
route({ body: "MemberChangeSchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
let { guild_id, member_id } = req.params;
|
||||
if (member_id === "@me") member_id = req.user_id;
|
||||
const { guild_id } = req.params;
|
||||
const member_id =
|
||||
req.params.member_id === "@me" ? req.user_id : req.params.member_id;
|
||||
const body = req.body as MemberChangeSchema;
|
||||
|
||||
let member = await Member.findOneOrFail({
|
||||
const member = await Member.findOneOrFail({
|
||||
where: { id: member_id, guild_id },
|
||||
relations: ["roles", "user"],
|
||||
});
|
||||
@ -101,7 +102,8 @@ router.put("/", route({}), async (req: Request, res: Response) => {
|
||||
|
||||
const rights = await getRights(req.user_id);
|
||||
|
||||
let { guild_id, member_id } = req.params;
|
||||
const { guild_id } = req.params;
|
||||
let { member_id } = req.params;
|
||||
if (member_id === "@me") {
|
||||
member_id = req.user_id;
|
||||
rights.hasThrow("JOIN_GUILDS");
|
||||
@ -109,19 +111,19 @@ router.put("/", route({}), async (req: Request, res: Response) => {
|
||||
// TODO: join others by controller
|
||||
}
|
||||
|
||||
var guild = await Guild.findOneOrFail({
|
||||
const guild = await Guild.findOneOrFail({
|
||||
where: { id: guild_id },
|
||||
});
|
||||
|
||||
var emoji = await Emoji.find({
|
||||
const emoji = await Emoji.find({
|
||||
where: { guild_id: guild_id },
|
||||
});
|
||||
|
||||
var roles = await Role.find({
|
||||
const roles = await Role.find({
|
||||
where: { guild_id: guild_id },
|
||||
});
|
||||
|
||||
var stickers = await Sticker.find({
|
||||
const stickers = await Sticker.find({
|
||||
where: { guild_id: guild_id },
|
||||
});
|
||||
|
||||
|
@ -26,12 +26,12 @@ router.patch(
|
||||
"/",
|
||||
route({ body: "MemberNickChangeSchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
var { guild_id, member_id } = req.params;
|
||||
var permissionString: PermissionResolvable = "MANAGE_NICKNAMES";
|
||||
if (member_id === "@me") {
|
||||
member_id = req.user_id;
|
||||
permissionString = "CHANGE_NICKNAME";
|
||||
}
|
||||
const { guild_id } = req.params;
|
||||
let permissionString: PermissionResolvable = "MANAGE_NICKNAMES";
|
||||
const member_id =
|
||||
req.params.member_id === "@me"
|
||||
? ((permissionString = "CHANGE_NICKNAME"), req.user_id)
|
||||
: req.params.member_id;
|
||||
|
||||
const perms = await getPermission(req.user_id, guild_id);
|
||||
perms.hasThrow(permissionString);
|
||||
|
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { getPermission, Member } from "@fosscord/util";
|
||||
import { Member } from "@fosscord/util";
|
||||
import { route } from "@fosscord/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Guild, Member, PublicMemberProjection } from "@fosscord/util";
|
||||
import { Member, PublicMemberProjection } from "@fosscord/util";
|
||||
import { route } from "@fosscord/api";
|
||||
import { MoreThan } from "typeorm";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
@ -16,6 +16,8 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
import { getPermission, FieldErrors, Message, Channel } from "@fosscord/util";
|
||||
@ -28,10 +30,10 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const {
|
||||
channel_id,
|
||||
content,
|
||||
include_nsfw, // TODO
|
||||
// include_nsfw, // TODO
|
||||
offset,
|
||||
sort_order,
|
||||
sort_by, // TODO: Handle 'relevance'
|
||||
// sort_by, // TODO: Handle 'relevance'
|
||||
limit,
|
||||
author_id,
|
||||
} = req.query;
|
||||
@ -62,7 +64,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
if (!permissions.has("READ_MESSAGE_HISTORY"))
|
||||
return res.json({ messages: [], total_results: 0 });
|
||||
|
||||
var query: FindManyOptions<Message> = {
|
||||
const query: FindManyOptions<Message> = {
|
||||
order: {
|
||||
timestamp: sort_order
|
||||
? (sort_order.toUpperCase() as "ASC" | "DESC")
|
||||
@ -87,7 +89,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
skip: offset ? Number(offset) : 0,
|
||||
};
|
||||
//@ts-ignore
|
||||
if (channel_id) query.where!.channel = { id: channel_id };
|
||||
if (channel_id) query.where.channel = { id: channel_id };
|
||||
else {
|
||||
// get all channel IDs that this user can access
|
||||
const channels = await Channel.find({
|
||||
@ -96,7 +98,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
});
|
||||
const ids = [];
|
||||
|
||||
for (var channel of channels) {
|
||||
for (const channel of channels) {
|
||||
const perm = await getPermission(
|
||||
req.user_id,
|
||||
req.params.guild_id,
|
||||
@ -108,12 +110,12 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
query.where!.channel = { id: In(ids) };
|
||||
query.where.channel = { id: In(ids) };
|
||||
}
|
||||
//@ts-ignore
|
||||
if (author_id) query.where!.author = { id: author_id };
|
||||
if (author_id) query.where.author = { id: author_id };
|
||||
//@ts-ignore
|
||||
if (content) query.where!.content = Like(`%${content}%`);
|
||||
if (content) query.where.content = Like(`%${content}%`);
|
||||
|
||||
const messages: Message[] = await Message.find(query);
|
||||
|
||||
|
@ -33,8 +33,9 @@ router.patch(
|
||||
"/:member_id",
|
||||
route({ body: "MemberChangeProfileSchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
let { guild_id, member_id } = req.params;
|
||||
if (member_id === "@me") member_id = req.user_id;
|
||||
const { guild_id } = req.params;
|
||||
// const member_id =
|
||||
// req.params.member_id === "@me" ? req.user_id : req.params.member_id;
|
||||
const body = req.body as MemberChangeProfileSchema;
|
||||
|
||||
let member = await Member.findOneOrFail({
|
||||
|
@ -29,16 +29,16 @@ export const inactiveMembers = async (
|
||||
days: number,
|
||||
roles: string[] = [],
|
||||
) => {
|
||||
var date = new Date();
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - days);
|
||||
//Snowflake should have `generateFromTime` method? Or similar?
|
||||
var minId = BigInt(date.valueOf() - Snowflake.EPOCH) << BigInt(22);
|
||||
const minId = BigInt(date.valueOf() - Snowflake.EPOCH) << BigInt(22);
|
||||
|
||||
/**
|
||||
idea: ability to customise the cutoff variable
|
||||
possible candidates: public read receipt, last presence, last VC leave
|
||||
**/
|
||||
var members = await Member.find({
|
||||
let members = await Member.find({
|
||||
where: [
|
||||
{
|
||||
guild_id,
|
||||
@ -83,7 +83,7 @@ export const inactiveMembers = async (
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const days = parseInt(req.query.days as string);
|
||||
|
||||
var roles = req.query.include_roles;
|
||||
let roles = req.query.include_roles;
|
||||
if (typeof roles === "string") roles = [roles]; //express will return array otherwise
|
||||
|
||||
const members = await inactiveMembers(
|
||||
@ -102,7 +102,7 @@ router.post(
|
||||
async (req: Request, res: Response) => {
|
||||
const days = parseInt(req.body.days);
|
||||
|
||||
var roles = req.query.include_roles;
|
||||
let roles = req.query.include_roles;
|
||||
if (typeof roles === "string") roles = [roles];
|
||||
|
||||
const { guild_id } = req.params;
|
||||
|
@ -16,10 +16,9 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Config, Guild, Member } from "@fosscord/util";
|
||||
import { Guild } from "@fosscord/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { getVoiceRegions, route } from "@fosscord/api";
|
||||
import { getIpAdress } from "@fosscord/api";
|
||||
import { getVoiceRegions, route, getIpAdress } from "@fosscord/api";
|
||||
|
||||
const router = Router();
|
||||
|
||||
|
@ -87,7 +87,8 @@ router.patch(
|
||||
role.assign({
|
||||
...body,
|
||||
permissions: String(
|
||||
req.permission!.bitfield & BigInt(body.permissions || "0"),
|
||||
(req.permission?.bitfield || 0n) &
|
||||
BigInt(body.permissions || "0"),
|
||||
),
|
||||
});
|
||||
|
||||
|
@ -68,7 +68,8 @@ router.post(
|
||||
guild_id: guild_id,
|
||||
managed: false,
|
||||
permissions: String(
|
||||
req.permission!.bitfield & BigInt(body.permissions || "0"),
|
||||
(req.permission?.bitfield || 0n) &
|
||||
BigInt(body.permissions || "0"),
|
||||
),
|
||||
tags: undefined,
|
||||
icon: undefined,
|
||||
|
@ -44,7 +44,7 @@ const TemplateGuildProjection: (keyof Guild)[] = [
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
var templates = await Template.find({
|
||||
const templates = await Template.find({
|
||||
where: { source_guild_id: guild_id },
|
||||
});
|
||||
|
||||
@ -60,9 +60,9 @@ router.post(
|
||||
where: { id: guild_id },
|
||||
select: TemplateGuildProjection,
|
||||
});
|
||||
const exists = await Template.findOneOrFail({
|
||||
const exists = await Template.findOne({
|
||||
where: { id: guild_id },
|
||||
}).catch((e) => {});
|
||||
});
|
||||
if (exists) throw new HTTPError("Template already exists", 400);
|
||||
|
||||
const template = await Template.create({
|
||||
|
@ -37,8 +37,9 @@ router.patch(
|
||||
route({ body: "VoiceStateUpdateSchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as VoiceStateUpdateSchema;
|
||||
var { guild_id, user_id } = req.params;
|
||||
if (user_id === "@me") user_id = req.user_id;
|
||||
const { guild_id } = req.params;
|
||||
const user_id =
|
||||
req.params.user_id === "@me" ? req.user_id : req.params.user_id;
|
||||
|
||||
const perms = await getPermission(
|
||||
req.user_id,
|
||||
|
@ -17,14 +17,7 @@
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import {
|
||||
Config,
|
||||
Permissions,
|
||||
Guild,
|
||||
Invite,
|
||||
Channel,
|
||||
Member,
|
||||
} from "@fosscord/util";
|
||||
import { Permissions, Guild, Invite, Channel, Member } from "@fosscord/util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { random, route } from "@fosscord/api";
|
||||
|
||||
@ -46,7 +39,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
if (!guild.widget_enabled) throw new HTTPError("Widget Disabled", 404);
|
||||
|
||||
// Fetch existing widget invite for widget channel
|
||||
var invite = await Invite.findOne({
|
||||
let invite = await Invite.findOne({
|
||||
where: { channel_id: guild.widget_channel_id },
|
||||
});
|
||||
|
||||
@ -70,7 +63,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
// Fetch voice channels, and the @everyone permissions object
|
||||
const channels = [] as any[];
|
||||
const channels: { id: string; name: string; position: number }[] = [];
|
||||
|
||||
(
|
||||
await Channel.find({
|
||||
@ -88,15 +81,15 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
) {
|
||||
channels.push({
|
||||
id: doc.id,
|
||||
name: doc.name,
|
||||
position: doc.position,
|
||||
name: doc.name ?? "Unknown channel",
|
||||
position: doc.position ?? 0,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch members
|
||||
// TODO: Understand how Discord's max 100 random member sample works, and apply to here (see top of this file)
|
||||
let members = await Member.find({ where: { guild_id: guild_id } });
|
||||
const members = await Member.find({ where: { guild_id: guild_id } });
|
||||
|
||||
// Construct object to respond with
|
||||
const data = {
|
||||
|
@ -16,6 +16,8 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Guild } from "@fosscord/util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
@ -161,8 +163,7 @@ async function drawIcon(
|
||||
scale: number,
|
||||
icon: string,
|
||||
) {
|
||||
// @ts-ignore
|
||||
const img = new require("canvas").Image();
|
||||
const img = new (require("canvas").Image)();
|
||||
img.src = icon;
|
||||
|
||||
// Do some canvas clipping magic!
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import {
|
||||
Role,
|
||||
Guild,
|
||||
Config,
|
||||
getRights,
|
||||
@ -52,6 +51,7 @@ router.post(
|
||||
|
||||
const { autoJoin } = Config.get().guild;
|
||||
if (autoJoin.enabled && !autoJoin.guilds?.length) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } });
|
||||
}
|
||||
|
@ -86,8 +86,8 @@ router.post(
|
||||
const {
|
||||
enabled,
|
||||
allowTemplateCreation,
|
||||
allowDiscordTemplates,
|
||||
allowRaws,
|
||||
// allowDiscordTemplates,
|
||||
// allowRaws,
|
||||
} = Config.get().templates;
|
||||
if (!enabled)
|
||||
return res
|
||||
@ -121,7 +121,7 @@ router.post(
|
||||
|
||||
const guild_id = Snowflake.generate();
|
||||
|
||||
const [guild, role] = await Promise.all([
|
||||
const [guild] = await Promise.all([
|
||||
Guild.create({
|
||||
...body,
|
||||
...template.serialized_source_guild,
|
||||
|
@ -27,16 +27,14 @@ import {
|
||||
Member,
|
||||
Permissions,
|
||||
User,
|
||||
getRights,
|
||||
Rights,
|
||||
MemberPrivateProjection,
|
||||
} from "@fosscord/util";
|
||||
const router = Router();
|
||||
|
||||
// TODO: scopes, other oauth types
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { client_id, scope, response_type, redirect_url } = req.query;
|
||||
// const { client_id, scope, response_type, redirect_url } = req.query;
|
||||
const { client_id } = req.query;
|
||||
|
||||
const app = await Application.findOne({
|
||||
where: {
|
||||
@ -68,6 +66,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
},
|
||||
},
|
||||
relations: ["guild", "roles"],
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
// prettier-ignore
|
||||
select: ["guild.id", "guild.name", "guild.icon", "guild.mfa_level", "guild.owner_id", "roles.id"],
|
||||
@ -139,7 +138,8 @@ router.post(
|
||||
route({ body: "ApplicationAuthorizeSchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as ApplicationAuthorizeSchema;
|
||||
const { client_id, scope, response_type, redirect_url } = req.query;
|
||||
// const { client_id, scope, response_type, redirect_url } = req.query;
|
||||
const { client_id } = req.query;
|
||||
|
||||
// TODO: captcha verification
|
||||
// TODO: MFA verification
|
||||
@ -153,7 +153,7 @@ router.post(
|
||||
// getPermission cache won't exist if we're owner
|
||||
if (
|
||||
Object.keys(perms.cache || {}).length > 0 &&
|
||||
perms.cache.member!.user.bot
|
||||
perms.cache.member?.user.bot
|
||||
)
|
||||
throw DiscordApiErrors.UNAUTHORIZED;
|
||||
perms.hasThrow("MANAGE_GUILD");
|
||||
|
@ -16,8 +16,6 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Guild, Config } from "@fosscord/util";
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
import { Config } from "@fosscord/util";
|
||||
import { config } from "dotenv";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
|
@ -23,7 +23,7 @@ const router: Router = Router();
|
||||
|
||||
router.get("/:id", route({}), async (req: Request, res: Response) => {
|
||||
//TODO
|
||||
const id = req.params.id;
|
||||
// const id = req.params.id;
|
||||
res.json({
|
||||
id: "",
|
||||
summary: "",
|
||||
|
@ -23,7 +23,7 @@ const router: Router = Router();
|
||||
|
||||
router.get("/:id", route({}), async (req: Request, res: Response) => {
|
||||
//TODO
|
||||
const id = req.params.id;
|
||||
// const id = req.params.id;
|
||||
res.json({
|
||||
id: "",
|
||||
summary: "",
|
||||
|
@ -18,12 +18,11 @@
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
import { Config, FieldErrors, Release } from "@fosscord/util";
|
||||
import { FieldErrors, Release } from "@fosscord/util";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { client } = Config.get();
|
||||
const platform = req.query.platform;
|
||||
|
||||
if (!platform)
|
||||
|
@ -23,7 +23,6 @@ import {
|
||||
PrivateUserProjection,
|
||||
User,
|
||||
UserDeleteEvent,
|
||||
UserDeleteSchema,
|
||||
} from "@fosscord/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
@ -33,7 +32,7 @@ router.post(
|
||||
"/",
|
||||
route({ right: "MANAGE_USERS" }),
|
||||
async (req: Request, res: Response) => {
|
||||
let user = await User.findOneOrFail({
|
||||
await User.findOneOrFail({
|
||||
where: { id: req.params.id },
|
||||
select: [...PrivateUserProjection, "data"],
|
||||
});
|
||||
|
@ -19,11 +19,9 @@
|
||||
import { Router, Request, Response } from "express";
|
||||
import {
|
||||
PublicConnectedAccount,
|
||||
PublicUser,
|
||||
User,
|
||||
UserPublic,
|
||||
Member,
|
||||
Guild,
|
||||
UserProfileModifySchema,
|
||||
handleFile,
|
||||
PrivateUserProjection,
|
||||
@ -53,8 +51,8 @@ router.get(
|
||||
relations: ["connected_accounts"],
|
||||
});
|
||||
|
||||
var mutual_guilds: object[] = [];
|
||||
var premium_guild_since;
|
||||
const mutual_guilds: object[] = [];
|
||||
let premium_guild_since;
|
||||
|
||||
if (with_mutual_guilds == "true") {
|
||||
const requested_member = await Member.find({
|
||||
@ -169,7 +167,7 @@ router.patch(
|
||||
`/banners/${req.user_id}`,
|
||||
body.banner as string,
|
||||
);
|
||||
let user = await User.findOneOrFail({
|
||||
const user = await User.findOneOrFail({
|
||||
where: { id: req.user_id },
|
||||
select: [...PrivateUserProjection, "data"],
|
||||
});
|
||||
@ -177,6 +175,7 @@ router.patch(
|
||||
user.assign(body);
|
||||
await user.save();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
delete user.data;
|
||||
|
||||
|
@ -36,7 +36,7 @@ router.get(
|
||||
"/",
|
||||
route({ test: { response: { body: "UserRelationsResponse" } } }),
|
||||
async (req: Request, res: Response) => {
|
||||
var mutual_relations: object[] = [];
|
||||
const mutual_relations: object[] = [];
|
||||
const requested_relations = await User.findOneOrFail({
|
||||
where: { id: req.params.id },
|
||||
relations: ["relationships"],
|
||||
@ -53,7 +53,7 @@ router.get(
|
||||
rmem.type === 1 &&
|
||||
rmem.to_id !== req.user_id
|
||||
) {
|
||||
var relation_user = await User.getPublicUser(rmem.to_id);
|
||||
const relation_user = await User.getPublicUser(rmem.to_id);
|
||||
|
||||
mutual_relations.push({
|
||||
id: relation_user.id,
|
||||
|
@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { Guild, Member, User } from "@fosscord/util";
|
||||
import { Member, User } from "@fosscord/util";
|
||||
import { route } from "@fosscord/api";
|
||||
import bcrypt from "bcrypt";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
@ -43,7 +43,7 @@ router.patch(
|
||||
const body = req.body as UserGuildSettingsSchema;
|
||||
|
||||
if (body.channel_overrides) {
|
||||
for (var channel in body.channel_overrides) {
|
||||
for (const channel in body.channel_overrides) {
|
||||
Channel.findOneOrFail({ where: { id: channel } });
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ router.patch(
|
||||
});
|
||||
|
||||
// Populated on password change
|
||||
var newToken: string | undefined;
|
||||
let newToken: string | undefined;
|
||||
|
||||
if (body.avatar)
|
||||
body.avatar = await handleFile(
|
||||
@ -120,7 +120,7 @@ router.patch(
|
||||
}
|
||||
|
||||
if (body.username) {
|
||||
var check_username = body?.username?.replace(/\s/g, "");
|
||||
const check_username = body?.username?.replace(/\s/g, "");
|
||||
if (!check_username) {
|
||||
throw FieldErrors({
|
||||
username: {
|
||||
@ -153,6 +153,7 @@ router.patch(
|
||||
user.validate();
|
||||
await user.save();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
delete user.data;
|
||||
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
generateMfaBackupCodes,
|
||||
User,
|
||||
CodesVerificationSchema,
|
||||
DiscordApiErrors,
|
||||
} from "@fosscord/util";
|
||||
|
||||
const router = Router();
|
||||
@ -31,14 +32,17 @@ router.post(
|
||||
"/",
|
||||
route({ body: "CodesVerificationSchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
const { key, nonce, regenerate } = req.body as CodesVerificationSchema;
|
||||
// const { key, nonce, regenerate } = req.body as CodesVerificationSchema;
|
||||
const { regenerate } = req.body as CodesVerificationSchema;
|
||||
|
||||
// TODO: We don't have email/etc etc, so can't send a verification code.
|
||||
// Once that's done, this route can verify `key`
|
||||
|
||||
const user = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||
// const user = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||
if ((await User.count({ where: { id: req.user_id } })) === 0)
|
||||
throw DiscordApiErrors.UNKNOWN_USER;
|
||||
|
||||
var codes: BackupCode[];
|
||||
let codes: BackupCode[];
|
||||
if (regenerate) {
|
||||
await BackupCode.update(
|
||||
{ user: { id: req.user_id } },
|
||||
|
@ -51,7 +51,7 @@ router.post(
|
||||
});
|
||||
}
|
||||
|
||||
var codes: BackupCode[];
|
||||
let codes: BackupCode[];
|
||||
if (regenerate) {
|
||||
await BackupCode.update(
|
||||
{ user: { id: req.user_id } },
|
||||
|
@ -42,7 +42,7 @@ router.post(
|
||||
|
||||
const backup = await BackupCode.findOne({ where: { code: body.code } });
|
||||
if (!backup) {
|
||||
const ret = verifyToken(user.totp_secret!, body.code);
|
||||
const ret = verifyToken(user.totp_secret || "", body.code);
|
||||
if (!ret || ret.delta != 0)
|
||||
throw new HTTPError(
|
||||
req.t("auth:login.INVALID_TOTP_CODE"),
|
||||
|
@ -57,7 +57,7 @@ router.post(
|
||||
if (verifyToken(body.secret, body.code)?.delta != 0)
|
||||
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
||||
|
||||
let backup_codes = generateMfaBackupCodes(req.user_id);
|
||||
const backup_codes = generateMfaBackupCodes(req.user_id);
|
||||
await Promise.all(backup_codes.map((x) => x.save()));
|
||||
await User.update(
|
||||
{ id: req.user_id },
|
||||
|
@ -175,7 +175,7 @@ async function updateRelationship(
|
||||
select: userProjection,
|
||||
});
|
||||
|
||||
var relationship = user.relationships.find((x) => x.to_id === id);
|
||||
let relationship = user.relationships.find((x) => x.to_id === id);
|
||||
const friendRequest = friend.relationships.find(
|
||||
(x) => x.to_id === req.user_id,
|
||||
);
|
||||
@ -219,13 +219,13 @@ async function updateRelationship(
|
||||
if (user.relationships.length >= maxFriends)
|
||||
throw DiscordApiErrors.MAXIMUM_FRIENDS.withParams(maxFriends);
|
||||
|
||||
var incoming_relationship = Relationship.create({
|
||||
let incoming_relationship = Relationship.create({
|
||||
nickname: undefined,
|
||||
type: RelationshipType.incoming,
|
||||
to: user,
|
||||
from: friend,
|
||||
});
|
||||
var outgoing_relationship = Relationship.create({
|
||||
let outgoing_relationship = Relationship.create({
|
||||
nickname: undefined,
|
||||
type: RelationshipType.outgoing,
|
||||
to: friend,
|
||||
|
@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { OrmUtils, User, UserSettingsSchema } from "@fosscord/util";
|
||||
import { User, UserSettingsSchema } from "@fosscord/util";
|
||||
import { route } from "@fosscord/api";
|
||||
|
||||
const router = Router();
|
||||
|
@ -26,7 +26,7 @@ config();
|
||||
import { FosscordServer } from "./Server";
|
||||
import cluster from "cluster";
|
||||
import os from "os";
|
||||
var cores = 1;
|
||||
let cores = 1;
|
||||
try {
|
||||
cores = Number(process.env.THREADS) || os.cpus().length;
|
||||
} catch {
|
||||
@ -41,16 +41,17 @@ if (cluster.isPrimary && process.env.NODE_ENV == "production") {
|
||||
cluster.fork();
|
||||
}
|
||||
|
||||
cluster.on("exit", (worker, code, signal) => {
|
||||
cluster.on("exit", (worker) => {
|
||||
console.log(`worker ${worker.process.pid} died, restart worker`);
|
||||
cluster.fork();
|
||||
});
|
||||
} else {
|
||||
var port = Number(process.env.PORT) || 3001;
|
||||
const port = Number(process.env.PORT) || 3001;
|
||||
|
||||
const server = new FosscordServer({ port });
|
||||
server.start().catch(console.error);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
global.server = server;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Config, Guild, Session } from "@fosscord/util";
|
||||
import { Session } from "@fosscord/util";
|
||||
|
||||
export async function initInstance() {
|
||||
// TODO: clean up database and delete tombstone data
|
||||
@ -24,15 +24,14 @@ export async function initInstance() {
|
||||
|
||||
// create default guild and add it to auto join
|
||||
// TODO: check if any current user is not part of autoJoinGuilds
|
||||
const { autoJoin } = Config.get().guild;
|
||||
// const { autoJoin } = Config.get().guild;
|
||||
|
||||
if (autoJoin.enabled && !autoJoin.guilds?.length) {
|
||||
let guild = await Guild.findOne({ where: {}, select: ["id"] });
|
||||
if (guild) {
|
||||
// @ts-ignore
|
||||
await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } });
|
||||
}
|
||||
}
|
||||
// if (autoJoin.enabled && !autoJoin.guilds?.length) {
|
||||
// const guild = await Guild.findOne({ where: {}, select: ["id"] });
|
||||
// if (guild) {
|
||||
// await Config.set({ guild: { autoJoin: { guilds: [guild.id] } } });
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO: do no clear sessions for instance cluster
|
||||
await Session.delete({});
|
||||
|
@ -51,7 +51,7 @@ const allow_empty = false;
|
||||
// TODO: embed gifs/videos/images
|
||||
|
||||
const LINK_REGEX =
|
||||
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
|
||||
/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/g;
|
||||
|
||||
export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
const channel = await Channel.findOneOrFail({
|
||||
@ -129,7 +129,6 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
}
|
||||
/** Q: should be checked if the referenced message exists? ANSWER: NO
|
||||
otherwise backfilling won't work **/
|
||||
// @ts-ignore
|
||||
message.type = MessageType.REPLY;
|
||||
}
|
||||
|
||||
@ -144,29 +143,29 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
throw new HTTPError("Empty messages are not allowed", 50006);
|
||||
}
|
||||
|
||||
var content = opts.content;
|
||||
var mention_channel_ids = [] as string[];
|
||||
var mention_role_ids = [] as string[];
|
||||
var mention_user_ids = [] as string[];
|
||||
var mention_everyone = false;
|
||||
let content = opts.content;
|
||||
const mention_channel_ids = [] as string[];
|
||||
const mention_role_ids = [] as string[];
|
||||
const mention_user_ids = [] as string[];
|
||||
let mention_everyone = false;
|
||||
|
||||
if (content) {
|
||||
// TODO: explicit-only mentions
|
||||
message.content = content.trim();
|
||||
content = content.replace(/ *\`[^)]*\` */g, ""); // remove codeblocks
|
||||
for (const [_, mention] of content.matchAll(CHANNEL_MENTION)) {
|
||||
content = content.replace(/ *`[^)]*` */g, ""); // remove codeblocks
|
||||
for (const [, mention] of content.matchAll(CHANNEL_MENTION)) {
|
||||
if (!mention_channel_ids.includes(mention))
|
||||
mention_channel_ids.push(mention);
|
||||
}
|
||||
|
||||
for (const [_, mention] of content.matchAll(USER_MENTION)) {
|
||||
for (const [, mention] of content.matchAll(USER_MENTION)) {
|
||||
if (!mention_user_ids.includes(mention))
|
||||
mention_user_ids.push(mention);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
Array.from(content.matchAll(ROLE_MENTION)).map(
|
||||
async ([_, mention]) => {
|
||||
async ([, mention]) => {
|
||||
const role = await Role.findOneOrFail({
|
||||
where: { id: mention, guild_id: channel.guild_id },
|
||||
});
|
||||
@ -198,8 +197,8 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
|
||||
// TODO: cache link result in db
|
||||
export async function postHandleMessage(message: Message) {
|
||||
const content = message.content?.replace(/ *\`[^)]*\` */g, ""); // remove markdown
|
||||
var links = content?.match(LINK_REGEX);
|
||||
const content = message.content?.replace(/ *`[^)]*` */g, ""); // remove markdown
|
||||
let links = content?.match(LINK_REGEX);
|
||||
if (!links) return;
|
||||
|
||||
const data = { ...message };
|
||||
@ -232,8 +231,8 @@ export async function postHandleMessage(message: Message) {
|
||||
// tried to use shorthand but types didn't like me L
|
||||
if (!Array.isArray(res)) res = [res];
|
||||
|
||||
for (var embed of res) {
|
||||
var cache = EmbedCache.create({
|
||||
for (const embed of res) {
|
||||
const cache = EmbedCache.create({
|
||||
url: link,
|
||||
embed: embed,
|
||||
});
|
||||
@ -279,7 +278,10 @@ export async function sendMessage(opts: MessageOptions) {
|
||||
} as MessageCreateEvent),
|
||||
]);
|
||||
|
||||
postHandleMessage(message).catch((e) => {}); // no await as it should catch error non-blockingly
|
||||
// no await as it should catch error non-blockingly
|
||||
postHandleMessage(message).catch((e) =>
|
||||
console.error("[Message] post-message handler failed", e),
|
||||
);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ export async function getVoiceRegions(ipAddress: string, vip: boolean) {
|
||||
|
||||
let min = Number.POSITIVE_INFINITY;
|
||||
|
||||
for (let ar of availableRegions) {
|
||||
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,
|
||||
|
@ -34,6 +34,8 @@ import { NextFunction, Request, Response } from "express";
|
||||
import { AnyValidateFunction } from "ajv/dist/core";
|
||||
|
||||
declare global {
|
||||
// TODO: fix this
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Express {
|
||||
interface Request {
|
||||
permission?: Permissions;
|
||||
@ -53,7 +55,7 @@ export interface RouteOptions {
|
||||
body?: `${string}Schema`; // typescript interface name
|
||||
test?: {
|
||||
response?: RouteResponse;
|
||||
body?: any;
|
||||
body?: unknown;
|
||||
path?: string;
|
||||
event?: EVENT | EVENT[];
|
||||
headers?: Record<string, string>;
|
||||
@ -61,7 +63,7 @@ export interface RouteOptions {
|
||||
}
|
||||
|
||||
export function route(opts: RouteOptions) {
|
||||
var validate: AnyValidateFunction<any> | undefined;
|
||||
let validate: AnyValidateFunction | undefined;
|
||||
if (opts.body) {
|
||||
validate = ajv.getSchema(opts.body);
|
||||
if (!validate) throw new Error(`Body schema ${opts.body} not found`);
|
||||
|
@ -17,13 +17,13 @@
|
||||
*/
|
||||
|
||||
import { Config, Embed, EmbedType } from "@fosscord/util";
|
||||
import fetch, { Response } from "node-fetch";
|
||||
import fetch, { RequestInit } from "node-fetch";
|
||||
import * as cheerio from "cheerio";
|
||||
import probe from "probe-image-size";
|
||||
import crypto from "crypto";
|
||||
import { yellow } from "picocolors";
|
||||
|
||||
export const DEFAULT_FETCH_OPTIONS: any = {
|
||||
export const DEFAULT_FETCH_OPTIONS: RequestInit = {
|
||||
redirect: "follow",
|
||||
follow: 1,
|
||||
headers: {
|
||||
@ -50,7 +50,7 @@ export const getProxyUrl = (
|
||||
|
||||
// Imagor
|
||||
if (imagorServerUrl) {
|
||||
let path = `${width}x${height}/${url.host}${url.pathname}`;
|
||||
const path = `${width}x${height}/${url.host}${url.pathname}`;
|
||||
|
||||
const hash = crypto
|
||||
.createHmac("sha1", secret)
|
||||
@ -92,8 +92,8 @@ export const getMetaDescriptions = (text: string) => {
|
||||
image: getMeta($, "og:image") || getMeta($, "twitter:image"),
|
||||
image_fallback: $(`image`).attr("src"),
|
||||
video_fallback: $(`video`).attr("src"),
|
||||
width: parseInt(getMeta($, "og:image:width")!) || 0,
|
||||
height: parseInt(getMeta($, "og:image:height")!) || 0,
|
||||
width: parseInt(getMeta($, "og:image:width") || "0"),
|
||||
height: parseInt(getMeta($, "og:image:height") || "0"),
|
||||
url: getMeta($, "og:url"),
|
||||
youtube_embed: getMeta($, "og:video:secure_url"),
|
||||
};
|
||||
@ -192,8 +192,8 @@ export const EmbedHandlers: {
|
||||
proxy_url: metas.image
|
||||
? getProxyUrl(
|
||||
new URL(metas.image),
|
||||
metas.width!,
|
||||
metas.height!,
|
||||
metas.width,
|
||||
metas.height,
|
||||
)
|
||||
: undefined,
|
||||
},
|
||||
@ -239,9 +239,9 @@ export const EmbedHandlers: {
|
||||
const text = json.data.text;
|
||||
const created_at = new Date(json.data.created_at);
|
||||
const metrics = json.data.public_metrics;
|
||||
let media = json.includes.media?.filter(
|
||||
(x: any) => x.type == "photo",
|
||||
) as any[]; // TODO: video
|
||||
const media = json.includes.media?.filter(
|
||||
(x: { type: string }) => x.type == "photo",
|
||||
);
|
||||
|
||||
const embed: Embed = {
|
||||
type: EmbedType.rich,
|
||||
@ -334,7 +334,7 @@ export const EmbedHandlers: {
|
||||
width: 640,
|
||||
height: 640,
|
||||
proxy_url: metas.image
|
||||
? getProxyUrl(new URL(metas.image!), 640, 640)
|
||||
? getProxyUrl(new URL(metas.image), 640, 640)
|
||||
: undefined,
|
||||
url: metas.image,
|
||||
},
|
||||
@ -365,9 +365,9 @@ export const EmbedHandlers: {
|
||||
url: url.href,
|
||||
proxy_url: metas.image
|
||||
? getProxyUrl(
|
||||
new URL(metas.image!),
|
||||
metas.width!,
|
||||
metas.height!,
|
||||
new URL(metas.image),
|
||||
metas.width,
|
||||
metas.height,
|
||||
)
|
||||
: undefined,
|
||||
},
|
||||
@ -395,7 +395,7 @@ export const EmbedHandlers: {
|
||||
height: 215,
|
||||
url: metas.image,
|
||||
proxy_url: metas.image
|
||||
? getProxyUrl(new URL(metas.image!), 460, 215)
|
||||
? getProxyUrl(new URL(metas.image), 460, 215)
|
||||
: undefined,
|
||||
},
|
||||
provider: {
|
||||
@ -436,7 +436,7 @@ export const EmbedHandlers: {
|
||||
// TODO: does this adjust with aspect ratio?
|
||||
width: metas.width,
|
||||
height: metas.height,
|
||||
url: metas.youtube_embed!,
|
||||
url: metas.youtube_embed,
|
||||
},
|
||||
url: url.href,
|
||||
type: EmbedType.video,
|
||||
@ -447,9 +447,9 @@ export const EmbedHandlers: {
|
||||
url: metas.image,
|
||||
proxy_url: metas.image
|
||||
? getProxyUrl(
|
||||
new URL(metas.image!),
|
||||
metas.width!,
|
||||
metas.height!,
|
||||
new URL(metas.image),
|
||||
metas.width,
|
||||
metas.height,
|
||||
)
|
||||
: undefined,
|
||||
},
|
||||
|
@ -24,7 +24,7 @@ import crypto from "crypto";
|
||||
|
||||
export function random(length = 6) {
|
||||
// Declare all characters
|
||||
let chars =
|
||||
const chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
// Pick characers randomly
|
||||
@ -38,14 +38,14 @@ export function random(length = 6) {
|
||||
|
||||
export function snowflakeBasedInvite() {
|
||||
// Declare all characters
|
||||
let chars =
|
||||
const chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
let base = BigInt(chars.length);
|
||||
const base = BigInt(chars.length);
|
||||
let snowflake = Snowflake.generateWorkerProcess();
|
||||
|
||||
// snowflakes hold ~10.75 characters worth of entropy;
|
||||
// safe to generate a 8-char invite out of them
|
||||
let str = "";
|
||||
const str = "";
|
||||
for (let i = 0; i < 10; i++) {
|
||||
str.concat(chars.charAt(Number(snowflake % base)));
|
||||
snowflake = snowflake / base;
|
||||
|
@ -47,7 +47,10 @@ export async function verifyCaptcha(response: string, ip?: string) {
|
||||
const { security } = Config.get();
|
||||
const { service, secret, sitekey } = security.captcha;
|
||||
|
||||
if (!service) throw new Error("Cannot verify captcha without service");
|
||||
if (!service || !secret || !sitekey)
|
||||
throw new Error(
|
||||
"CAPTCHA is not configured correctly. https://docs.fosscord.com/setup/server/security/captcha/",
|
||||
);
|
||||
|
||||
const res = await fetch(verifyEndpoints[service], {
|
||||
method: "POST",
|
||||
@ -56,9 +59,9 @@ export async function verifyCaptcha(response: string, ip?: string) {
|
||||
},
|
||||
body:
|
||||
`response=${encodeURIComponent(response)}` +
|
||||
`&secret=${encodeURIComponent(secret!)}` +
|
||||
`&sitekey=${encodeURIComponent(sitekey!)}` +
|
||||
(ip ? `&remoteip=${encodeURIComponent(ip!)}` : ""),
|
||||
`&secret=${encodeURIComponent(secret)}` +
|
||||
`&sitekey=${encodeURIComponent(sitekey)}` +
|
||||
(ip ? `&remoteip=${encodeURIComponent(ip)}` : ""),
|
||||
});
|
||||
|
||||
return (await res.json()) as hcaptchaResponse | recaptchaResponse;
|
||||
|
@ -85,7 +85,7 @@ export async function IPAnalysis(ip: string): Promise<typeof exampleData> {
|
||||
|
||||
return (
|
||||
await fetch(`https://api.ipdata.co/${ip}?api-key=${ipdataApiKey}`)
|
||||
).json() as any; // TODO: types
|
||||
).json();
|
||||
}
|
||||
|
||||
export function isProxy(data: typeof exampleData) {
|
||||
@ -97,14 +97,21 @@ export function isProxy(data: typeof exampleData) {
|
||||
}
|
||||
|
||||
export function getIpAdress(req: Request): string {
|
||||
// TODO: express can do this (trustProxies: true)?
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
req.headers[Config.get().security.forwadedFor] ||
|
||||
req.socket.remoteAddress
|
||||
);
|
||||
}
|
||||
|
||||
export function distanceBetweenLocations(loc1: any, loc2: any): number {
|
||||
type Location = { latitude: number; longitude: number };
|
||||
export function distanceBetweenLocations(
|
||||
loc1: Location,
|
||||
loc2: Location,
|
||||
): number {
|
||||
return distanceBetweenCoords(
|
||||
loc1.latitude,
|
||||
loc1.longitude,
|
||||
|
@ -23,7 +23,7 @@ const reNUMBER = /[0-9]/g;
|
||||
const reUPPERCASELETTER = /[A-Z]/g;
|
||||
const reSYMBOLS = /[A-Z,a-z,0-9]/g;
|
||||
|
||||
const blocklist: string[] = []; // TODO: update ones passwordblocklist is stored in db
|
||||
// const blocklist: string[] = []; // TODO: update ones passwordblocklist is stored in db
|
||||
/*
|
||||
* https://en.wikipedia.org/wiki/Password_policy
|
||||
* password must meet following criteria, to be perfect:
|
||||
@ -38,7 +38,7 @@ const blocklist: string[] = []; // TODO: update ones passwordblocklist is stored
|
||||
export function checkPassword(password: string): number {
|
||||
const { minLength, minNumbers, minUpperCase, minSymbols } =
|
||||
Config.get().register.password;
|
||||
var strength = 0;
|
||||
let strength = 0;
|
||||
|
||||
// checks for total password len
|
||||
if (password.length >= minLength - 1) {
|
||||
@ -68,13 +68,13 @@ export function checkPassword(password: string): number {
|
||||
strength = 0;
|
||||
}
|
||||
|
||||
let entropyMap: { [key: string]: number } = {};
|
||||
const entropyMap: { [key: string]: number } = {};
|
||||
for (let i = 0; i < password.length; i++) {
|
||||
if (entropyMap[password[i]]) entropyMap[password[i]]++;
|
||||
else entropyMap[password[i]] = 1;
|
||||
}
|
||||
|
||||
let entropies = Object.values(entropyMap);
|
||||
const entropies = Object.values(entropyMap);
|
||||
|
||||
entropies.map((x) => x / entropyMap.length);
|
||||
strength +=
|
||||
|
@ -24,7 +24,7 @@ import * as Api from "@fosscord/api";
|
||||
import * as Gateway from "@fosscord/gateway";
|
||||
import { CDNServer } from "@fosscord/cdn";
|
||||
import express from "express";
|
||||
import { green, bold, yellow } from "picocolors";
|
||||
import { green, bold } from "picocolors";
|
||||
import { Config, initDatabase, Sentry } from "@fosscord/util";
|
||||
|
||||
const app = express();
|
||||
|
@ -29,7 +29,6 @@ import { execSync } from "child_process";
|
||||
|
||||
const cores = process.env.THREADS ? parseInt(process.env.THREADS) : 1;
|
||||
|
||||
if (cluster.isPrimary) {
|
||||
function getCommitOrFail() {
|
||||
try {
|
||||
return execSync("git rev-parse HEAD").toString().trim();
|
||||
@ -37,6 +36,8 @@ if (cluster.isPrimary) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (cluster.isPrimary) {
|
||||
const commit = getCommitOrFail();
|
||||
|
||||
console.log(
|
||||
@ -81,14 +82,14 @@ Cores: ${cyan(os.cpus().length)} (Using ${cores} thread(s).)
|
||||
// Fork workers.
|
||||
for (let i = 0; i < cores; i++) {
|
||||
// Delay each worker start if using sqlite database to prevent locking it
|
||||
let delay = process.env.DATABASE?.includes("://") ? 0 : i * 1000;
|
||||
const delay = process.env.DATABASE?.includes("://") ? 0 : i * 1000;
|
||||
setTimeout(() => {
|
||||
cluster.fork();
|
||||
console.log(`[Process] worker ${cyan(i)} started.`);
|
||||
}, delay);
|
||||
}
|
||||
|
||||
cluster.on("message", (sender: Worker, message: any) => {
|
||||
cluster.on("message", (sender: Worker, message) => {
|
||||
for (const id in cluster.workers) {
|
||||
const worker = cluster.workers[id];
|
||||
if (worker === sender || !worker) continue;
|
||||
@ -96,7 +97,7 @@ Cores: ${cyan(os.cpus().length)} (Using ${cores} thread(s).)
|
||||
}
|
||||
});
|
||||
|
||||
cluster.on("exit", (worker: any, code: any, signal: any) => {
|
||||
cluster.on("exit", (worker) => {
|
||||
console.log(
|
||||
`[Worker] ${red(
|
||||
`died with PID: ${worker.process.pid} , restarting ...`,
|
||||
|
@ -24,7 +24,7 @@ import guildProfilesRoute from "./routes/guild-profiles";
|
||||
import iconsRoute from "./routes/role-icons";
|
||||
import bodyParser from "body-parser";
|
||||
|
||||
export interface CDNServerOptions extends ServerOptions {}
|
||||
export type CDNServerOptions = ServerOptions;
|
||||
|
||||
export class CDNServer extends Server {
|
||||
public declare options: CDNServerOptions;
|
||||
|
@ -41,7 +41,7 @@ router.post(
|
||||
throw new HTTPError("Invalid request signature");
|
||||
if (!req.file) throw new HTTPError("file missing");
|
||||
|
||||
const { buffer, mimetype, size, originalname, fieldname } = req.file;
|
||||
const { buffer, mimetype, size, originalname } = req.file;
|
||||
const { channel_id } = req.params;
|
||||
const filename = originalname
|
||||
.replaceAll(" ", "_")
|
||||
@ -53,8 +53,8 @@ router.post(
|
||||
Config.get()?.cdn.endpointPublic || "http://localhost:3003";
|
||||
|
||||
await storage.set(path, buffer);
|
||||
var width;
|
||||
var height;
|
||||
let width;
|
||||
let height;
|
||||
if (mimetype.includes("image")) {
|
||||
const dimensions = imageSize(buffer);
|
||||
if (dimensions) {
|
||||
@ -81,10 +81,10 @@ router.get(
|
||||
"/:channel_id/:id/:filename",
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id, id, filename } = req.params;
|
||||
const { format } = req.query;
|
||||
// const { format } = req.query;
|
||||
|
||||
const path = `attachments/${channel_id}/${id}/${filename}`;
|
||||
let file = await storage.get(path);
|
||||
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";
|
||||
|
@ -48,10 +48,10 @@ router.post(
|
||||
if (req.headers.signature !== Config.get().security.requestSignature)
|
||||
throw new HTTPError("Invalid request signature");
|
||||
if (!req.file) throw new HTTPError("Missing file");
|
||||
const { buffer, mimetype, size, originalname, fieldname } = req.file;
|
||||
const { buffer, size } = req.file;
|
||||
const { user_id } = req.params;
|
||||
|
||||
var hash = crypto
|
||||
let hash = crypto
|
||||
.createHash("md5")
|
||||
.update(Snowflake.generate())
|
||||
.digest("hex");
|
||||
@ -77,7 +77,7 @@ router.post(
|
||||
);
|
||||
|
||||
router.get("/:user_id", async (req: Request, res: Response) => {
|
||||
var { user_id } = req.params;
|
||||
let { user_id } = req.params;
|
||||
user_id = user_id.split(".")[0]; // remove .file extension
|
||||
const path = `avatars/${user_id}`;
|
||||
|
||||
@ -92,7 +92,8 @@ router.get("/:user_id", async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
export const getAvatar = async (req: Request, res: Response) => {
|
||||
var { user_id, hash } = req.params;
|
||||
const { user_id } = req.params;
|
||||
let { hash } = req.params;
|
||||
hash = hash.split(".")[0]; // remove .file extension
|
||||
const path = `avatars/${user_id}/${hash}`;
|
||||
|
||||
|
@ -45,7 +45,7 @@ router.post("/", 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, mimetype, size, originalname, fieldname } = req.file;
|
||||
const { buffer, size } = req.file;
|
||||
const { guild_id, user_id } = req.params;
|
||||
|
||||
let hash = crypto
|
||||
@ -72,7 +72,8 @@ router.post("/", multer.single("file"), async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
let { guild_id, user_id } = req.params;
|
||||
const { guild_id } = req.params;
|
||||
let { user_id } = req.params;
|
||||
user_id = user_id.split(".")[0]; // remove .file extension
|
||||
const path = `guilds/${guild_id}/users/${user_id}/avatars`;
|
||||
|
||||
@ -87,7 +88,8 @@ router.get("/", async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
router.get("/:hash", async (req: Request, res: Response) => {
|
||||
let { guild_id, user_id, hash } = req.params;
|
||||
const { guild_id, user_id } = req.params;
|
||||
let { hash } = req.params;
|
||||
hash = hash.split(".")[0]; // remove .file extension
|
||||
const path = `guilds/${guild_id}/users/${user_id}/avatars/${hash}`;
|
||||
|
||||
|
@ -48,10 +48,10 @@ router.post(
|
||||
if (req.headers.signature !== Config.get().security.requestSignature)
|
||||
throw new HTTPError("Invalid request signature");
|
||||
if (!req.file) throw new HTTPError("Missing file");
|
||||
const { buffer, mimetype, size, originalname, fieldname } = req.file;
|
||||
const { buffer, size } = req.file;
|
||||
const { role_id } = req.params;
|
||||
|
||||
var hash = crypto
|
||||
const hash = crypto
|
||||
.createHash("md5")
|
||||
.update(Snowflake.generate())
|
||||
.digest("hex");
|
||||
@ -76,7 +76,7 @@ router.post(
|
||||
);
|
||||
|
||||
router.get("/:role_id", async (req: Request, res: Response) => {
|
||||
var { role_id } = req.params;
|
||||
const { role_id } = req.params;
|
||||
//role_id = role_id.split(".")[0]; // remove .file extension
|
||||
const path = `role-icons/${role_id}`;
|
||||
|
||||
@ -91,7 +91,7 @@ router.get("/:role_id", async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
router.get("/:role_id/:hash", async (req: Request, res: Response) => {
|
||||
var { role_id, hash } = req.params;
|
||||
const { role_id, hash } = req.params;
|
||||
//hash = hash.split(".")[0]; // remove .file extension
|
||||
const path = `role-icons/${role_id}/${hash}`;
|
||||
|
||||
|
@ -28,7 +28,7 @@ import ExifTransformer from "exif-be-gone";
|
||||
function getPath(path: string) {
|
||||
// STORAGE_LOCATION has a default value in start.ts
|
||||
const root = process.env.STORAGE_LOCATION || "../";
|
||||
var filename = join(root, path);
|
||||
const filename = join(root, path);
|
||||
|
||||
if (path.indexOf("\0") !== -1 || !filename.startsWith(root))
|
||||
throw new Error("invalid path");
|
||||
@ -51,15 +51,15 @@ export class FileStorage implements Storage {
|
||||
}
|
||||
}
|
||||
|
||||
async set(path: string, value: any) {
|
||||
async set(path: string, value: Buffer) {
|
||||
path = getPath(path);
|
||||
if (!fs.existsSync(dirname(path)))
|
||||
fs.mkdirSync(dirname(path), { recursive: true });
|
||||
|
||||
value = Readable.from(value);
|
||||
const ret = Readable.from(value);
|
||||
const cleaned_file = fs.createWriteStream(path);
|
||||
|
||||
return value.pipe(new ExifTransformer()).pipe(cleaned_file);
|
||||
ret.pipe(new ExifTransformer()).pipe(cleaned_file);
|
||||
}
|
||||
|
||||
async delete(path: string) {
|
||||
|
@ -19,7 +19,6 @@
|
||||
import { FileStorage } from "./FileStorage";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { bgCyan, black } from "picocolors";
|
||||
import { S3 } from "@aws-sdk/client-s3";
|
||||
import { S3Storage } from "./S3Storage";
|
||||
process.cwd();
|
||||
|
@ -56,7 +56,6 @@ export class Server {
|
||||
}
|
||||
|
||||
this.server.on("upgrade", (request, socket, head) => {
|
||||
// @ts-ignore
|
||||
this.ws.handleUpgrade(request, socket, head, (socket) => {
|
||||
this.ws.emit("connection", socket, request);
|
||||
});
|
||||
|
@ -26,7 +26,7 @@ import {
|
||||
User,
|
||||
} from "@fosscord/util";
|
||||
|
||||
export async function Close(this: WebSocket, code: number, reason: string) {
|
||||
export async function Close(this: WebSocket, code: number, reason: Buffer) {
|
||||
console.log("[WebSocket] closed", code, reason.toString());
|
||||
if (this.heartbeatTimeout) clearTimeout(this.heartbeatTimeout);
|
||||
if (this.readyTimeout) clearTimeout(this.readyTimeout);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user