mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-25 03:33:33 +01:00
Actually run prettier
This commit is contained in:
parent
daeb6c3587
commit
0a8ceb9e63
@ -34,12 +34,7 @@ const addToDir = (dir) => {
|
|||||||
const commentStrings = languageCommentStrings[fileType];
|
const commentStrings = languageCommentStrings[fileType];
|
||||||
if (!commentStrings) continue;
|
if (!commentStrings) continue;
|
||||||
|
|
||||||
const preamble =
|
const preamble = commentStrings[0] + "\n" + SPACEBAR_LICENSE_PREAMBLE + "\n" + commentStrings[1];
|
||||||
commentStrings[0] +
|
|
||||||
"\n" +
|
|
||||||
SPACEBAR_LICENSE_PREAMBLE +
|
|
||||||
"\n" +
|
|
||||||
commentStrings[1];
|
|
||||||
|
|
||||||
if (file.startsWith(preamble)) {
|
if (file.startsWith(preamble)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -20,9 +20,7 @@ require("module-alias/register");
|
|||||||
const getRouteDescriptions = require("./util/getRouteDescriptions");
|
const getRouteDescriptions = require("./util/getRouteDescriptions");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const {
|
const { NO_AUTHORIZATION_ROUTES } = require("../dist/api/middlewares/Authentication");
|
||||||
NO_AUTHORIZATION_ROUTES,
|
|
||||||
} = require("../dist/api/middlewares/Authentication");
|
|
||||||
require("missing-native-js-functions");
|
require("missing-native-js-functions");
|
||||||
|
|
||||||
const openapiPath = path.join(__dirname, "..", "assets", "openapi.json");
|
const openapiPath = path.join(__dirname, "..", "assets", "openapi.json");
|
||||||
@ -35,8 +33,7 @@ let specification = {
|
|||||||
openapi: "3.1.0",
|
openapi: "3.1.0",
|
||||||
info: {
|
info: {
|
||||||
title: "Spacebar Server",
|
title: "Spacebar Server",
|
||||||
description:
|
description: "Spacebar is a free open source selfhostable discord compatible chat, voice and video platform",
|
||||||
"Spacebar is a free open source selfhostable discord compatible chat, voice and video platform",
|
|
||||||
license: {
|
license: {
|
||||||
name: "AGPLV3",
|
name: "AGPLV3",
|
||||||
url: "https://www.gnu.org/licenses/agpl-3.0.en.html",
|
url: "https://www.gnu.org/licenses/agpl-3.0.en.html",
|
||||||
@ -90,8 +87,7 @@ function combineSchemas(schemas) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
specification.components = specification.components || {};
|
specification.components = specification.components || {};
|
||||||
specification.components.schemas =
|
specification.components.schemas = specification.components.schemas || {};
|
||||||
specification.components.schemas || {};
|
|
||||||
specification.components.schemas[key] = definitions[key];
|
specification.components.schemas[key] = definitions[key];
|
||||||
delete definitions[key].additionalProperties;
|
delete definitions[key].additionalProperties;
|
||||||
delete definitions[key].$schema;
|
delete definitions[key].$schema;
|
||||||
@ -170,8 +166,7 @@ function apiRoutes() {
|
|||||||
[k]: {
|
[k]: {
|
||||||
...(v.body
|
...(v.body
|
||||||
? {
|
? {
|
||||||
description:
|
description: obj?.responses?.[k]?.description || "",
|
||||||
obj?.responses?.[k]?.description || "",
|
|
||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
schema: schema,
|
schema: schema,
|
||||||
@ -218,12 +213,9 @@ function apiRoutes() {
|
|||||||
|
|
||||||
obj.tags = [...(obj.tags || []), getTag(p)].unique();
|
obj.tags = [...(obj.tags || []), getTag(p)].unique();
|
||||||
|
|
||||||
specification.paths[path] = Object.assign(
|
specification.paths[path] = Object.assign(specification.paths[path] || {}, {
|
||||||
specification.paths[path] || {},
|
[method]: obj,
|
||||||
{
|
});
|
||||||
[method]: obj,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +228,7 @@ function main() {
|
|||||||
openapiPath,
|
openapiPath,
|
||||||
JSON.stringify(specification, null, 4)
|
JSON.stringify(specification, null, 4)
|
||||||
.replaceAll("#/definitions", "#/components/schemas")
|
.replaceAll("#/definitions", "#/components/schemas")
|
||||||
.replaceAll("bigint", "number"),
|
.replaceAll("bigint", "number")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,18 +62,13 @@ const Excluded = [
|
|||||||
function main() {
|
function main() {
|
||||||
const program = TJS.programFromConfig(
|
const program = TJS.programFromConfig(
|
||||||
path.join(__dirname, "..", "tsconfig.json"),
|
path.join(__dirname, "..", "tsconfig.json"),
|
||||||
walk(path.join(__dirname, "..", "src", "util", "schemas")),
|
walk(path.join(__dirname, "..", "src", "util", "schemas"))
|
||||||
);
|
);
|
||||||
const generator = TJS.buildGenerator(program, settings);
|
const generator = TJS.buildGenerator(program, settings);
|
||||||
if (!generator || !program) return;
|
if (!generator || !program) return;
|
||||||
|
|
||||||
let schemas = generator.getUserSymbols().filter((x) => {
|
let schemas = generator.getUserSymbols().filter((x) => {
|
||||||
return (
|
return (x.endsWith("Schema") || x.endsWith("Response") || x.startsWith("API")) && !Excluded.includes(x);
|
||||||
(x.endsWith("Schema") ||
|
|
||||||
x.endsWith("Response") ||
|
|
||||||
x.startsWith("API")) &&
|
|
||||||
!Excluded.includes(x)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var definitions = {};
|
var definitions = {};
|
||||||
|
@ -22,15 +22,7 @@ const path = require("path");
|
|||||||
(async () => {
|
(async () => {
|
||||||
DataSourceOptions.setOptions({
|
DataSourceOptions.setOptions({
|
||||||
logging: true,
|
logging: true,
|
||||||
migrations: [
|
migrations: [path.join(process.cwd(), "scripts", "stagingMigration", DatabaseType, "*.js")],
|
||||||
path.join(
|
|
||||||
process.cwd(),
|
|
||||||
"scripts",
|
|
||||||
"stagingMigration",
|
|
||||||
DatabaseType,
|
|
||||||
"*.js",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const dbConnection = await DataSourceOptions.initialize();
|
const dbConnection = await DataSourceOptions.initialize();
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -22,148 +22,74 @@ module.exports = class staging1672815835837 {
|
|||||||
name = "staging1672815835837";
|
name = "staging1672815835837";
|
||||||
|
|
||||||
async up(queryRunner) {
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "FK_76ba283779c8441fd5ff819c8cf"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_settings" RENAME COLUMN "id" TO "index"`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "FK_76ba283779c8441fd5ff819c8cf"`,
|
`ALTER TABLE "user_settings" RENAME CONSTRAINT "PK_00f004f5922a0744d174530d639" TO "PK_e81f8bb92802737337d35c00981"`
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "user_settings" RENAME COLUMN "id" TO "index"`,
|
`CREATE TABLE "embed_cache" ("id" character varying NOT NULL, "url" character varying NOT NULL, "embed" text NOT NULL, CONSTRAINT "PK_0abb7581d4efc5a8b1361389c5e" PRIMARY KEY ("id"))`
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "user_settings" RENAME CONSTRAINT "PK_00f004f5922a0744d174530d639" TO "PK_e81f8bb92802737337d35c00981"`,
|
`CREATE TABLE "security_settings" ("id" character varying NOT NULL, "guild_id" character varying, "channel_id" character varying, "encryption_permission_mask" integer NOT NULL, "allowed_algorithms" text NOT NULL, "current_algorithm" character varying NOT NULL, "used_since_message" character varying, CONSTRAINT "PK_4aec436cf81177ae97a1bcec3c7" PRIMARY KEY ("id"))`
|
||||||
|
);
|
||||||
|
await queryRunner.query(`ALTER TABLE "client_release" DROP COLUMN IF EXISTS "deb_url"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "client_release" DROP COLUMN IF EXISTS "osx_url"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "client_release" DROP COLUMN IF EXISTS "win_url"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "REL_76ba283779c8441fd5ff819c8c"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN IF EXISTS "settingsId"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "client_release" ADD "platform" character varying NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "client_release" ADD "enabled" boolean NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "users" ADD "purchased_flags" integer NOT NULL DEFAULT 0`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "users" ADD "premium_usage_flags" integer NOT NULl DEFAULT 0`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "users" ADD "settingsIndex" integer`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "users" ADD CONSTRAINT "UQ_0c14beb78d8c5ccba66072adbc7" UNIQUE ("settingsIndex")`
|
||||||
|
);
|
||||||
|
await queryRunner.query(`ALTER TABLE "client_release" DROP COLUMN IF EXISTS "pub_date"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "client_release" ADD "pub_date" TIMESTAMP NOT NULL`);
|
||||||
|
await queryRunner.query(`UPDATE channels SET nsfw = false WHERE nsfw IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "nsfw" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`UPDATE channels SET flags = 0 WHERE flags IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "channels" ALTER COLUMN "flags" SET NOT NULL`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`UPDATE channels SET default_thread_rate_limit_per_user = 0 WHERE default_thread_rate_limit_per_user IS NULL`
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE TABLE "embed_cache" ("id" character varying NOT NULL, "url" character varying NOT NULL, "embed" text NOT NULL, CONSTRAINT "PK_0abb7581d4efc5a8b1361389c5e" PRIMARY KEY ("id"))`,
|
`ALTER TABLE "channels" ALTER COLUMN "default_thread_rate_limit_per_user" SET NOT NULL`
|
||||||
);
|
);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`CREATE TABLE "security_settings" ("id" character varying NOT NULL, "guild_id" character varying, "channel_id" character varying, "encryption_permission_mask" integer NOT NULL, "allowed_algorithms" text NOT NULL, "current_algorithm" character varying NOT NULL, "used_since_message" character varying, CONSTRAINT "PK_4aec436cf81177ae97a1bcec3c7" PRIMARY KEY ("id"))`,
|
`ALTER TABLE "user_settings" DROP CONSTRAINT IF EXISTS "PK_e81f8bb92802737337d35c00981"`
|
||||||
);
|
);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_settings" DROP COLUMN IF EXISTS "index"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "user_settings" ADD "index" SERIAL NOT NULL`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`ALTER TABLE "client_release" DROP COLUMN IF EXISTS "deb_url"`,
|
`ALTER TABLE "user_settings" ADD CONSTRAINT "PK_e81f8bb92802737337d35c00981" PRIMARY KEY ("index")`
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "client_release" DROP COLUMN IF EXISTS "osx_url"`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "client_release" DROP COLUMN IF EXISTS "win_url"`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "REL_76ba283779c8441fd5ff819c8c"`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "users" DROP COLUMN IF EXISTS "settingsId"`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "client_release" ADD "platform" character varying NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "client_release" ADD "enabled" boolean NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "users" ADD "purchased_flags" integer NOT NULL DEFAULT 0`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "users" ADD "premium_usage_flags" integer NOT NULl DEFAULT 0`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "users" ADD "settingsIndex" integer`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "users" ADD CONSTRAINT "UQ_0c14beb78d8c5ccba66072adbc7" UNIQUE ("settingsIndex")`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "client_release" DROP COLUMN IF EXISTS "pub_date"`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "client_release" ADD "pub_date" TIMESTAMP NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`UPDATE channels SET nsfw = false WHERE nsfw IS NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "channels" ALTER COLUMN "nsfw" SET NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`UPDATE channels SET flags = 0 WHERE flags IS NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "channels" ALTER COLUMN "flags" SET NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`UPDATE channels SET default_thread_rate_limit_per_user = 0 WHERE default_thread_rate_limit_per_user IS NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "channels" ALTER COLUMN "default_thread_rate_limit_per_user" SET NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "user_settings" DROP CONSTRAINT IF EXISTS "PK_e81f8bb92802737337d35c00981"`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "user_settings" DROP COLUMN IF EXISTS "index"`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "user_settings" ADD "index" SERIAL NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "user_settings" ADD CONSTRAINT "PK_e81f8bb92802737337d35c00981" PRIMARY KEY ("index")`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "guilds" DROP COLUMN IF EXISTS "primary_category_id"`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "guilds" ADD "primary_category_id" character varying`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`UPDATE guilds SET large = false WHERE large IS NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "guilds" ALTER COLUMN "large" SET NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`UPDATE guilds SET premium_tier = 0 WHERE premium_tier IS NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "guilds" ALTER COLUMN "premium_tier" SET NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`UPDATE guilds SET unavailable = false WHERE unavailable IS NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "guilds" ALTER COLUMN "unavailable" SET NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`UPDATE guilds SET widget_enabled = false WHERE widget_enabled IS NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "guilds" ALTER COLUMN "widget_enabled" SET NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`UPDATE guilds SET nsfw = false WHERE nsfw IS NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "guilds" ALTER COLUMN "nsfw" SET NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "members" DROP COLUMN IF EXISTS "premium_since"`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "members" ADD "premium_since" bigint`,
|
|
||||||
);
|
);
|
||||||
|
await queryRunner.query(`ALTER TABLE "guilds" DROP COLUMN IF EXISTS "primary_category_id"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "guilds" ADD "primary_category_id" character varying`);
|
||||||
|
await queryRunner.query(`UPDATE guilds SET large = false WHERE large IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "guilds" ALTER COLUMN "large" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`UPDATE guilds SET premium_tier = 0 WHERE premium_tier IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "guilds" ALTER COLUMN "premium_tier" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`UPDATE guilds SET unavailable = false WHERE unavailable IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "guilds" ALTER COLUMN "unavailable" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`UPDATE guilds SET widget_enabled = false WHERE widget_enabled IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "guilds" ALTER COLUMN "widget_enabled" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`UPDATE guilds SET nsfw = false WHERE nsfw IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "guilds" ALTER COLUMN "nsfw" SET NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "members" DROP COLUMN IF EXISTS "premium_since"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "members" ADD "premium_since" bigint`);
|
||||||
await queryRunner.query(`ALTER TABLE members ADD theme_colors text`);
|
await queryRunner.query(`ALTER TABLE members ADD theme_colors text`);
|
||||||
await queryRunner.query(`ALTER TABLE members ADD pronouns varchar`);
|
await queryRunner.query(`ALTER TABLE members ADD pronouns varchar`);
|
||||||
await queryRunner.query(`UPDATE users SET bio = '' WHERE bio IS NULL`);
|
await queryRunner.query(`UPDATE users SET bio = '' WHERE bio IS NULL`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(`ALTER TABLE users ALTER COLUMN bio SET NOT NULL`);
|
||||||
`ALTER TABLE users ALTER COLUMN bio SET NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(`ALTER TABLE users ADD theme_colors text`);
|
await queryRunner.query(`ALTER TABLE users ADD theme_colors text`);
|
||||||
await queryRunner.query(`ALTER TABLE users ADD pronouns varchar`);
|
await queryRunner.query(`ALTER TABLE users ADD pronouns varchar`);
|
||||||
|
await queryRunner.query(`UPDATE users SET mfa_enabled = false WHERE mfa_enabled IS NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE users ALTER COLUMN mfa_enabled SET NOT NULL`);
|
||||||
await queryRunner.query(
|
await queryRunner.query(
|
||||||
`UPDATE users SET mfa_enabled = false WHERE mfa_enabled IS NULL`,
|
`ALTER TABLE "users" ADD CONSTRAINT "FK_0c14beb78d8c5ccba66072adbc7" FOREIGN KEY ("settingsIndex") REFERENCES "user_settings"("index") ON DELETE NO ACTION ON UPDATE NO ACTION`
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE users ALTER COLUMN mfa_enabled SET NOT NULL`,
|
|
||||||
);
|
|
||||||
await queryRunner.query(
|
|
||||||
`ALTER TABLE "users" ADD CONSTRAINT "FK_0c14beb78d8c5ccba66072adbc7" FOREIGN KEY ("settingsIndex") REFERENCES "user_settings"("index") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ const doTimedIdentify = () =>
|
|||||||
token: TOKEN,
|
token: TOKEN,
|
||||||
properties: {},
|
properties: {},
|
||||||
},
|
},
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case OPCODES.Dispatch:
|
case OPCODES.Dispatch:
|
||||||
|
@ -24,9 +24,7 @@
|
|||||||
const { spawn } = require("child_process");
|
const { spawn } = require("child_process");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
|
|
||||||
const server = spawn("node", [
|
const server = spawn("node", [path.join(__dirname, "..", "dist", "bundle", "start.js")]);
|
||||||
path.join(__dirname, "..", "dist", "bundle", "start.js"),
|
|
||||||
]);
|
|
||||||
|
|
||||||
server.stdout.on("data", (data) => {
|
server.stdout.on("data", (data) => {
|
||||||
process.stdout.write(data);
|
process.stdout.write(data);
|
||||||
|
@ -15,10 +15,7 @@ let currentPath = "";
|
|||||||
|
|
||||||
const proxy = (file, method, prefix, path, ...args) => {
|
const proxy = (file, method, prefix, path, ...args) => {
|
||||||
const opts = args.find((x) => x?.prototype?.OPTS_MARKER == true);
|
const opts = args.find((x) => x?.prototype?.OPTS_MARKER == true);
|
||||||
if (!opts)
|
if (!opts) return console.error(`${file} has route without route() description middleware`);
|
||||||
return console.error(
|
|
||||||
`${file} has route without route() description middleware`,
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(prefix + path + " - " + method);
|
console.log(prefix + path + " - " + method);
|
||||||
opts.file = file.replace("/dist/", "/src/").replace(".js", ".ts");
|
opts.file = file.replace("/dist/", "/src/").replace(".js", ".ts");
|
||||||
@ -26,12 +23,7 @@ const proxy = (file, method, prefix, path, ...args) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
express.Router = () => {
|
express.Router = () => {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(methods.map((method) => [method, proxy.bind(null, currentFile, method, currentPath)]));
|
||||||
methods.map((method) => [
|
|
||||||
method,
|
|
||||||
proxy.bind(null, currentFile, method, currentPath),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
RouteUtility.route = (opts) => {
|
RouteUtility.route = (opts) => {
|
||||||
@ -50,8 +42,7 @@ module.exports = function getRouteDescriptions() {
|
|||||||
currentPath = file.replace(root.slice(0, -1), "");
|
currentPath = file.replace(root.slice(0, -1), "");
|
||||||
currentPath = currentPath.split(".").slice(0, -1).join("."); // trancate .js/.ts file extension of path
|
currentPath = currentPath.split(".").slice(0, -1).join("."); // trancate .js/.ts file extension of path
|
||||||
currentPath = currentPath.replaceAll("#", ":").replaceAll("\\", "/"); // replace # with : for path parameters and windows paths with slashes
|
currentPath = currentPath.replaceAll("#", ":").replaceAll("\\", "/"); // replace # with : for path parameters and windows paths with slashes
|
||||||
if (currentPath.endsWith("/index"))
|
if (currentPath.endsWith("/index")) currentPath = currentPath.slice(0, "/index".length * -1); // delete index from path
|
||||||
currentPath = currentPath.slice(0, "/index".length * -1); // delete index from path
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
require(file);
|
require(file);
|
||||||
|
@ -41,13 +41,7 @@ import { initRateLimits } from "./middlewares/RateLimit";
|
|||||||
import { initTranslation } from "./middlewares/Translation";
|
import { initTranslation } from "./middlewares/Translation";
|
||||||
import { initInstance } from "./util/handlers/Instance";
|
import { initInstance } from "./util/handlers/Instance";
|
||||||
|
|
||||||
const PUBLIC_ASSETS_FOLDER = path.join(
|
const PUBLIC_ASSETS_FOLDER = path.join(__dirname, "..", "..", "assets", "public");
|
||||||
__dirname,
|
|
||||||
"..",
|
|
||||||
"..",
|
|
||||||
"assets",
|
|
||||||
"public",
|
|
||||||
);
|
|
||||||
|
|
||||||
export type SpacebarServerOptions = ServerOptions;
|
export type SpacebarServerOptions = ServerOptions;
|
||||||
|
|
||||||
@ -84,16 +78,11 @@ export class SpacebarServer extends Server {
|
|||||||
this.app.use(
|
this.app.use(
|
||||||
morgan("combined", {
|
morgan("combined", {
|
||||||
skip: (req, res) => {
|
skip: (req, res) => {
|
||||||
let skip = !(
|
let skip = !(process.env["LOG_REQUESTS"]?.includes(res.statusCode.toString()) ?? false);
|
||||||
process.env["LOG_REQUESTS"]?.includes(
|
if (process.env["LOG_REQUESTS"]?.charAt(0) == "-") skip = !skip;
|
||||||
res.statusCode.toString(),
|
|
||||||
) ?? false
|
|
||||||
);
|
|
||||||
if (process.env["LOG_REQUESTS"]?.charAt(0) == "-")
|
|
||||||
skip = !skip;
|
|
||||||
return skip;
|
return skip;
|
||||||
},
|
},
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,10 +101,7 @@ export class SpacebarServer extends Server {
|
|||||||
await initRateLimits(api);
|
await initRateLimits(api);
|
||||||
await initTranslation(api);
|
await initTranslation(api);
|
||||||
|
|
||||||
this.routes = await registerRoutes(
|
this.routes = await registerRoutes(this, path.join(__dirname, "routes", "/"));
|
||||||
this,
|
|
||||||
path.join(__dirname, "routes", "/"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// 404 is not an error in express, so this should not be an error middleware
|
// 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
|
// 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/v9", api);
|
||||||
app.use("/api", api); // allow unversioned requests
|
app.use("/api", api); // allow unversioned requests
|
||||||
|
|
||||||
app.get("/", (req, res) =>
|
app.get("/", (req, res) => res.sendFile(path.join(PUBLIC_ASSETS_FOLDER, "index.html")));
|
||||||
res.sendFile(path.join(PUBLIC_ASSETS_FOLDER, "index.html")),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.app.use(ErrorHandler);
|
this.app.use(ErrorHandler);
|
||||||
|
|
||||||
@ -150,8 +134,8 @@ export class SpacebarServer extends Server {
|
|||||||
if (logRequests)
|
if (logRequests)
|
||||||
console.log(
|
console.log(
|
||||||
red(
|
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();
|
return super.start();
|
||||||
|
@ -71,11 +71,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function Authentication(
|
export async function Authentication(req: Request, res: Response, next: NextFunction) {
|
||||||
req: Request,
|
|
||||||
res: Response,
|
|
||||||
next: NextFunction,
|
|
||||||
) {
|
|
||||||
if (req.method === "OPTIONS") return res.sendStatus(204);
|
if (req.method === "OPTIONS") return res.sendStatus(204);
|
||||||
const url = req.url.replace(API_PREFIX, "");
|
const url = req.url.replace(API_PREFIX, "");
|
||||||
if (url.startsWith("/invites") && req.method === "GET") return next();
|
if (url.startsWith("/invites") && req.method === "GET") return next();
|
||||||
@ -86,8 +82,7 @@ export async function Authentication(
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
return next();
|
return next();
|
||||||
if (!req.headers.authorization)
|
if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401));
|
||||||
return next(new HTTPError("Missing Authorization Header", 401));
|
|
||||||
|
|
||||||
Sentry.setUser({ id: req.user_id });
|
Sentry.setUser({ id: req.user_id });
|
||||||
|
|
||||||
|
@ -24,8 +24,7 @@ export function BodyParser(opts?: OptionsJson) {
|
|||||||
const jsonParser = bodyParser.json(opts);
|
const jsonParser = bodyParser.json(opts);
|
||||||
|
|
||||||
return (req: Request, res: Response, next: NextFunction) => {
|
return (req: Request, res: Response, next: NextFunction) => {
|
||||||
if (!req.headers["content-type"])
|
if (!req.headers["content-type"]) req.headers["content-type"] = "application/json";
|
||||||
req.headers["content-type"] = "application/json";
|
|
||||||
|
|
||||||
jsonParser(req, res, (err) => {
|
jsonParser(req, res, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -25,16 +25,10 @@ export function CORS(req: Request, res: Response, next: NextFunction) {
|
|||||||
// TODO: use better CSP
|
// TODO: use better CSP
|
||||||
res.set(
|
res.set(
|
||||||
"Content-security-policy",
|
"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';",
|
"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") || "*",
|
|
||||||
);
|
);
|
||||||
|
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();
|
next();
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,7 @@ import { HTTPError } from "lambert-server";
|
|||||||
import { ApiError, FieldError } from "@spacebar/util";
|
import { ApiError, FieldError } from "@spacebar/util";
|
||||||
const EntityNotFoundErrorRegex = /"(\w+)"/;
|
const EntityNotFoundErrorRegex = /"(\w+)"/;
|
||||||
|
|
||||||
export function ErrorHandler(
|
export function ErrorHandler(error: Error & { type?: string }, req: Request, res: Response, next: NextFunction) {
|
||||||
error: Error & { type?: string },
|
|
||||||
req: Request,
|
|
||||||
res: Response,
|
|
||||||
next: NextFunction,
|
|
||||||
) {
|
|
||||||
if (!error) return next();
|
if (!error) return next();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -35,16 +30,13 @@ export function ErrorHandler(
|
|||||||
let message = error?.toString();
|
let message = error?.toString();
|
||||||
let errors = undefined;
|
let errors = undefined;
|
||||||
|
|
||||||
if (error instanceof HTTPError && error.code)
|
if (error instanceof HTTPError && error.code) code = httpcode = error.code;
|
||||||
code = httpcode = error.code;
|
|
||||||
else if (error instanceof ApiError) {
|
else if (error instanceof ApiError) {
|
||||||
code = error.code;
|
code = error.code;
|
||||||
message = error.message;
|
message = error.message;
|
||||||
httpcode = error.httpStatus;
|
httpcode = error.httpStatus;
|
||||||
} else if (error.name === "EntityNotFoundError") {
|
} else if (error.name === "EntityNotFoundError") {
|
||||||
message = `${
|
message = `${error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item"} could not be found`;
|
||||||
error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item"
|
|
||||||
} could not be found`;
|
|
||||||
code = httpcode = 404;
|
code = httpcode = 404;
|
||||||
} else if (error instanceof FieldError) {
|
} else if (error instanceof FieldError) {
|
||||||
code = Number(error.code);
|
code = Number(error.code);
|
||||||
@ -56,12 +48,7 @@ export function ErrorHandler(
|
|||||||
code = 50109;
|
code = 50109;
|
||||||
message = "The request body contains invalid JSON.";
|
message = "The request body contains invalid JSON.";
|
||||||
} else {
|
} else {
|
||||||
console.error(
|
console.error(`[Error] ${code} ${req.url}\n`, errors || error, "\nbody:", req.body);
|
||||||
`[Error] ${code} ${req.url}\n`,
|
|
||||||
errors || error,
|
|
||||||
"\nbody:",
|
|
||||||
req.body,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (req.server?.options?.production) {
|
if (req.server?.options?.production) {
|
||||||
// don't expose internal errors to the user, instead human errors should be thrown as HTTPError
|
// 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 });
|
res.status(httpcode).json({ code: code, message, errors });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[Internal Server Error] 500`, error);
|
console.error(`[Internal Server Error] 500`, error);
|
||||||
return res
|
return res.status(500).json({ code: 500, message: "Internal Server Error" });
|
||||||
.status(500)
|
|
||||||
.json({ code: 500, message: "Internal Server Error" });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,21 +65,14 @@ export default function rateLimit(opts: {
|
|||||||
if (rights.has("BYPASS_RATE_LIMITS")) return next();
|
if (rights.has("BYPASS_RATE_LIMITS")) return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
const bucket_id =
|
const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, "");
|
||||||
opts.bucket ||
|
|
||||||
req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, "");
|
|
||||||
let executor_id = getIpAdress(req);
|
let executor_id = getIpAdress(req);
|
||||||
if (!opts.onlyIp && req.user_id) executor_id = req.user_id;
|
if (!opts.onlyIp && req.user_id) executor_id = req.user_id;
|
||||||
|
|
||||||
let max_hits = opts.count;
|
let max_hits = opts.count;
|
||||||
if (opts.bot && req.user_bot) max_hits = opts.bot;
|
if (opts.bot && req.user_bot) max_hits = opts.bot;
|
||||||
if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method))
|
if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) max_hits = opts.GET;
|
||||||
max_hits = opts.GET;
|
else if (opts.MODIFY && ["POST", "DELETE", "PATCH", "PUT"].includes(req.method)) max_hits = opts.MODIFY;
|
||||||
else if (
|
|
||||||
opts.MODIFY &&
|
|
||||||
["POST", "DELETE", "PATCH", "PUT"].includes(req.method)
|
|
||||||
)
|
|
||||||
max_hits = opts.MODIFY;
|
|
||||||
|
|
||||||
const offender = Cache.get(executor_id + bucket_id);
|
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", `${reset}`);
|
||||||
res.set(
|
res.set("X-RateLimit-Reset-After", `${Math.max(0, Math.ceil(resetAfterSec))}`);
|
||||||
"X-RateLimit-Reset-After",
|
|
||||||
`${Math.max(0, Math.ceil(resetAfterSec))}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (offender.blocked) {
|
if (offender.blocked) {
|
||||||
const global = bucket_id === "global";
|
const global = bucket_id === "global";
|
||||||
// each block violation pushes the expiry one full window further
|
// each block violation pushes the expiry one full window further
|
||||||
reset += opts.window * 1000;
|
reset += opts.window * 1000;
|
||||||
offender.expires_at = new Date(
|
offender.expires_at = new Date(offender.expires_at.getTime() + opts.window * 1000);
|
||||||
offender.expires_at.getTime() + opts.window * 1000,
|
|
||||||
);
|
|
||||||
resetAfterMs = reset - Date.now();
|
resetAfterMs = reset - Date.now();
|
||||||
resetAfterSec = Math.ceil(resetAfterMs / 1000);
|
resetAfterSec = Math.ceil(resetAfterMs / 1000);
|
||||||
|
|
||||||
@ -129,10 +117,7 @@ export default function rateLimit(opts: {
|
|||||||
res
|
res
|
||||||
.status(429)
|
.status(429)
|
||||||
.set("X-RateLimit-Remaining", "0")
|
.set("X-RateLimit-Remaining", "0")
|
||||||
.set(
|
.set("Retry-After", `${Math.max(0, Math.ceil(resetAfterSec))}`)
|
||||||
"Retry-After",
|
|
||||||
`${Math.max(0, Math.ceil(resetAfterSec))}`,
|
|
||||||
)
|
|
||||||
// TODO: error rate limit message translation
|
// TODO: error rate limit message translation
|
||||||
.send({
|
.send({
|
||||||
message: "You are being rate limited.",
|
message: "You are being rate limited.",
|
||||||
@ -156,11 +141,7 @@ export default function rateLimit(opts: {
|
|||||||
// check if error and increment error rate limit
|
// check if error and increment error rate limit
|
||||||
if (res.statusCode >= 400 && opts.error) {
|
if (res.statusCode >= 400 && opts.error) {
|
||||||
return hitRoute(hitRouteOpts);
|
return hitRoute(hitRouteOpts);
|
||||||
} else if (
|
} else if (res.statusCode >= 200 && res.statusCode < 300 && opts.success) {
|
||||||
res.statusCode >= 200 &&
|
|
||||||
res.statusCode < 300 &&
|
|
||||||
opts.success
|
|
||||||
) {
|
|
||||||
return hitRoute(hitRouteOpts);
|
return hitRoute(hitRouteOpts);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -198,7 +179,7 @@ export async function initRateLimits(app: Router) {
|
|||||||
bucket: "global",
|
bucket: "global",
|
||||||
onlyIp: true,
|
onlyIp: true,
|
||||||
...ip,
|
...ip,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
app.use(rateLimit({ bucket: "global", ...global }));
|
app.use(rateLimit({ bucket: "global", ...global }));
|
||||||
app.use(
|
app.use(
|
||||||
@ -207,24 +188,16 @@ export async function initRateLimits(app: Router) {
|
|||||||
error: true,
|
error: true,
|
||||||
onlyIp: true,
|
onlyIp: true,
|
||||||
...error,
|
...error,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
app.use("/guilds/:id", rateLimit(routes.guild));
|
app.use("/guilds/:id", rateLimit(routes.guild));
|
||||||
app.use("/webhooks/:id", rateLimit(routes.webhook));
|
app.use("/webhooks/:id", rateLimit(routes.webhook));
|
||||||
app.use("/channels/:id", rateLimit(routes.channel));
|
app.use("/channels/:id", rateLimit(routes.channel));
|
||||||
app.use("/auth/login", rateLimit(routes.auth.login));
|
app.use("/auth/login", rateLimit(routes.auth.login));
|
||||||
app.use(
|
app.use("/auth/register", rateLimit({ onlyIp: true, success: true, ...routes.auth.register }));
|
||||||
"/auth/register",
|
|
||||||
rateLimit({ onlyIp: true, success: true, ...routes.auth.register }),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function hitRoute(opts: {
|
async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number }) {
|
||||||
executor_id: string;
|
|
||||||
bucket_id: string;
|
|
||||||
max_hits: number;
|
|
||||||
window: number;
|
|
||||||
}) {
|
|
||||||
const id = opts.executor_id + opts.bucket_id;
|
const id = opts.executor_id + opts.bucket_id;
|
||||||
let limit = Cache.get(id);
|
let limit = Cache.get(id);
|
||||||
if (!limit) {
|
if (!limit) {
|
||||||
|
@ -27,12 +27,8 @@ const ASSET_FOLDER_PATH = path.join(__dirname, "..", "..", "..", "assets");
|
|||||||
|
|
||||||
export async function initTranslation(router: Router) {
|
export async function initTranslation(router: Router) {
|
||||||
const languages = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "locales"));
|
const languages = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "locales"));
|
||||||
const namespaces = fs.readdirSync(
|
const namespaces = fs.readdirSync(path.join(ASSET_FOLDER_PATH, "locales", "en"));
|
||||||
path.join(ASSET_FOLDER_PATH, "locales", "en"),
|
const ns = namespaces.filter((x) => x.endsWith(".json")).map((x) => x.slice(0, x.length - 5));
|
||||||
);
|
|
||||||
const ns = namespaces
|
|
||||||
.filter((x) => x.endsWith(".json"))
|
|
||||||
.map((x) => x.slice(0, x.length - 5));
|
|
||||||
|
|
||||||
await i18next
|
await i18next
|
||||||
.use(i18nextBackend)
|
.use(i18nextBackend)
|
||||||
@ -43,9 +39,7 @@ export async function initTranslation(router: Router) {
|
|||||||
fallbackLng: "en",
|
fallbackLng: "en",
|
||||||
ns,
|
ns,
|
||||||
backend: {
|
backend: {
|
||||||
loadPath:
|
loadPath: path.join(ASSET_FOLDER_PATH, "locales") + "/{{lng}}/{{ns}}.json",
|
||||||
path.join(ASSET_FOLDER_PATH, "locales") +
|
|
||||||
"/{{lng}}/{{ns}}.json",
|
|
||||||
},
|
},
|
||||||
load: "all",
|
load: "all",
|
||||||
});
|
});
|
||||||
|
@ -50,15 +50,14 @@ router.post(
|
|||||||
relations: ["owner"],
|
relations: ["owner"],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (app.owner.id != req.user_id)
|
if (app.owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
|
||||||
|
|
||||||
const user = await createAppBotUser(app, req);
|
const user = await createAppBotUser(app, req);
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
token: await generateToken(user.id),
|
token: await generateToken(user.id),
|
||||||
}).status(204);
|
}).status(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
@ -77,13 +76,9 @@ router.post(
|
|||||||
const bot = await User.findOneOrFail({ where: { id: req.params.id } });
|
const bot = await User.findOneOrFail({ where: { id: req.params.id } });
|
||||||
const owner = await User.findOneOrFail({ where: { id: req.user_id } });
|
const owner = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||||
|
|
||||||
if (owner.id != req.user_id)
|
if (owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
|
||||||
|
|
||||||
if (
|
if (owner.totp_secret && (!req.body.code || verifyToken(owner.totp_secret, req.body.code)))
|
||||||
owner.totp_secret &&
|
|
||||||
(!req.body.code || verifyToken(owner.totp_secret, req.body.code))
|
|
||||||
)
|
|
||||||
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
||||||
|
|
||||||
bot.data = { hash: undefined, valid_tokens_since: new Date() };
|
bot.data = { hash: undefined, valid_tokens_since: new Date() };
|
||||||
@ -93,7 +88,7 @@ router.post(
|
|||||||
const token = await generateToken(bot.id);
|
const token = await generateToken(bot.id);
|
||||||
|
|
||||||
res.json({ token }).status(200);
|
res.json({ token }).status(200);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -120,14 +115,9 @@ router.patch(
|
|||||||
|
|
||||||
if (!app.bot) throw DiscordApiErrors.BOT_ONLY_ENDPOINT;
|
if (!app.bot) throw DiscordApiErrors.BOT_ONLY_ENDPOINT;
|
||||||
|
|
||||||
if (app.owner.id != req.user_id)
|
if (app.owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
|
||||||
|
|
||||||
if (body.avatar)
|
if (body.avatar) body.avatar = await handleFile(`/avatars/${app.id}`, body.avatar as string);
|
||||||
body.avatar = await handleFile(
|
|
||||||
`/avatars/${app.id}`,
|
|
||||||
body.avatar as string,
|
|
||||||
);
|
|
||||||
|
|
||||||
app.bot.assign(body);
|
app.bot.assign(body);
|
||||||
|
|
||||||
@ -135,7 +125,7 @@ router.patch(
|
|||||||
|
|
||||||
await app.save();
|
await app.save();
|
||||||
res.json(app).status(200);
|
res.json(app).status(200);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -34,7 +34,7 @@ router.get(
|
|||||||
// TODO:
|
// TODO:
|
||||||
//const { exclude_consumed } = req.query;
|
//const { exclude_consumed } = req.query;
|
||||||
res.status(200).send([]);
|
res.status(200).send([]);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,11 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { Application, ApplicationModifySchema, DiscordApiErrors } from "@spacebar/util";
|
||||||
Application,
|
|
||||||
ApplicationModifySchema,
|
|
||||||
DiscordApiErrors,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import { verifyToken } from "node-2fa";
|
import { verifyToken } from "node-2fa";
|
||||||
@ -45,11 +41,10 @@ router.get(
|
|||||||
where: { id: req.params.id },
|
where: { id: req.params.id },
|
||||||
relations: ["owner", "bot"],
|
relations: ["owner", "bot"],
|
||||||
});
|
});
|
||||||
if (app.owner.id != req.user_id)
|
if (app.owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
|
||||||
|
|
||||||
return res.json(app);
|
return res.json(app);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -73,14 +68,9 @@ router.patch(
|
|||||||
relations: ["owner", "bot"],
|
relations: ["owner", "bot"],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (app.owner.id != req.user_id)
|
if (app.owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
|
||||||
|
|
||||||
if (
|
if (app.owner.totp_secret && (!req.body.code || verifyToken(app.owner.totp_secret, req.body.code)))
|
||||||
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);
|
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
||||||
|
|
||||||
if (app.bot) {
|
if (app.bot) {
|
||||||
@ -93,7 +83,7 @@ router.patch(
|
|||||||
await app.save();
|
await app.save();
|
||||||
|
|
||||||
return res.json(app);
|
return res.json(app);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
@ -111,20 +101,15 @@ router.post(
|
|||||||
where: { id: req.params.id },
|
where: { id: req.params.id },
|
||||||
relations: ["bot", "owner"],
|
relations: ["bot", "owner"],
|
||||||
});
|
});
|
||||||
if (app.owner.id != req.user_id)
|
if (app.owner.id != req.user_id) throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
|
||||||
|
|
||||||
if (
|
if (app.owner.totp_secret && (!req.body.code || verifyToken(app.owner.totp_secret, req.body.code)))
|
||||||
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);
|
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
||||||
|
|
||||||
await Application.delete({ id: app.id });
|
await Application.delete({ id: app.id });
|
||||||
|
|
||||||
res.send().status(200);
|
res.send().status(200);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -32,7 +32,7 @@ router.get(
|
|||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
res.json([]).status(200);
|
res.json([]).status(200);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -33,7 +33,7 @@ router.get(
|
|||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
//TODO
|
//TODO
|
||||||
res.send([]).status(200);
|
res.send([]).status(200);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,14 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { Application, ApplicationCreateSchema, Config, User, createAppBotUser, trimSpecial } from "@spacebar/util";
|
||||||
Application,
|
|
||||||
ApplicationCreateSchema,
|
|
||||||
Config,
|
|
||||||
User,
|
|
||||||
createAppBotUser,
|
|
||||||
trimSpecial,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
@ -44,7 +37,7 @@ router.get(
|
|||||||
relations: ["owner", "bot"],
|
relations: ["owner", "bot"],
|
||||||
});
|
});
|
||||||
res.json(results).status(200);
|
res.json(results).status(200);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
@ -77,7 +70,7 @@ router.post(
|
|||||||
} else await app.save();
|
} else await app.save();
|
||||||
|
|
||||||
res.json(app);
|
res.json(app);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,13 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { getIpAdress, route, verifyCaptcha } from "@spacebar/api";
|
import { getIpAdress, route, verifyCaptcha } from "@spacebar/api";
|
||||||
import {
|
import { Config, Email, FieldErrors, ForgotPasswordSchema, User } from "@spacebar/util";
|
||||||
Config,
|
|
||||||
Email,
|
|
||||||
FieldErrors,
|
|
||||||
ForgotPasswordSchema,
|
|
||||||
User,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
const router = Router();
|
const router = Router();
|
||||||
@ -47,10 +41,7 @@ router.post(
|
|||||||
|
|
||||||
const config = Config.get();
|
const config = Config.get();
|
||||||
|
|
||||||
if (
|
if (config.passwordReset.requireCaptcha && config.security.captcha.enabled) {
|
||||||
config.passwordReset.requireCaptcha &&
|
|
||||||
config.security.captcha.enabled
|
|
||||||
) {
|
|
||||||
const { sitekey, service } = config.security.captcha;
|
const { sitekey, service } = config.security.captcha;
|
||||||
if (!captcha_key) {
|
if (!captcha_key) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
@ -87,8 +78,7 @@ router.post(
|
|||||||
if (!user.email)
|
if (!user.email)
|
||||||
throw FieldErrors({
|
throw FieldErrors({
|
||||||
login: {
|
login: {
|
||||||
message:
|
message: "This account does not have an email address associated with it.",
|
||||||
"This account does not have an email address associated with it.",
|
|
||||||
code: "NO_EMAIL",
|
code: "NO_EMAIL",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -110,12 +100,10 @@ router.post(
|
|||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.error(
|
console.error(`Failed to send password reset email to ${user.username}#${user.discriminator}: ${e}`);
|
||||||
`Failed to send password reset email to ${user.username}#${user.discriminator}: ${e}`,
|
|
||||||
);
|
|
||||||
throw new HTTPError("Failed to send password reset email", 500);
|
throw new HTTPError("Failed to send password reset email", 500);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -29,13 +29,11 @@ router.get(
|
|||||||
query: {
|
query: {
|
||||||
count: {
|
count: {
|
||||||
type: "number",
|
type: "number",
|
||||||
description:
|
description: "The number of registration tokens to generate. Defaults to 1.",
|
||||||
"The number of registration tokens to generate. Defaults to 1.",
|
|
||||||
},
|
},
|
||||||
length: {
|
length: {
|
||||||
type: "number",
|
type: "number",
|
||||||
description:
|
description: "The length of each registration token. Defaults to 255.",
|
||||||
"The length of each registration token. Defaults to 255.",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
right: "OPERATOR",
|
right: "OPERATOR",
|
||||||
@ -43,18 +41,14 @@ router.get(
|
|||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const count = req.query.count ? parseInt(req.query.count as string) : 1;
|
const count = req.query.count ? parseInt(req.query.count as string) : 1;
|
||||||
const length = req.query.length
|
const length = req.query.length ? parseInt(req.query.length as string) : 255;
|
||||||
? parseInt(req.query.length as string)
|
|
||||||
: 255;
|
|
||||||
|
|
||||||
const tokens: ValidRegistrationToken[] = [];
|
const tokens: ValidRegistrationToken[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const token = ValidRegistrationToken.create({
|
const token = ValidRegistrationToken.create({
|
||||||
token: random(length),
|
token: random(length),
|
||||||
expires_at:
|
expires_at: Date.now() + Config.get().security.defaultRegistrationTokenExpiration,
|
||||||
Date.now() +
|
|
||||||
Config.get().security.defaultRegistrationTokenExpiration,
|
|
||||||
});
|
});
|
||||||
tokens.push(token);
|
tokens.push(token);
|
||||||
}
|
}
|
||||||
@ -67,16 +61,11 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
const ret = req.query.include_url
|
const ret = req.query.include_url
|
||||||
? tokens.map(
|
? tokens.map((x) => `${Config.get().general.frontPage}/register?token=${x.token}`)
|
||||||
(x) =>
|
|
||||||
`${Config.get().general.frontPage}/register?token=${
|
|
||||||
x.token
|
|
||||||
}`,
|
|
||||||
)
|
|
||||||
: tokens.map((x) => x.token);
|
: tokens.map((x) => x.token);
|
||||||
|
|
||||||
if (req.query.plain) return res.send(ret.join("\n"));
|
if (req.query.plain) return res.send(ret.join("\n"));
|
||||||
|
|
||||||
return res.json({ tokens: ret });
|
return res.json({ tokens: ret });
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
@ -38,7 +38,7 @@ router.get(
|
|||||||
country_code: country_code,
|
country_code: country_code,
|
||||||
promotional_email_opt_in: { required: true, pre_checked: false },
|
promotional_email_opt_in: { required: true, pre_checked: false },
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -47,8 +47,7 @@ router.post(
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { login, password, captcha_key, undelete } =
|
const { login, password, captcha_key, undelete } = req.body as LoginSchema;
|
||||||
req.body as LoginSchema;
|
|
||||||
|
|
||||||
const config = Config.get();
|
const config = Config.get();
|
||||||
|
|
||||||
@ -101,10 +100,7 @@ router.post(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// the salt is saved in the password refer to bcrypt docs
|
// the salt is saved in the password refer to bcrypt docs
|
||||||
const same_password = await bcrypt.compare(
|
const same_password = await bcrypt.compare(password, user.data.hash || "");
|
||||||
password,
|
|
||||||
user.data.hash || "",
|
|
||||||
);
|
|
||||||
if (!same_password) {
|
if (!same_password) {
|
||||||
throw FieldErrors({
|
throw FieldErrors({
|
||||||
login: {
|
login: {
|
||||||
@ -123,8 +119,7 @@ router.post(
|
|||||||
throw FieldErrors({
|
throw FieldErrors({
|
||||||
login: {
|
login: {
|
||||||
code: "ACCOUNT_LOGIN_VERIFICATION_EMAIL",
|
code: "ACCOUNT_LOGIN_VERIFICATION_EMAIL",
|
||||||
message:
|
message: "Email verification is required, please check your email.",
|
||||||
"Email verification is required, please check your email.",
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -153,9 +148,7 @@ router.post(
|
|||||||
const challenge = JSON.stringify({
|
const challenge = JSON.stringify({
|
||||||
publicKey: {
|
publicKey: {
|
||||||
...options,
|
...options,
|
||||||
challenge: Buffer.from(options.challenge).toString(
|
challenge: Buffer.from(options.challenge).toString("base64"),
|
||||||
"base64",
|
|
||||||
),
|
|
||||||
allowCredentials: user.security_keys.map((x) => ({
|
allowCredentials: user.security_keys.map((x) => ({
|
||||||
id: x.key_id,
|
id: x.key_id,
|
||||||
type: "public-key",
|
type: "public-key",
|
||||||
@ -179,10 +172,8 @@ router.post(
|
|||||||
|
|
||||||
if (undelete) {
|
if (undelete) {
|
||||||
// undelete refers to un'disable' here
|
// undelete refers to un'disable' here
|
||||||
if (user.disabled)
|
if (user.disabled) await User.update({ id: user.id }, { disabled: false });
|
||||||
await User.update({ id: user.id }, { disabled: false });
|
if (user.deleted) await User.update({ id: user.id }, { deleted: false });
|
||||||
if (user.deleted)
|
|
||||||
await User.update({ id: user.id }, { deleted: false });
|
|
||||||
} else {
|
} else {
|
||||||
if (user.deleted)
|
if (user.deleted)
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
@ -203,7 +194,7 @@ router.post(
|
|||||||
// https://user-images.githubusercontent.com/6506416/81051916-dd8c9900-8ec2-11ea-8794-daf12d6f31f0.png
|
// https://user-images.githubusercontent.com/6506416/81051916-dd8c9900-8ec2-11ea-8794-daf12d6f31f0.png
|
||||||
|
|
||||||
res.json({ token, settings: { ...user.settings, index: undefined } });
|
res.json({ token, settings: { ...user.settings, index: undefined } });
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,16 +31,12 @@ router.post(
|
|||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
if (req.body.provider != null || req.body.voip_provider != null) {
|
if (req.body.provider != null || req.body.voip_provider != null) {
|
||||||
console.log(
|
console.log(`[LOGOUT]: provider or voip provider not null!`, req.body);
|
||||||
`[LOGOUT]: provider or voip provider not null!`,
|
|
||||||
req.body,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
delete req.body.provider;
|
delete req.body.provider;
|
||||||
delete req.body.voip_provider;
|
delete req.body.voip_provider;
|
||||||
if (Object.keys(req.body).length != 0)
|
if (Object.keys(req.body).length != 0) console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body);
|
||||||
console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body);
|
|
||||||
}
|
}
|
||||||
res.status(204).send();
|
res.status(204).send();
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
@ -59,11 +59,7 @@ router.post(
|
|||||||
|
|
||||||
if (!backup) {
|
if (!backup) {
|
||||||
const ret = verifyToken(user.totp_secret || "", code);
|
const ret = verifyToken(user.totp_secret || "", code);
|
||||||
if (!ret || ret.delta != 0)
|
if (!ret || ret.delta != 0) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
||||||
throw new HTTPError(
|
|
||||||
req.t("auth:login.INVALID_TOTP_CODE"),
|
|
||||||
60008,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
backup.consumed = true;
|
backup.consumed = true;
|
||||||
await backup.save();
|
await backup.save();
|
||||||
@ -75,7 +71,7 @@ router.post(
|
|||||||
token: await generateToken(user.id),
|
token: await generateToken(user.id),
|
||||||
settings: { ...user.settings, index: undefined },
|
settings: { ...user.settings, index: undefined },
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,14 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { generateToken, SecurityKey, User, verifyWebAuthnToken, WebAuthn, WebAuthnTotpSchema } from "@spacebar/util";
|
||||||
generateToken,
|
|
||||||
SecurityKey,
|
|
||||||
User,
|
|
||||||
verifyWebAuthnToken,
|
|
||||||
WebAuthn,
|
|
||||||
WebAuthnTotpSchema,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import { ExpectedAssertionResult } from "fido2-lib";
|
import { ExpectedAssertionResult } from "fido2-lib";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
@ -65,46 +58,33 @@ router.post(
|
|||||||
});
|
});
|
||||||
|
|
||||||
const ret = await verifyWebAuthnToken(ticket);
|
const ret = await verifyWebAuthnToken(ticket);
|
||||||
if (!ret)
|
if (!ret) throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
||||||
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
|
||||||
|
|
||||||
await User.update({ id: user.id }, { totp_last_ticket: "" });
|
await User.update({ id: user.id }, { totp_last_ticket: "" });
|
||||||
|
|
||||||
const clientAttestationResponse = JSON.parse(code);
|
const clientAttestationResponse = JSON.parse(code);
|
||||||
|
|
||||||
if (!clientAttestationResponse.rawId)
|
if (!clientAttestationResponse.rawId) throw new HTTPError("Missing rawId", 400);
|
||||||
throw new HTTPError("Missing rawId", 400);
|
|
||||||
|
|
||||||
clientAttestationResponse.rawId = toArrayBuffer(
|
clientAttestationResponse.rawId = toArrayBuffer(Buffer.from(clientAttestationResponse.rawId, "base64url"));
|
||||||
Buffer.from(clientAttestationResponse.rawId, "base64url"),
|
|
||||||
);
|
|
||||||
|
|
||||||
const securityKey = await SecurityKey.findOneOrFail({
|
const securityKey = await SecurityKey.findOneOrFail({
|
||||||
where: {
|
where: {
|
||||||
key_id: Buffer.from(
|
key_id: Buffer.from(clientAttestationResponse.rawId, "base64url").toString("base64"),
|
||||||
clientAttestationResponse.rawId,
|
|
||||||
"base64url",
|
|
||||||
).toString("base64"),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const assertionExpectations: ExpectedAssertionResult = JSON.parse(
|
const assertionExpectations: ExpectedAssertionResult = JSON.parse(
|
||||||
Buffer.from(
|
Buffer.from(clientAttestationResponse.response.clientDataJSON, "base64").toString()
|
||||||
clientAttestationResponse.response.clientDataJSON,
|
|
||||||
"base64",
|
|
||||||
).toString(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const authnResult = await WebAuthn.fido2.assertionResult(
|
const authnResult = await WebAuthn.fido2.assertionResult(clientAttestationResponse, {
|
||||||
clientAttestationResponse,
|
...assertionExpectations,
|
||||||
{
|
factor: "second",
|
||||||
...assertionExpectations,
|
publicKey: securityKey.public_key,
|
||||||
factor: "second",
|
prevCounter: securityKey.counter,
|
||||||
publicKey: securityKey.public_key,
|
userHandle: securityKey.key_id,
|
||||||
prevCounter: securityKey.counter,
|
});
|
||||||
userHandle: securityKey.key_id,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const counter = authnResult.authnrData.get("counter");
|
const counter = authnResult.authnrData.get("counter");
|
||||||
|
|
||||||
@ -116,7 +96,7 @@ router.post(
|
|||||||
token: await generateToken(user.id),
|
token: await generateToken(user.id),
|
||||||
user_settings: user.settings,
|
user_settings: user.settings,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -16,13 +16,7 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { IPAnalysis, getIpAdress, isProxy, route, verifyCaptcha } from "@spacebar/api";
|
||||||
IPAnalysis,
|
|
||||||
getIpAdress,
|
|
||||||
isProxy,
|
|
||||||
route,
|
|
||||||
verifyCaptcha,
|
|
||||||
} from "@spacebar/api";
|
|
||||||
import {
|
import {
|
||||||
Config,
|
Config,
|
||||||
FieldErrors,
|
FieldErrors,
|
||||||
@ -65,13 +59,9 @@ router.post(
|
|||||||
});
|
});
|
||||||
await regToken.remove();
|
await regToken.remove();
|
||||||
regTokenUsed = true;
|
regTokenUsed = true;
|
||||||
console.log(
|
console.log(`[REGISTER] Registration token ${token} used for registration!`);
|
||||||
`[REGISTER] Registration token ${token} used for registration!`,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
console.log(`[REGISTER] Invalid registration token ${token} used for registration by ${ip}!`);
|
||||||
`[REGISTER] Invalid registration token ${token} used for registration by ${ip}!`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,11 +94,7 @@ router.post(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!regTokenUsed && register.requireCaptcha && security.captcha.enabled) {
|
||||||
!regTokenUsed &&
|
|
||||||
register.requireCaptcha &&
|
|
||||||
security.captcha.enabled
|
|
||||||
) {
|
|
||||||
const { sitekey, service } = security.captcha;
|
const { sitekey, service } = security.captcha;
|
||||||
if (!body.captcha_key) {
|
if (!body.captcha_key) {
|
||||||
return res?.status(400).json({
|
return res?.status(400).json({
|
||||||
@ -139,9 +125,7 @@ router.post(
|
|||||||
throw FieldErrors({
|
throw FieldErrors({
|
||||||
email: {
|
email: {
|
||||||
code: "EMAIL_ALREADY_REGISTERED",
|
code: "EMAIL_ALREADY_REGISTERED",
|
||||||
message: req.t(
|
message: req.t("auth:register.EMAIL_ALREADY_REGISTERED"),
|
||||||
"auth:register.EMAIL_ALREADY_REGISTERED",
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -176,9 +160,7 @@ router.post(
|
|||||||
throw FieldErrors({
|
throw FieldErrors({
|
||||||
email: {
|
email: {
|
||||||
code: "EMAIL_ALREADY_REGISTERED",
|
code: "EMAIL_ALREADY_REGISTERED",
|
||||||
message: req.t(
|
message: req.t("auth:register.EMAIL_ALREADY_REGISTERED"),
|
||||||
"auth:register.EMAIL_ALREADY_REGISTERED",
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -198,14 +180,9 @@ router.post(
|
|||||||
message: req.t("common:field.BASE_TYPE_REQUIRED"),
|
message: req.t("common:field.BASE_TYPE_REQUIRED"),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (register.dateOfBirth.required && register.dateOfBirth.minimum) {
|
||||||
register.dateOfBirth.required &&
|
|
||||||
register.dateOfBirth.minimum
|
|
||||||
) {
|
|
||||||
const minimum = new Date();
|
const minimum = new Date();
|
||||||
minimum.setFullYear(
|
minimum.setFullYear(minimum.getFullYear() - register.dateOfBirth.minimum);
|
||||||
minimum.getFullYear() - register.dateOfBirth.minimum,
|
|
||||||
);
|
|
||||||
body.date_of_birth = new Date(body.date_of_birth as Date);
|
body.date_of_birth = new Date(body.date_of_birth as Date);
|
||||||
|
|
||||||
// higher is younger
|
// higher is younger
|
||||||
@ -228,10 +205,7 @@ router.post(
|
|||||||
throw FieldErrors({
|
throw FieldErrors({
|
||||||
password: {
|
password: {
|
||||||
code: "PASSWORD_REQUIREMENTS_MIN_LENGTH",
|
code: "PASSWORD_REQUIREMENTS_MIN_LENGTH",
|
||||||
message: req.t(
|
message: req.t("auth:register.PASSWORD_REQUIREMENTS_MIN_LENGTH", { min: min }),
|
||||||
"auth:register.PASSWORD_REQUIREMENTS_MIN_LENGTH",
|
|
||||||
{ min: min },
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -249,8 +223,7 @@ router.post(
|
|||||||
if (
|
if (
|
||||||
!regTokenUsed &&
|
!regTokenUsed &&
|
||||||
!body.invite &&
|
!body.invite &&
|
||||||
(register.requireInvite ||
|
(register.requireInvite || (register.guestsRequireInvite && !register.email))
|
||||||
(register.guestsRequireInvite && !register.email))
|
|
||||||
) {
|
) {
|
||||||
// require invite to register -> e.g. for organizations to send invites to their employees
|
// require invite to register -> e.g. for organizations to send invites to their employees
|
||||||
throw FieldErrors({
|
throw FieldErrors({
|
||||||
@ -266,18 +239,14 @@ router.post(
|
|||||||
limits.absoluteRate.register.enabled &&
|
limits.absoluteRate.register.enabled &&
|
||||||
(await User.count({
|
(await User.count({
|
||||||
where: {
|
where: {
|
||||||
created_at: MoreThan(
|
created_at: MoreThan(new Date(Date.now() - limits.absoluteRate.register.window)),
|
||||||
new Date(
|
|
||||||
Date.now() - limits.absoluteRate.register.window,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
})) >= limits.absoluteRate.register.limit
|
})) >= limits.absoluteRate.register.limit
|
||||||
) {
|
) {
|
||||||
console.log(
|
console.log(
|
||||||
`Global register ratelimit exceeded for ${getIpAdress(req)}, ${
|
`Global register ratelimit exceeded for ${getIpAdress(req)}, ${req.body.username}, ${
|
||||||
req.body.username
|
req.body.invite || "No invite given"
|
||||||
}, ${req.body.invite || "No invite given"}`,
|
}`
|
||||||
);
|
);
|
||||||
throw FieldErrors({
|
throw FieldErrors({
|
||||||
email: {
|
email: {
|
||||||
@ -295,7 +264,7 @@ router.post(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return res.json({ token: await generateToken(user.id) });
|
return res.json({ token: await generateToken(user.id) });
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,14 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { checkToken, Email, FieldErrors, generateToken, PasswordResetSchema, User } from "@spacebar/util";
|
||||||
checkToken,
|
|
||||||
Email,
|
|
||||||
FieldErrors,
|
|
||||||
generateToken,
|
|
||||||
PasswordResetSchema,
|
|
||||||
User,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
|
|
||||||
@ -76,7 +69,7 @@ router.post(
|
|||||||
await Email.sendPasswordChanged(user, user.email!);
|
await Email.sendPasswordChanged(user, user.email!);
|
||||||
|
|
||||||
res.json({ token: await generateToken(user.id) });
|
res.json({ token: await generateToken(user.id) });
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,13 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { getIpAdress, route, verifyCaptcha } from "@spacebar/api";
|
import { getIpAdress, route, verifyCaptcha } from "@spacebar/api";
|
||||||
import {
|
import { checkToken, Config, FieldErrors, generateToken, User } from "@spacebar/util";
|
||||||
checkToken,
|
|
||||||
Config,
|
|
||||||
FieldErrors,
|
|
||||||
generateToken,
|
|
||||||
User,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@ -97,7 +91,7 @@ router.post(
|
|||||||
await User.update({ id: user.id }, { verified: true });
|
await User.update({ id: user.id }, { verified: true });
|
||||||
|
|
||||||
return res.json(await getToken(user));
|
return res.json(await getToken(user));
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -52,12 +52,10 @@ router.post(
|
|||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.error(
|
console.error(`Failed to send verification email to ${user.username}#${user.discriminator}: ${e}`);
|
||||||
`Failed to send verification email to ${user.username}#${user.discriminator}: ${e}`,
|
|
||||||
);
|
|
||||||
throw new HTTPError("Failed to send verification email", 500);
|
throw new HTTPError("Failed to send verification email", 500);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -52,7 +52,7 @@ router.post(
|
|||||||
nonce: "NoncePlaceholder",
|
nonce: "NoncePlaceholder",
|
||||||
regenerate_nonce: "RegenNoncePlaceholder",
|
regenerate_nonce: "RegenNoncePlaceholder",
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -52,7 +52,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return res.send(channel);
|
return res.send(channel);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
@ -101,7 +101,7 @@ router.delete(
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.send(channel);
|
res.send(channel);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -122,11 +122,7 @@ router.patch(
|
|||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const payload = req.body as ChannelModifySchema;
|
const payload = req.body as ChannelModifySchema;
|
||||||
const { channel_id } = req.params;
|
const { channel_id } = req.params;
|
||||||
if (payload.icon)
|
if (payload.icon) payload.icon = await handleFile(`/channel-icons/${channel_id}`, payload.icon);
|
||||||
payload.icon = await handleFile(
|
|
||||||
`/channel-icons/${channel_id}`,
|
|
||||||
payload.icon,
|
|
||||||
);
|
|
||||||
|
|
||||||
const channel = await Channel.findOneOrFail({
|
const channel = await Channel.findOneOrFail({
|
||||||
where: { id: channel_id },
|
where: { id: channel_id },
|
||||||
@ -143,7 +139,7 @@ router.patch(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
res.send(channel);
|
res.send(channel);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -65,9 +65,7 @@ router.post(
|
|||||||
const { guild_id } = channel;
|
const { guild_id } = channel;
|
||||||
|
|
||||||
const expires_at =
|
const expires_at =
|
||||||
body.max_age == 0 || body.max_age == undefined
|
body.max_age == 0 || body.max_age == undefined ? undefined : new Date(body.max_age * 1000 + Date.now());
|
||||||
? undefined
|
|
||||||
: new Date(body.max_age * 1000 + Date.now());
|
|
||||||
|
|
||||||
const invite = await Invite.create({
|
const invite = await Invite.create({
|
||||||
code: random(),
|
code: random(),
|
||||||
@ -95,7 +93,7 @@ router.post(
|
|||||||
} as InviteCreateEvent);
|
} as InviteCreateEvent);
|
||||||
|
|
||||||
res.status(201).send(data);
|
res.status(201).send(data);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@ -126,7 +124,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).send(invites);
|
res.status(200).send(invites);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,12 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { emitEvent, getPermission, MessageAckEvent, ReadState } from "@spacebar/util";
|
||||||
emitEvent,
|
|
||||||
getPermission,
|
|
||||||
MessageAckEvent,
|
|
||||||
ReadState,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
@ -43,18 +38,13 @@ router.post(
|
|||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { channel_id, message_id } = req.params;
|
const { channel_id, message_id } = req.params;
|
||||||
|
|
||||||
const permission = await getPermission(
|
const permission = await getPermission(req.user_id, undefined, channel_id);
|
||||||
req.user_id,
|
|
||||||
undefined,
|
|
||||||
channel_id,
|
|
||||||
);
|
|
||||||
permission.hasThrow("VIEW_CHANNEL");
|
permission.hasThrow("VIEW_CHANNEL");
|
||||||
|
|
||||||
let read_state = await ReadState.findOne({
|
let read_state = await ReadState.findOne({
|
||||||
where: { user_id: req.user_id, channel_id },
|
where: { user_id: req.user_id, channel_id },
|
||||||
});
|
});
|
||||||
if (!read_state)
|
if (!read_state) read_state = ReadState.create({ user_id: req.user_id, channel_id });
|
||||||
read_state = ReadState.create({ user_id: req.user_id, channel_id });
|
|
||||||
read_state.last_message_id = message_id;
|
read_state.last_message_id = message_id;
|
||||||
|
|
||||||
await read_state.save();
|
await read_state.save();
|
||||||
@ -70,7 +60,7 @@ router.post(
|
|||||||
} as MessageAckEvent);
|
} as MessageAckEvent);
|
||||||
|
|
||||||
res.json({ token: null });
|
res.json({ token: null });
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -57,7 +57,7 @@ router.post(
|
|||||||
flags: 1,
|
flags: 1,
|
||||||
components: [],
|
components: [],
|
||||||
}).status(200);
|
}).status(200);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -75,11 +75,7 @@ router.patch(
|
|||||||
relations: ["attachments"],
|
relations: ["attachments"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const permissions = await getPermission(
|
const permissions = await getPermission(req.user_id, undefined, channel_id);
|
||||||
req.user_id,
|
|
||||||
undefined,
|
|
||||||
channel_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
const rights = await getRights(req.user_id);
|
const rights = await getRights(req.user_id);
|
||||||
|
|
||||||
@ -139,7 +135,7 @@ router.patch(
|
|||||||
// these are not in the Discord.com response
|
// these are not in the Discord.com response
|
||||||
mention_channels: new_message.mention_channels,
|
mention_channels: new_message.mention_channels,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Backfill message with specific timestamp
|
// Backfill message with specific timestamp
|
||||||
@ -196,13 +192,8 @@ router.put(
|
|||||||
|
|
||||||
if (req.file) {
|
if (req.file) {
|
||||||
try {
|
try {
|
||||||
const file = await uploadFile(
|
const file = await uploadFile(`/attachments/${req.params.channel_id}`, req.file);
|
||||||
`/attachments/${req.params.channel_id}`,
|
attachments.push(Attachment.create({ ...file, proxy_url: file.url }));
|
||||||
req.file,
|
|
||||||
);
|
|
||||||
attachments.push(
|
|
||||||
Attachment.create({ ...file, proxy_url: file.url }),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.status(400).json(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
|
// no await as it shouldnt block the message send function and silently catch error
|
||||||
postHandleMessage(message).catch((e) =>
|
postHandleMessage(message).catch((e) => console.error("[Message] post-message handler failed", e));
|
||||||
console.error("[Message] post-message handler failed", e),
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.json(message);
|
return res.json(message);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@ -272,17 +261,12 @@ router.get(
|
|||||||
relations: ["attachments"],
|
relations: ["attachments"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const permissions = await getPermission(
|
const permissions = await getPermission(req.user_id, undefined, channel_id);
|
||||||
req.user_id,
|
|
||||||
undefined,
|
|
||||||
channel_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (message.author_id !== req.user_id)
|
if (message.author_id !== req.user_id) permissions.hasThrow("READ_MESSAGE_HISTORY");
|
||||||
permissions.hasThrow("READ_MESSAGE_HISTORY");
|
|
||||||
|
|
||||||
return res.json(message);
|
return res.json(message);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
@ -310,11 +294,7 @@ router.delete(
|
|||||||
|
|
||||||
if (message.author_id !== req.user_id) {
|
if (message.author_id !== req.user_id) {
|
||||||
if (!rights.has("MANAGE_MESSAGES")) {
|
if (!rights.has("MANAGE_MESSAGES")) {
|
||||||
const permission = await getPermission(
|
const permission = await getPermission(req.user_id, channel.guild_id, channel_id);
|
||||||
req.user_id,
|
|
||||||
channel.guild_id,
|
|
||||||
channel_id,
|
|
||||||
);
|
|
||||||
permission.hasThrow("MANAGE_MESSAGES");
|
permission.hasThrow("MANAGE_MESSAGES");
|
||||||
}
|
}
|
||||||
} else rights.hasThrow("SELF_DELETE_MESSAGES");
|
} else rights.hasThrow("SELF_DELETE_MESSAGES");
|
||||||
@ -332,7 +312,7 @@ router.delete(
|
|||||||
} as MessageDeleteEvent);
|
} as MessageDeleteEvent);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -88,7 +88,7 @@ router.delete(
|
|||||||
} as MessageReactionRemoveAllEvent);
|
} as MessageReactionRemoveAllEvent);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
@ -113,9 +113,7 @@ router.delete(
|
|||||||
});
|
});
|
||||||
|
|
||||||
const already_added = message.reactions.find(
|
const already_added = message.reactions.find(
|
||||||
(x) =>
|
(x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name
|
||||||
(x.emoji.id === emoji.id && emoji.id) ||
|
|
||||||
x.emoji.name === emoji.name,
|
|
||||||
);
|
);
|
||||||
if (!already_added) throw new HTTPError("Reaction not found", 404);
|
if (!already_added) throw new HTTPError("Reaction not found", 404);
|
||||||
message.reactions.remove(already_added);
|
message.reactions.remove(already_added);
|
||||||
@ -135,7 +133,7 @@ router.delete(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@ -161,9 +159,7 @@ router.get(
|
|||||||
where: { id: message_id, channel_id },
|
where: { id: message_id, channel_id },
|
||||||
});
|
});
|
||||||
const reaction = message.reactions.find(
|
const reaction = message.reactions.find(
|
||||||
(x) =>
|
(x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name
|
||||||
(x.emoji.id === emoji.id && emoji.id) ||
|
|
||||||
x.emoji.name === emoji.name,
|
|
||||||
);
|
);
|
||||||
if (!reaction) throw new HTTPError("Reaction not found", 404);
|
if (!reaction) throw new HTTPError("Reaction not found", 404);
|
||||||
|
|
||||||
@ -175,7 +171,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
res.json(users);
|
res.json(users);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
@ -204,9 +200,7 @@ router.put(
|
|||||||
where: { id: message_id, channel_id },
|
where: { id: message_id, channel_id },
|
||||||
});
|
});
|
||||||
const already_added = message.reactions.find(
|
const already_added = message.reactions.find(
|
||||||
(x) =>
|
(x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name
|
||||||
(x.emoji.id === emoji.id && emoji.id) ||
|
|
||||||
x.emoji.name === emoji.name,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!already_added) req.permission?.hasThrow("ADD_REACTIONS");
|
if (!already_added) req.permission?.hasThrow("ADD_REACTIONS");
|
||||||
@ -222,8 +216,7 @@ router.put(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (already_added) {
|
if (already_added) {
|
||||||
if (already_added.user_ids.includes(req.user_id))
|
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
|
||||||
return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error
|
|
||||||
already_added.count++;
|
already_added.count++;
|
||||||
already_added.user_ids.push(req.user_id);
|
already_added.user_ids.push(req.user_id);
|
||||||
} else
|
} else
|
||||||
@ -258,7 +251,7 @@ router.put(
|
|||||||
} as MessageReactionAddEvent);
|
} as MessageReactionAddEvent);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
@ -288,30 +281,19 @@ router.delete(
|
|||||||
|
|
||||||
if (user_id === "@me") user_id = req.user_id;
|
if (user_id === "@me") user_id = req.user_id;
|
||||||
else {
|
else {
|
||||||
const permissions = await getPermission(
|
const permissions = await getPermission(req.user_id, undefined, channel_id);
|
||||||
req.user_id,
|
|
||||||
undefined,
|
|
||||||
channel_id,
|
|
||||||
);
|
|
||||||
permissions.hasThrow("MANAGE_MESSAGES");
|
permissions.hasThrow("MANAGE_MESSAGES");
|
||||||
}
|
}
|
||||||
|
|
||||||
const already_added = message.reactions.find(
|
const already_added = message.reactions.find(
|
||||||
(x) =>
|
(x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name
|
||||||
(x.emoji.id === emoji.id && emoji.id) ||
|
|
||||||
x.emoji.name === emoji.name,
|
|
||||||
);
|
);
|
||||||
if (!already_added || !already_added.user_ids.includes(user_id))
|
if (!already_added || !already_added.user_ids.includes(user_id)) throw new HTTPError("Reaction not found", 404);
|
||||||
throw new HTTPError("Reaction not found", 404);
|
|
||||||
|
|
||||||
already_added.count--;
|
already_added.count--;
|
||||||
|
|
||||||
if (already_added.count <= 0) message.reactions.remove(already_added);
|
if (already_added.count <= 0) message.reactions.remove(already_added);
|
||||||
else
|
else already_added.user_ids.splice(already_added.user_ids.indexOf(user_id), 1);
|
||||||
already_added.user_ids.splice(
|
|
||||||
already_added.user_ids.indexOf(user_id),
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
|
|
||||||
await message.save();
|
await message.save();
|
||||||
|
|
||||||
@ -328,7 +310,7 @@ router.delete(
|
|||||||
} as MessageReactionRemoveEvent);
|
} as MessageReactionRemoveEvent);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,15 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { Channel, Config, emitEvent, getPermission, getRights, Message, MessageDeleteBulkEvent } from "@spacebar/util";
|
||||||
Channel,
|
|
||||||
Config,
|
|
||||||
emitEvent,
|
|
||||||
getPermission,
|
|
||||||
getRights,
|
|
||||||
Message,
|
|
||||||
MessageDeleteBulkEvent,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
|
|
||||||
@ -54,30 +46,22 @@ router.post(
|
|||||||
const channel = await Channel.findOneOrFail({
|
const channel = await Channel.findOneOrFail({
|
||||||
where: { id: channel_id },
|
where: { id: channel_id },
|
||||||
});
|
});
|
||||||
if (!channel.guild_id)
|
if (!channel.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400);
|
||||||
throw new HTTPError("Can't bulk delete dm channel messages", 400);
|
|
||||||
|
|
||||||
const rights = await getRights(req.user_id);
|
const rights = await getRights(req.user_id);
|
||||||
rights.hasThrow("SELF_DELETE_MESSAGES");
|
rights.hasThrow("SELF_DELETE_MESSAGES");
|
||||||
|
|
||||||
const superuser = rights.has("MANAGE_MESSAGES");
|
const superuser = rights.has("MANAGE_MESSAGES");
|
||||||
const permission = await getPermission(
|
const permission = await getPermission(req.user_id, channel?.guild_id, channel_id);
|
||||||
req.user_id,
|
|
||||||
channel?.guild_id,
|
|
||||||
channel_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { maxBulkDelete } = Config.get().limits.message;
|
const { maxBulkDelete } = Config.get().limits.message;
|
||||||
|
|
||||||
const { messages } = req.body as { messages: string[] };
|
const { messages } = req.body as { messages: string[] };
|
||||||
if (messages.length === 0)
|
if (messages.length === 0) throw new HTTPError("You must specify messages to bulk delete");
|
||||||
throw new HTTPError("You must specify messages to bulk delete");
|
|
||||||
if (!superuser) {
|
if (!superuser) {
|
||||||
permission.hasThrow("MANAGE_MESSAGES");
|
permission.hasThrow("MANAGE_MESSAGES");
|
||||||
if (messages.length > maxBulkDelete)
|
if (messages.length > maxBulkDelete)
|
||||||
throw new HTTPError(
|
throw new HTTPError(`You cannot delete more than ${maxBulkDelete} messages`);
|
||||||
`You cannot delete more than ${maxBulkDelete} messages`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Message.delete(messages);
|
await Message.delete(messages);
|
||||||
@ -89,5 +73,5 @@ router.post(
|
|||||||
} as MessageDeleteBulkEvent);
|
} as MessageDeleteBulkEvent);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
@ -40,13 +40,7 @@ import {
|
|||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import multer from "multer";
|
import multer from "multer";
|
||||||
import {
|
import { FindManyOptions, FindOperator, LessThan, MoreThan, MoreThanOrEqual } from "typeorm";
|
||||||
FindManyOptions,
|
|
||||||
FindOperator,
|
|
||||||
LessThan,
|
|
||||||
MoreThan,
|
|
||||||
MoreThanOrEqual,
|
|
||||||
} from "typeorm";
|
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
@ -68,8 +62,7 @@ router.get(
|
|||||||
},
|
},
|
||||||
limit: {
|
limit: {
|
||||||
type: "number",
|
type: "number",
|
||||||
description:
|
description: "max number of messages to return (1-100). defaults to 50",
|
||||||
"max number of messages to return (1-100). defaults to 50",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
responses: {
|
responses: {
|
||||||
@ -95,14 +88,9 @@ router.get(
|
|||||||
const before = req.query.before ? `${req.query.before}` : undefined;
|
const before = req.query.before ? `${req.query.before}` : undefined;
|
||||||
const after = req.query.after ? `${req.query.after}` : undefined;
|
const after = req.query.after ? `${req.query.after}` : undefined;
|
||||||
const limit = Number(req.query.limit) || 50;
|
const limit = Number(req.query.limit) || 50;
|
||||||
if (limit < 1 || limit > 100)
|
if (limit < 1 || limit > 100) throw new HTTPError("limit must be between 1 and 100", 422);
|
||||||
throw new HTTPError("limit must be between 1 and 100", 422);
|
|
||||||
|
|
||||||
const permissions = await getPermission(
|
const permissions = await getPermission(req.user_id, channel.guild_id, channel_id);
|
||||||
req.user_id,
|
|
||||||
channel.guild_id,
|
|
||||||
channel_id,
|
|
||||||
);
|
|
||||||
permissions.hasThrow("VIEW_CHANNEL");
|
permissions.hasThrow("VIEW_CHANNEL");
|
||||||
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
|
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
|
||||||
|
|
||||||
@ -148,12 +136,10 @@ router.get(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (after) {
|
if (after) {
|
||||||
if (BigInt(after) > BigInt(Snowflake.generate()))
|
if (BigInt(after) > BigInt(Snowflake.generate())) return res.status(422);
|
||||||
return res.status(422);
|
|
||||||
query.where.id = MoreThan(after);
|
query.where.id = MoreThan(after);
|
||||||
} else if (before) {
|
} else if (before) {
|
||||||
if (BigInt(before) > BigInt(Snowflake.generate()))
|
if (BigInt(before) > BigInt(Snowflake.generate())) return res.status(422);
|
||||||
return res.status(422);
|
|
||||||
query.where.id = LessThan(before);
|
query.where.id = LessThan(before);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,12 +166,8 @@ router.get(
|
|||||||
});
|
});
|
||||||
x.attachments?.forEach((y: Attachment) => {
|
x.attachments?.forEach((y: Attachment) => {
|
||||||
// dynamically set attachment proxy_url in case the endpoint changed
|
// dynamically set attachment proxy_url in case the endpoint changed
|
||||||
const uri = y.proxy_url.startsWith("http")
|
const uri = y.proxy_url.startsWith("http") ? y.proxy_url : `https://example.org${y.proxy_url}`;
|
||||||
? y.proxy_url
|
y.proxy_url = `${endpoint == null ? "" : endpoint}${new URL(uri).pathname}`;
|
||||||
: `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);
|
return res.json(ret);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: config max upload size
|
// TODO: config max upload size
|
||||||
@ -258,10 +240,7 @@ router.post(
|
|||||||
relations: ["recipients", "recipients.user"],
|
relations: ["recipients", "recipients.user"],
|
||||||
});
|
});
|
||||||
if (!channel.isWritable()) {
|
if (!channel.isWritable()) {
|
||||||
throw new HTTPError(
|
throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400);
|
||||||
`Cannot send messages to channel of type ${channel.type}`,
|
|
||||||
400,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body.nonce) {
|
if (body.nonce) {
|
||||||
@ -283,12 +262,7 @@ router.post(
|
|||||||
const count = await Message.count({
|
const count = await Message.count({
|
||||||
where: {
|
where: {
|
||||||
channel_id,
|
channel_id,
|
||||||
timestamp: MoreThan(
|
timestamp: MoreThan(new Date(Date.now() - limits.absoluteRate.sendMessage.window)),
|
||||||
new Date(
|
|
||||||
Date.now() -
|
|
||||||
limits.absoluteRate.sendMessage.window,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -305,13 +279,8 @@ router.post(
|
|||||||
const files = (req.files as Express.Multer.File[]) ?? [];
|
const files = (req.files as Express.Multer.File[]) ?? [];
|
||||||
for (const currFile of files) {
|
for (const currFile of files) {
|
||||||
try {
|
try {
|
||||||
const file = await uploadFile(
|
const file = await uploadFile(`/attachments/${channel.id}`, currFile);
|
||||||
`/attachments/${channel.id}`,
|
attachments.push(Attachment.create({ ...file, proxy_url: file.url }));
|
||||||
currFile,
|
|
||||||
);
|
|
||||||
attachments.push(
|
|
||||||
Attachment.create({ ...file, proxy_url: file.url }),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.status(400).json({ message: error?.toString() });
|
return res.status(400).json({ message: error?.toString() });
|
||||||
}
|
}
|
||||||
@ -347,14 +316,12 @@ router.post(
|
|||||||
recipient.save(),
|
recipient.save(),
|
||||||
emitEvent({
|
emitEvent({
|
||||||
event: "CHANNEL_CREATE",
|
event: "CHANNEL_CREATE",
|
||||||
data: channel_dto.excludedRecipients([
|
data: channel_dto.excludedRecipients([recipient.user_id]),
|
||||||
recipient.user_id,
|
|
||||||
]),
|
|
||||||
user_id: recipient.user_id,
|
user_id: recipient.user_id,
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}) || [],
|
}) || []
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,16 +337,13 @@ router.post(
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
message.member.roles = message.member.roles
|
message.member.roles = message.member.roles.filter((x) => x.id != x.guild_id).map((x) => x.id);
|
||||||
.filter((x) => x.id != x.guild_id)
|
|
||||||
.map((x) => x.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let read_state = await ReadState.findOne({
|
let read_state = await ReadState.findOne({
|
||||||
where: { user_id: req.user_id, channel_id },
|
where: { user_id: req.user_id, channel_id },
|
||||||
});
|
});
|
||||||
if (!read_state)
|
if (!read_state) read_state = ReadState.create({ user_id: req.user_id, channel_id });
|
||||||
read_state = ReadState.create({ user_id: req.user_id, channel_id });
|
|
||||||
read_state.last_message_id = message.id;
|
read_state.last_message_id = message.id;
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -391,21 +355,16 @@ router.post(
|
|||||||
data: message,
|
data: message,
|
||||||
} as MessageCreateEvent),
|
} as MessageCreateEvent),
|
||||||
message.guild_id
|
message.guild_id
|
||||||
? Member.update(
|
? Member.update({ id: req.user_id, guild_id: message.guild_id }, { last_message_id: message.id })
|
||||||
{ id: req.user_id, guild_id: message.guild_id },
|
|
||||||
{ last_message_id: message.id },
|
|
||||||
)
|
|
||||||
: null,
|
: null,
|
||||||
channel.save(),
|
channel.save(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 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) =>
|
postHandleMessage(message).catch((e) => console.error("[Message] post-message handler failed", e));
|
||||||
console.error("[Message] post-message handler failed", e),
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.json(message);
|
return res.json(message);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -55,15 +55,14 @@ router.put(
|
|||||||
if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
|
if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
|
||||||
|
|
||||||
if (body.type === 0) {
|
if (body.type === 0) {
|
||||||
if (!(await Role.count({ where: { id: overwrite_id } })))
|
if (!(await Role.count({ where: { id: overwrite_id } }))) throw new HTTPError("role not found", 404);
|
||||||
throw new HTTPError("role not found", 404);
|
|
||||||
} else if (body.type === 1) {
|
} else if (body.type === 1) {
|
||||||
if (!(await Member.count({ where: { id: overwrite_id } })))
|
if (!(await Member.count({ where: { id: overwrite_id } }))) throw new HTTPError("user not found", 404);
|
||||||
throw new HTTPError("user not found", 404);
|
|
||||||
} else throw new HTTPError("type not supported", 501);
|
} else throw new HTTPError("type not supported", 501);
|
||||||
|
|
||||||
let overwrite: ChannelPermissionOverwrite | undefined =
|
let overwrite: ChannelPermissionOverwrite | undefined = channel.permission_overwrites?.find(
|
||||||
channel.permission_overwrites?.find((x) => x.id === overwrite_id);
|
(x) => x.id === overwrite_id
|
||||||
|
);
|
||||||
if (!overwrite) {
|
if (!overwrite) {
|
||||||
overwrite = {
|
overwrite = {
|
||||||
id: overwrite_id,
|
id: overwrite_id,
|
||||||
@ -73,14 +72,8 @@ router.put(
|
|||||||
};
|
};
|
||||||
channel.permission_overwrites?.push(overwrite);
|
channel.permission_overwrites?.push(overwrite);
|
||||||
}
|
}
|
||||||
overwrite.allow = String(
|
overwrite.allow = String((req.permission?.bitfield || 0n) & (BigInt(body.allow) || BigInt("0")));
|
||||||
(req.permission?.bitfield || 0n) &
|
overwrite.deny = String((req.permission?.bitfield || 0n) & (BigInt(body.deny) || BigInt("0")));
|
||||||
(BigInt(body.allow) || BigInt("0")),
|
|
||||||
);
|
|
||||||
overwrite.deny = String(
|
|
||||||
(req.permission?.bitfield || 0n) &
|
|
||||||
(BigInt(body.deny) || BigInt("0")),
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
channel.save(),
|
channel.save(),
|
||||||
@ -92,7 +85,7 @@ router.put(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: check permission hierarchy
|
// TODO: check permission hierarchy
|
||||||
@ -107,9 +100,7 @@ router.delete(
|
|||||||
});
|
});
|
||||||
if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
|
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);
|
||||||
(x) => x.id === overwrite_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
channel.save(),
|
channel.save(),
|
||||||
@ -121,7 +112,7 @@ router.delete(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -57,8 +57,7 @@ router.put(
|
|||||||
where: { channel: { id: channel_id }, pinned: true },
|
where: { channel: { id: channel_id }, pinned: true },
|
||||||
});
|
});
|
||||||
const { maxPins } = Config.get().limits.channel;
|
const { maxPins } = Config.get().limits.channel;
|
||||||
if (pinned_count >= maxPins)
|
if (pinned_count >= maxPins) throw DiscordApiErrors.MAXIMUM_PINS.withParams(maxPins);
|
||||||
throw DiscordApiErrors.MAXIMUM_PINS.withParams(maxPins);
|
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
Message.update({ id: message_id }, { pinned: true }),
|
Message.update({ id: message_id }, { pinned: true }),
|
||||||
@ -79,7 +78,7 @@ router.put(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
@ -129,7 +128,7 @@ router.delete(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@ -153,7 +152,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
res.send(pins);
|
res.send(pins);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -57,17 +57,12 @@ router.post(
|
|||||||
where: { id: channel_id },
|
where: { id: channel_id },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!channel.guild_id)
|
if (!channel.guild_id) throw new HTTPError("Can't purge dm channels", 400);
|
||||||
throw new HTTPError("Can't purge dm channels", 400);
|
|
||||||
isTextChannel(channel.type);
|
isTextChannel(channel.type);
|
||||||
|
|
||||||
const rights = await getRights(req.user_id);
|
const rights = await getRights(req.user_id);
|
||||||
if (!rights.has("MANAGE_MESSAGES")) {
|
if (!rights.has("MANAGE_MESSAGES")) {
|
||||||
const permissions = await getPermission(
|
const permissions = await getPermission(req.user_id, channel.guild_id, channel_id);
|
||||||
req.user_id,
|
|
||||||
channel.guild_id,
|
|
||||||
channel_id,
|
|
||||||
);
|
|
||||||
permissions.hasThrow("MANAGE_MESSAGES");
|
permissions.hasThrow("MANAGE_MESSAGES");
|
||||||
permissions.hasThrow("MANAGE_CHANNELS");
|
permissions.hasThrow("MANAGE_CHANNELS");
|
||||||
}
|
}
|
||||||
@ -84,9 +79,7 @@ router.post(
|
|||||||
where: {
|
where: {
|
||||||
channel_id,
|
channel_id,
|
||||||
id: Between(after, before), // the right way around
|
id: Between(after, before), // the right way around
|
||||||
author_id: rights.has("SELF_DELETE_MESSAGES")
|
author_id: rights.has("SELF_DELETE_MESSAGES") ? undefined : Not(req.user_id),
|
||||||
? undefined
|
|
||||||
: Not(req.user_id),
|
|
||||||
// if you lack the right of self-deletion, you can't delete your own messages, even in purges
|
// if you lack the right of self-deletion, you can't delete your own messages, even in purges
|
||||||
},
|
},
|
||||||
relations: [
|
relations: [
|
||||||
@ -121,5 +114,5 @@ router.post(
|
|||||||
} as MessageDeleteBulkEvent);
|
} as MessageDeleteBulkEvent);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
@ -48,24 +48,16 @@ router.put(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (channel.type !== ChannelType.GROUP_DM) {
|
if (channel.type !== ChannelType.GROUP_DM) {
|
||||||
const recipients = [
|
const recipients = [...(channel.recipients?.map((r) => r.user_id) || []), user_id].unique();
|
||||||
...(channel.recipients?.map((r) => r.user_id) || []),
|
|
||||||
user_id,
|
|
||||||
].unique();
|
|
||||||
|
|
||||||
const new_channel = await Channel.createDMChannel(
|
const new_channel = await Channel.createDMChannel(recipients, req.user_id);
|
||||||
recipients,
|
|
||||||
req.user_id,
|
|
||||||
);
|
|
||||||
return res.status(201).json(new_channel);
|
return res.status(201).json(new_channel);
|
||||||
} else {
|
} 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?
|
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 }));
|
||||||
Recipient.create({ channel_id: channel_id, user_id: user_id }),
|
|
||||||
);
|
|
||||||
await channel.save();
|
await channel.save();
|
||||||
|
|
||||||
await emitEvent({
|
await emitEvent({
|
||||||
@ -87,7 +79,7 @@ router.put(
|
|||||||
} as ChannelRecipientAddEvent);
|
} as ChannelRecipientAddEvent);
|
||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
@ -104,12 +96,7 @@ router.delete(
|
|||||||
where: { id: channel_id },
|
where: { id: channel_id },
|
||||||
relations: ["recipients"],
|
relations: ["recipients"],
|
||||||
});
|
});
|
||||||
if (
|
if (!(channel.type === ChannelType.GROUP_DM && (channel.owner_id === req.user_id || user_id === req.user_id)))
|
||||||
!(
|
|
||||||
channel.type === ChannelType.GROUP_DM &&
|
|
||||||
(channel.owner_id === req.user_id || user_id === req.user_id)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw DiscordApiErrors.MISSING_PERMISSIONS;
|
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)) {
|
||||||
@ -119,7 +106,7 @@ router.delete(
|
|||||||
await Channel.removeRecipientFromChannel(channel, user_id);
|
await Channel.removeRecipientFromChannel(channel, user_id);
|
||||||
|
|
||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -64,7 +64,7 @@ router.post(
|
|||||||
} as TypingStartEvent);
|
} as TypingStartEvent);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -47,7 +47,7 @@ router.get(
|
|||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
res.json([]);
|
res.json([]);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: use Image Data Type for avatar instead of String
|
// 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 webhook_count = await Webhook.count({ where: { channel_id } });
|
||||||
const { maxWebhooks } = Config.get().limits.channel;
|
const { maxWebhooks } = Config.get().limits.channel;
|
||||||
if (maxWebhooks && webhook_count > maxWebhooks)
|
if (maxWebhooks && webhook_count > maxWebhooks) throw DiscordApiErrors.MAXIMUM_WEBHOOKS.withParams(maxWebhooks);
|
||||||
throw DiscordApiErrors.MAXIMUM_WEBHOOKS.withParams(maxWebhooks);
|
|
||||||
|
|
||||||
let { avatar, name } = req.body as WebhookCreateSchema;
|
let { avatar, name } = req.body as WebhookCreateSchema;
|
||||||
name = trimSpecial(name);
|
name = trimSpecial(name);
|
||||||
@ -105,7 +104,7 @@ router.post(
|
|||||||
...hook,
|
...hook,
|
||||||
user: user,
|
user: user,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -30,9 +30,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
|||||||
provider_id: {
|
provider_id: {
|
||||||
code: "BASE_TYPE_CHOICES",
|
code: "BASE_TYPE_CHOICES",
|
||||||
message: req.t("common:field.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(", "),
|
||||||
", ",
|
|
||||||
),
|
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -17,55 +17,44 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { ConnectionCallbackSchema, ConnectionStore, emitEvent, FieldErrors } from "@spacebar/util";
|
||||||
ConnectionCallbackSchema,
|
|
||||||
ConnectionStore,
|
|
||||||
emitEvent,
|
|
||||||
FieldErrors,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post(
|
router.post("/", route({ requestBody: "ConnectionCallbackSchema" }), async (req: Request, res: Response) => {
|
||||||
"/",
|
const { connection_name } = req.params;
|
||||||
route({ requestBody: "ConnectionCallbackSchema" }),
|
const connection = ConnectionStore.connections.get(connection_name);
|
||||||
async (req: Request, res: Response) => {
|
if (!connection)
|
||||||
const { connection_name } = req.params;
|
throw FieldErrors({
|
||||||
const connection = ConnectionStore.connections.get(connection_name);
|
provider_id: {
|
||||||
if (!connection)
|
code: "BASE_TYPE_CHOICES",
|
||||||
throw FieldErrors({
|
message: req.t("common:field.BASE_TYPE_CHOICES", {
|
||||||
provider_id: {
|
types: Array.from(ConnectionStore.connections.keys()).join(", "),
|
||||||
code: "BASE_TYPE_CHOICES",
|
}),
|
||||||
message: req.t("common:field.BASE_TYPE_CHOICES", {
|
},
|
||||||
types: Array.from(
|
});
|
||||||
ConnectionStore.connections.keys(),
|
|
||||||
).join(", "),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!connection.settings.enabled)
|
if (!connection.settings.enabled)
|
||||||
throw FieldErrors({
|
throw FieldErrors({
|
||||||
provider_id: {
|
provider_id: {
|
||||||
message: "This connection has been disabled server-side.",
|
message: "This connection has been disabled server-side.",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const body = req.body as ConnectionCallbackSchema;
|
const body = req.body as ConnectionCallbackSchema;
|
||||||
const userId = connection.getUserId(body.state);
|
const userId = connection.getUserId(body.state);
|
||||||
const connectedAccnt = await connection.handleCallback(body);
|
const connectedAccnt = await connection.handleCallback(body);
|
||||||
|
|
||||||
// whether we should emit a connections update event, only used when a connection doesnt already exist
|
// whether we should emit a connections update event, only used when a connection doesnt already exist
|
||||||
if (connectedAccnt)
|
if (connectedAccnt)
|
||||||
emitEvent({
|
emitEvent({
|
||||||
event: "USER_CONNECTIONS_UPDATE",
|
event: "USER_CONNECTIONS_UPDATE",
|
||||||
data: { ...connectedAccnt, token_data: undefined },
|
data: { ...connectedAccnt, token_data: undefined },
|
||||||
user_id: userId,
|
user_id: userId,
|
||||||
});
|
});
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -70,7 +70,7 @@ router.get(
|
|||||||
offset: Number(offset || Config.get().guild.discovery.offset),
|
offset: Number(offset || Config.get().guild.discovery.offset),
|
||||||
limit: Number(limit || configLimit),
|
limit: Number(limit || configLimit),
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -38,12 +38,10 @@ router.get(
|
|||||||
// const { locale, primary_only } = req.query;
|
// const { locale, primary_only } = req.query;
|
||||||
const { primary_only } = req.query;
|
const { primary_only } = req.query;
|
||||||
|
|
||||||
const out = primary_only
|
const out = primary_only ? await Categories.find() : await Categories.find({ where: { is_primary: true } });
|
||||||
? await Categories.find()
|
|
||||||
: await Categories.find({ where: { is_primary: true } });
|
|
||||||
|
|
||||||
res.send(out);
|
res.send(out);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -52,7 +52,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
res.redirect(release.url);
|
res.redirect(release.url);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -43,7 +43,7 @@ router.get(
|
|||||||
max_concurrency: 1,
|
max_concurrency: 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -36,7 +36,7 @@ router.get(
|
|||||||
res.json({
|
res.json({
|
||||||
url: endpointPublic || process.env.GATEWAY || "ws://localhost:3001",
|
url: endpointPublic || process.env.GATEWAY || "ws://localhost:3001",
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -36,9 +36,7 @@ router.get(
|
|||||||
media_format: {
|
media_format: {
|
||||||
type: "string",
|
type: "string",
|
||||||
description: "Media format",
|
description: "Media format",
|
||||||
values: Object.keys(TenorMediaTypes).filter((key) =>
|
values: Object.keys(TenorMediaTypes).filter((key) => isNaN(Number(key))),
|
||||||
isNaN(Number(key)),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
locale: {
|
locale: {
|
||||||
type: "string",
|
type: "string",
|
||||||
@ -65,13 +63,13 @@ router.get(
|
|||||||
agent,
|
agent,
|
||||||
method: "get",
|
method: "get",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { results } = await response.json();
|
const { results } = await response.json();
|
||||||
|
|
||||||
res.json(results.map(parseGifResult)).status(200);
|
res.json(results.map(parseGifResult)).status(200);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -31,9 +31,7 @@ router.get(
|
|||||||
media_format: {
|
media_format: {
|
||||||
type: "string",
|
type: "string",
|
||||||
description: "Media format",
|
description: "Media format",
|
||||||
values: Object.keys(TenorMediaTypes).filter((key) =>
|
values: Object.keys(TenorMediaTypes).filter((key) => isNaN(Number(key))),
|
||||||
isNaN(Number(key)),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
locale: {
|
locale: {
|
||||||
type: "string",
|
type: "string",
|
||||||
@ -60,13 +58,13 @@ router.get(
|
|||||||
agent,
|
agent,
|
||||||
method: "get",
|
method: "get",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { results } = await response.json();
|
const { results } = await response.json();
|
||||||
|
|
||||||
res.json(results.map(parseGifResult)).status(200);
|
res.json(results.map(parseGifResult)).status(200);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,12 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { TenorCategoriesResults, TenorTrendingResults, getGifApiKey, parseGifResult } from "@spacebar/util";
|
||||||
TenorCategoriesResults,
|
|
||||||
TenorTrendingResults,
|
|
||||||
getGifApiKey,
|
|
||||||
parseGifResult,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import { ProxyAgent } from "proxy-agent";
|
import { ProxyAgent } from "proxy-agent";
|
||||||
@ -55,28 +50,20 @@ router.get(
|
|||||||
const agent = new ProxyAgent();
|
const agent = new ProxyAgent();
|
||||||
|
|
||||||
const [responseSource, trendGifSource] = await Promise.all([
|
const [responseSource, trendGifSource] = await Promise.all([
|
||||||
fetch(
|
fetch(`https://g.tenor.com/v1/categories?locale=${locale}&key=${apiKey}`, {
|
||||||
`https://g.tenor.com/v1/categories?locale=${locale}&key=${apiKey}`,
|
agent,
|
||||||
{
|
method: "get",
|
||||||
agent,
|
headers: { "Content-Type": "application/json" },
|
||||||
method: "get",
|
}),
|
||||||
headers: { "Content-Type": "application/json" },
|
fetch(`https://g.tenor.com/v1/trending?locale=${locale}&key=${apiKey}`, {
|
||||||
},
|
agent,
|
||||||
),
|
method: "get",
|
||||||
fetch(
|
headers: { "Content-Type": "application/json" },
|
||||||
`https://g.tenor.com/v1/trending?locale=${locale}&key=${apiKey}`,
|
}),
|
||||||
{
|
|
||||||
agent,
|
|
||||||
method: "get",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { tags } =
|
const { tags } = (await responseSource.json()) as TenorCategoriesResults;
|
||||||
(await responseSource.json()) as TenorCategoriesResults;
|
const { results } = (await trendGifSource.json()) as TenorTrendingResults;
|
||||||
const { results } =
|
|
||||||
(await trendGifSource.json()) as TenorTrendingResults;
|
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
categories: tags.map((x) => ({
|
categories: tags.map((x) => ({
|
||||||
@ -85,7 +72,7 @@ router.get(
|
|||||||
})),
|
})),
|
||||||
gifs: [parseGifResult(results[0])],
|
gifs: [parseGifResult(results[0])],
|
||||||
}).status(200);
|
}).status(200);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -39,9 +39,7 @@ router.get(
|
|||||||
const showAllGuilds = Config.get().guild.discovery.showAllGuilds;
|
const showAllGuilds = Config.get().guild.discovery.showAllGuilds;
|
||||||
|
|
||||||
const genLoadId = (size: number) =>
|
const genLoadId = (size: number) =>
|
||||||
[...Array(size)]
|
[...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join("");
|
||||||
.map(() => Math.floor(Math.random() * 16).toString(16))
|
|
||||||
.join("");
|
|
||||||
|
|
||||||
const guilds = showAllGuilds
|
const guilds = showAllGuilds
|
||||||
? await Guild.find({ take: Math.abs(Number(limit || 24)) })
|
? await Guild.find({ take: Math.abs(Number(limit || 24)) })
|
||||||
@ -53,7 +51,7 @@ router.get(
|
|||||||
recommended_guilds: guilds,
|
recommended_guilds: guilds,
|
||||||
load_id: `server_recs/${genLoadId(32)}`,
|
load_id: `server_recs/${genLoadId(32)}`,
|
||||||
}).status(200);
|
}).status(200);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -78,7 +78,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return res.json(bansObj);
|
return res.json(bansObj);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@ -115,7 +115,7 @@ router.get(
|
|||||||
delete ban.ip;
|
delete ban.ip;
|
||||||
|
|
||||||
return res.json(ban);
|
return res.json(ban);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
@ -139,14 +139,8 @@ router.put(
|
|||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const banned_user_id = req.params.user_id;
|
const banned_user_id = req.params.user_id;
|
||||||
|
|
||||||
if (
|
if (req.user_id === banned_user_id && banned_user_id === req.permission?.cache.guild?.owner_id)
|
||||||
req.user_id === banned_user_id &&
|
throw new HTTPError("You are the guild owner, hence can't ban yourself", 403);
|
||||||
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);
|
throw new HTTPError("You can't ban the owner", 400);
|
||||||
@ -175,7 +169,7 @@ router.put(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return res.json(ban);
|
return res.json(ban);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
@ -200,10 +194,7 @@ router.put(
|
|||||||
const banned_user = await User.getPublicUser(req.params.user_id);
|
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(
|
throw new HTTPError("You are the guild owner, hence can't ban yourself", 403);
|
||||||
"You are the guild owner, hence can't ban yourself",
|
|
||||||
403,
|
|
||||||
);
|
|
||||||
|
|
||||||
const ban = Ban.create({
|
const ban = Ban.create({
|
||||||
user_id: req.params.user_id,
|
user_id: req.params.user_id,
|
||||||
@ -227,7 +218,7 @@ router.put(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return res.json(ban);
|
return res.json(ban);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
@ -273,7 +264,7 @@ router.delete(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return res.status(204).send();
|
return res.status(204).send();
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -42,7 +42,7 @@ router.get(
|
|||||||
const channels = await Channel.find({ where: { guild_id } });
|
const channels = await Channel.find({ where: { guild_id } });
|
||||||
|
|
||||||
res.json(channels);
|
res.json(channels);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
@ -67,13 +67,10 @@ router.post(
|
|||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const body = req.body as ChannelModifySchema;
|
const body = req.body as ChannelModifySchema;
|
||||||
|
|
||||||
const channel = await Channel.createChannel(
|
const channel = await Channel.createChannel({ ...body, guild_id }, req.user_id);
|
||||||
{ ...body, guild_id },
|
|
||||||
req.user_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
res.status(201).json(channel);
|
res.status(201).json(channel);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -102,9 +99,7 @@ router.patch(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// The channels not listed for this query
|
// The channels not listed for this query
|
||||||
const notMentioned = guild.channel_ordering.filter(
|
const notMentioned = guild.channel_ordering.filter((x) => !body.find((c) => c.id == x));
|
||||||
(x) => !body.find((c) => c.id == x),
|
|
||||||
);
|
|
||||||
|
|
||||||
const withParents = body.filter((x) => x.parent_id != undefined);
|
const withParents = body.filter((x) => x.parent_id != undefined);
|
||||||
const withPositions = body.filter((x) => x.position != undefined);
|
const withPositions = body.filter((x) => x.position != undefined);
|
||||||
@ -124,7 +119,7 @@ router.patch(
|
|||||||
channel_id: channel.id,
|
channel_id: channel.id,
|
||||||
guild_id,
|
guild_id,
|
||||||
} as ChannelUpdateEvent);
|
} as ChannelUpdateEvent);
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// have to do the parents after the positions
|
// have to do the parents after the positions
|
||||||
@ -141,10 +136,7 @@ router.patch(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
if (opt.lock_permissions)
|
if (opt.lock_permissions)
|
||||||
await Channel.update(
|
await Channel.update({ id: channel.id }, { permission_overwrites: parent.permission_overwrites });
|
||||||
{ id: channel.id },
|
|
||||||
{ permission_overwrites: parent.permission_overwrites },
|
|
||||||
);
|
|
||||||
|
|
||||||
const parentPos = notMentioned.indexOf(parent.id);
|
const parentPos = notMentioned.indexOf(parent.id);
|
||||||
notMentioned.splice(parentPos + 1, 0, channel.id);
|
notMentioned.splice(parentPos + 1, 0, channel.id);
|
||||||
@ -156,16 +148,13 @@ router.patch(
|
|||||||
channel_id: channel.id,
|
channel_id: channel.id,
|
||||||
guild_id,
|
guild_id,
|
||||||
} as ChannelUpdateEvent);
|
} as ChannelUpdateEvent);
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
await Guild.update(
|
await Guild.update({ id: guild_id }, { channel_ordering: notMentioned });
|
||||||
{ id: guild_id },
|
|
||||||
{ channel_ordering: notMentioned },
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -45,8 +45,7 @@ router.post(
|
|||||||
where: { id: guild_id },
|
where: { id: guild_id },
|
||||||
select: ["owner_id"],
|
select: ["owner_id"],
|
||||||
});
|
});
|
||||||
if (guild.owner_id !== req.user_id)
|
if (guild.owner_id !== req.user_id) throw new HTTPError("You are not the owner of this guild", 401);
|
||||||
throw new HTTPError("You are not the owner of this guild", 401);
|
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
Guild.delete({ id: guild_id }), // this will also delete all guild related data
|
Guild.delete({ id: guild_id }), // this will also delete all guild related data
|
||||||
@ -60,7 +59,7 @@ router.post(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -59,7 +59,7 @@ router.get(
|
|||||||
},
|
},
|
||||||
minimum_size: 0,
|
minimum_size: 0,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -57,7 +57,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return res.json(emojis);
|
return res.json(emojis);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
@ -86,7 +86,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return res.json(emoji);
|
return res.json(emoji);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
@ -116,10 +116,7 @@ router.post(
|
|||||||
});
|
});
|
||||||
const { maxEmojis } = Config.get().limits.guild;
|
const { maxEmojis } = Config.get().limits.guild;
|
||||||
|
|
||||||
if (emoji_count >= maxEmojis)
|
if (emoji_count >= maxEmojis) throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(maxEmojis);
|
||||||
throw DiscordApiErrors.MAXIMUM_NUMBER_OF_EMOJIS_REACHED.withParams(
|
|
||||||
maxEmojis,
|
|
||||||
);
|
|
||||||
if (body.require_colons == null) body.require_colons = true;
|
if (body.require_colons == null) body.require_colons = true;
|
||||||
|
|
||||||
const user = await User.findOneOrFail({ where: { id: req.user_id } });
|
const user = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||||
@ -147,7 +144,7 @@ router.post(
|
|||||||
} as GuildEmojisUpdateEvent);
|
} as GuildEmojisUpdateEvent);
|
||||||
|
|
||||||
return res.status(201).json(emoji);
|
return res.status(201).json(emoji);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -184,7 +181,7 @@ router.patch(
|
|||||||
} as GuildEmojisUpdateEvent);
|
} as GuildEmojisUpdateEvent);
|
||||||
|
|
||||||
return res.json(emoji);
|
return res.json(emoji);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
@ -216,7 +213,7 @@ router.delete(
|
|||||||
} as GuildEmojisUpdateEvent);
|
} as GuildEmojisUpdateEvent);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -58,17 +58,13 @@ router.get(
|
|||||||
Guild.findOneOrFail({ where: { id: guild_id } }),
|
Guild.findOneOrFail({ where: { id: guild_id } }),
|
||||||
Member.findOne({ where: { guild_id: guild_id, id: req.user_id } }),
|
Member.findOne({ where: { guild_id: guild_id, id: req.user_id } }),
|
||||||
]);
|
]);
|
||||||
if (!member)
|
if (!member) throw new HTTPError("You are not a member of the guild you are trying to access", 401);
|
||||||
throw new HTTPError(
|
|
||||||
"You are not a member of the guild you are trying to access",
|
|
||||||
401,
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.send({
|
return res.send({
|
||||||
...guild,
|
...guild,
|
||||||
joined_at: member?.joined_at,
|
joined_at: member?.joined_at,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -99,9 +95,7 @@ router.patch(
|
|||||||
const permission = await getPermission(req.user_id, guild_id);
|
const permission = await getPermission(req.user_id, guild_id);
|
||||||
|
|
||||||
if (!rights.has("MANAGE_GUILDS") && !permission.has("MANAGE_GUILD"))
|
if (!rights.has("MANAGE_GUILDS") && !permission.has("MANAGE_GUILD"))
|
||||||
throw DiscordApiErrors.MISSING_PERMISSIONS.withParams(
|
throw DiscordApiErrors.MISSING_PERMISSIONS.withParams("MANAGE_GUILDS");
|
||||||
"MANAGE_GUILDS",
|
|
||||||
);
|
|
||||||
|
|
||||||
const guild = await Guild.findOneOrFail({
|
const guild = await Guild.findOneOrFail({
|
||||||
where: { id: guild_id },
|
where: { id: guild_id },
|
||||||
@ -110,47 +104,29 @@ router.patch(
|
|||||||
|
|
||||||
// TODO: guild update check image
|
// TODO: guild update check image
|
||||||
|
|
||||||
if (body.icon && body.icon != guild.icon)
|
if (body.icon && body.icon != guild.icon) body.icon = await handleFile(`/icons/${guild_id}`, body.icon);
|
||||||
body.icon = await handleFile(`/icons/${guild_id}`, body.icon);
|
|
||||||
|
|
||||||
if (body.banner && body.banner !== guild.banner)
|
if (body.banner && body.banner !== guild.banner)
|
||||||
body.banner = await handleFile(`/banners/${guild_id}`, body.banner);
|
body.banner = await handleFile(`/banners/${guild_id}`, body.banner);
|
||||||
|
|
||||||
if (body.splash && body.splash !== guild.splash)
|
if (body.splash && body.splash !== guild.splash)
|
||||||
body.splash = await handleFile(
|
body.splash = await handleFile(`/splashes/${guild_id}`, body.splash);
|
||||||
`/splashes/${guild_id}`,
|
|
||||||
body.splash,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
if (body.discovery_splash && body.discovery_splash !== guild.discovery_splash)
|
||||||
body.discovery_splash &&
|
body.discovery_splash = await handleFile(`/discovery-splashes/${guild_id}`, body.discovery_splash);
|
||||||
body.discovery_splash !== guild.discovery_splash
|
|
||||||
)
|
|
||||||
body.discovery_splash = await handleFile(
|
|
||||||
`/discovery-splashes/${guild_id}`,
|
|
||||||
body.discovery_splash,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (body.features) {
|
if (body.features) {
|
||||||
const diff = guild.features
|
const diff = guild.features
|
||||||
.filter((x) => !body.features?.includes(x))
|
.filter((x) => !body.features?.includes(x))
|
||||||
.concat(
|
.concat(body.features.filter((x) => !guild.features.includes(x)));
|
||||||
body.features.filter((x) => !guild.features.includes(x)),
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO move these
|
// TODO move these
|
||||||
const MUTABLE_FEATURES = [
|
const MUTABLE_FEATURES = ["COMMUNITY", "INVITES_DISABLED", "DISCOVERABLE"];
|
||||||
"COMMUNITY",
|
|
||||||
"INVITES_DISABLED",
|
|
||||||
"DISCOVERABLE",
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const feature of diff) {
|
for (const feature of diff) {
|
||||||
if (MUTABLE_FEATURES.includes(feature)) continue;
|
if (MUTABLE_FEATURES.includes(feature)) continue;
|
||||||
|
|
||||||
throw SpacebarApiErrors.FEATURE_IS_IMMUTABLE.withParams(
|
throw SpacebarApiErrors.FEATURE_IS_IMMUTABLE.withParams(feature);
|
||||||
feature,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// for some reason, they don't update in the assign.
|
// for some reason, they don't update in the assign.
|
||||||
@ -179,7 +155,7 @@ router.patch(
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
{ skipPermissionCheck: true },
|
{ skipPermissionCheck: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
await Guild.insertChannelInOrder(guild.id, channel.id, 0, guild);
|
await Guild.insertChannelInOrder(guild.id, channel.id, 0, guild);
|
||||||
@ -212,7 +188,7 @@ router.patch(
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
undefined,
|
undefined,
|
||||||
{ skipPermissionCheck: true },
|
{ skipPermissionCheck: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
await Guild.insertChannelInOrder(guild.id, channel.id, 0, guild);
|
await Guild.insertChannelInOrder(guild.id, channel.id, 0, guild);
|
||||||
@ -242,7 +218,7 @@ router.patch(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return res.json(data);
|
return res.json(data);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -41,7 +41,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return res.json(invites);
|
return res.json(invites);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -36,7 +36,7 @@ router.get(
|
|||||||
message: "Unknown Guild Member Verification Form",
|
message: "Unknown Guild Member Verification Form",
|
||||||
code: 10068,
|
code: 10068,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -62,13 +62,9 @@ router.get(
|
|||||||
select: {
|
select: {
|
||||||
index: true,
|
index: true,
|
||||||
// only grab public member props
|
// only grab public member props
|
||||||
...Object.fromEntries(
|
...Object.fromEntries(PublicMemberProjection.map((x) => [x, true])),
|
||||||
PublicMemberProjection.map((x) => [x, true]),
|
|
||||||
),
|
|
||||||
// and public user props
|
// and public user props
|
||||||
user: Object.fromEntries(
|
user: Object.fromEntries(PublicUserProjection.map((x) => [x, true])),
|
||||||
PublicUserProjection.map((x) => [x, true]),
|
|
||||||
),
|
|
||||||
roles: {
|
roles: {
|
||||||
id: true,
|
id: true,
|
||||||
},
|
},
|
||||||
@ -80,7 +76,7 @@ router.get(
|
|||||||
user: member.user.toPublicUser(),
|
user: member.user.toPublicUser(),
|
||||||
roles: member.roles.map((x) => x.id),
|
roles: member.roles.map((x) => x.id),
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -104,8 +100,7 @@ router.patch(
|
|||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const member_id =
|
const member_id = req.params.member_id === "@me" ? req.user_id : req.params.member_id;
|
||||||
req.params.member_id === "@me" ? req.user_id : req.params.member_id;
|
|
||||||
const body = req.body as MemberChangeSchema;
|
const body = req.body as MemberChangeSchema;
|
||||||
|
|
||||||
const member = await Member.findOneOrFail({
|
const member = await Member.findOneOrFail({
|
||||||
@ -128,19 +123,13 @@ router.patch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (("bio" in body || "avatar" in body) && req.params.member_id != "@me") {
|
||||||
("bio" in body || "avatar" in body) &&
|
|
||||||
req.params.member_id != "@me"
|
|
||||||
) {
|
|
||||||
const rights = await getRights(req.user_id);
|
const rights = await getRights(req.user_id);
|
||||||
rights.hasThrow("MANAGE_USERS");
|
rights.hasThrow("MANAGE_USERS");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (body.avatar)
|
if (body.avatar)
|
||||||
body.avatar = await handleFile(
|
body.avatar = await handleFile(`/guilds/${guild_id}/users/${member_id}/avatars`, body.avatar as string);
|
||||||
`/guilds/${guild_id}/users/${member_id}/avatars`,
|
|
||||||
body.avatar as string,
|
|
||||||
);
|
|
||||||
|
|
||||||
member.assign(body);
|
member.assign(body);
|
||||||
|
|
||||||
@ -152,8 +141,7 @@ router.patch(
|
|||||||
body.roles = body.roles || [];
|
body.roles = body.roles || [];
|
||||||
body.roles.filter((x) => !!x);
|
body.roles.filter((x) => !!x);
|
||||||
|
|
||||||
if (body.roles.indexOf(everyone.id) === -1)
|
if (body.roles.indexOf(everyone.id) === -1) body.roles.push(everyone.id);
|
||||||
body.roles.push(everyone.id);
|
|
||||||
// foreign key constraint will fail if role doesn't exist
|
// foreign key constraint will fail if role doesn't exist
|
||||||
member.roles = body.roles.map((x) => Role.create({ id: x }));
|
member.roles = body.roles.map((x) => Role.create({ id: x }));
|
||||||
}
|
}
|
||||||
@ -170,7 +158,7 @@ router.patch(
|
|||||||
} as GuildMemberUpdateEvent);
|
} as GuildMemberUpdateEvent);
|
||||||
|
|
||||||
res.json(member);
|
res.json(member);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
@ -222,7 +210,7 @@ router.put(
|
|||||||
|
|
||||||
await Member.addToGuild(member_id, guild_id);
|
await Member.addToGuild(member_id, guild_id);
|
||||||
res.send({ ...guild, emojis: emoji, roles: roles, stickers: stickers });
|
res.send({ ...guild, emojis: emoji, roles: roles, stickers: stickers });
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
@ -249,7 +237,7 @@ router.delete(
|
|||||||
|
|
||||||
await Member.removeFromGuild(member_id, guild_id);
|
await Member.removeFromGuild(member_id, guild_id);
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -49,7 +49,7 @@ router.patch(
|
|||||||
|
|
||||||
await Member.changeNickname(member_id, guild_id, req.body.nick);
|
await Member.changeNickname(member_id, guild_id, req.body.nick);
|
||||||
res.status(200).send();
|
res.status(200).send();
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -38,7 +38,7 @@ router.delete(
|
|||||||
|
|
||||||
await Member.removeRole(member_id, guild_id, role_id);
|
await Member.removeRole(member_id, guild_id, role_id);
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
@ -55,7 +55,7 @@ router.put(
|
|||||||
|
|
||||||
await Member.addRole(member_id, guild_id, role_id);
|
await Member.addRole(member_id, guild_id, role_id);
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -33,8 +33,7 @@ router.get(
|
|||||||
query: {
|
query: {
|
||||||
limit: {
|
limit: {
|
||||||
type: "number",
|
type: "number",
|
||||||
description:
|
description: "max number of members to return (1-1000). default 1",
|
||||||
"max number of members to return (1-1000). default 1",
|
|
||||||
},
|
},
|
||||||
after: {
|
after: {
|
||||||
type: "string",
|
type: "string",
|
||||||
@ -52,8 +51,7 @@ router.get(
|
|||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const limit = Number(req.query.limit) || 1;
|
const limit = Number(req.query.limit) || 1;
|
||||||
if (limit > 1000 || limit < 1)
|
if (limit > 1000 || limit < 1) throw new HTTPError("Limit must be between 1 and 1000");
|
||||||
throw new HTTPError("Limit must be between 1 and 1000");
|
|
||||||
const after = `${req.query.after}`;
|
const after = `${req.query.after}`;
|
||||||
const query = after ? { id: MoreThan(after) } : {};
|
const query = after ? { id: MoreThan(after) } : {};
|
||||||
|
|
||||||
@ -67,7 +65,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return res.json(members);
|
return res.json(members);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -54,14 +54,10 @@ router.get(
|
|||||||
} = req.query;
|
} = req.query;
|
||||||
|
|
||||||
const parsedLimit = Number(limit) || 50;
|
const parsedLimit = Number(limit) || 50;
|
||||||
if (parsedLimit < 1 || parsedLimit > 100)
|
if (parsedLimit < 1 || parsedLimit > 100) throw new HTTPError("limit must be between 1 and 100", 422);
|
||||||
throw new HTTPError("limit must be between 1 and 100", 422);
|
|
||||||
|
|
||||||
if (sort_order) {
|
if (sort_order) {
|
||||||
if (
|
if (typeof sort_order != "string" || ["desc", "asc"].indexOf(sort_order) == -1)
|
||||||
typeof sort_order != "string" ||
|
|
||||||
["desc", "asc"].indexOf(sort_order) == -1
|
|
||||||
)
|
|
||||||
throw FieldErrors({
|
throw FieldErrors({
|
||||||
sort_order: {
|
sort_order: {
|
||||||
message: "Value must be one of ('desc', 'asc').",
|
message: "Value must be one of ('desc', 'asc').",
|
||||||
@ -70,20 +66,13 @@ router.get(
|
|||||||
}); // todo this is wrong
|
}); // todo this is wrong
|
||||||
}
|
}
|
||||||
|
|
||||||
const permissions = await getPermission(
|
const permissions = await getPermission(req.user_id, req.params.guild_id, channel_id as string | undefined);
|
||||||
req.user_id,
|
|
||||||
req.params.guild_id,
|
|
||||||
channel_id as string | undefined,
|
|
||||||
);
|
|
||||||
permissions.hasThrow("VIEW_CHANNEL");
|
permissions.hasThrow("VIEW_CHANNEL");
|
||||||
if (!permissions.has("READ_MESSAGE_HISTORY"))
|
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json({ messages: [], total_results: 0 });
|
||||||
return res.json({ messages: [], total_results: 0 });
|
|
||||||
|
|
||||||
const query: FindManyOptions<Message> = {
|
const query: FindManyOptions<Message> = {
|
||||||
order: {
|
order: {
|
||||||
timestamp: sort_order
|
timestamp: sort_order ? (sort_order.toUpperCase() as "ASC" | "DESC") : "DESC",
|
||||||
? (sort_order.toUpperCase() as "ASC" | "DESC")
|
|
||||||
: "DESC",
|
|
||||||
},
|
},
|
||||||
take: parsedLimit || 0,
|
take: parsedLimit || 0,
|
||||||
where: {
|
where: {
|
||||||
@ -114,16 +103,8 @@ router.get(
|
|||||||
const ids = [];
|
const ids = [];
|
||||||
|
|
||||||
for (const channel of channels) {
|
for (const channel of channels) {
|
||||||
const perm = await getPermission(
|
const perm = await getPermission(req.user_id, req.params.guild_id, channel.id);
|
||||||
req.user_id,
|
if (!perm.has("VIEW_CHANNEL") || !perm.has("READ_MESSAGE_HISTORY")) continue;
|
||||||
req.params.guild_id,
|
|
||||||
channel.id,
|
|
||||||
);
|
|
||||||
if (
|
|
||||||
!perm.has("VIEW_CHANNEL") ||
|
|
||||||
!perm.has("READ_MESSAGE_HISTORY")
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
ids.push(channel.id);
|
ids.push(channel.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +151,7 @@ router.get(
|
|||||||
messages: messagesDto,
|
messages: messagesDto,
|
||||||
total_results: messages.length,
|
total_results: messages.length,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -57,10 +57,7 @@ router.patch(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (body.banner)
|
if (body.banner)
|
||||||
body.banner = await handleFile(
|
body.banner = await handleFile(`/guilds/${guild_id}/users/${req.user_id}/avatars`, body.banner as string);
|
||||||
`/guilds/${guild_id}/users/${req.user_id}/avatars`,
|
|
||||||
body.banner as string,
|
|
||||||
);
|
|
||||||
|
|
||||||
member = await OrmUtils.mergeDeep(member, body);
|
member = await OrmUtils.mergeDeep(member, body);
|
||||||
|
|
||||||
@ -74,7 +71,7 @@ router.patch(
|
|||||||
} as GuildMemberUpdateEvent);
|
} as GuildMemberUpdateEvent);
|
||||||
|
|
||||||
res.json(member);
|
res.json(member);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -23,12 +23,7 @@ import { IsNull, LessThan } from "typeorm";
|
|||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
//Returns all inactive members, respecting role hierarchy
|
//Returns all inactive members, respecting role hierarchy
|
||||||
const inactiveMembers = async (
|
const inactiveMembers = async (guild_id: string, user_id: string, days: number, roles: string[] = []) => {
|
||||||
guild_id: string,
|
|
||||||
user_id: string,
|
|
||||||
days: number,
|
|
||||||
roles: string[] = [],
|
|
||||||
) => {
|
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
date.setDate(date.getDate() - days);
|
date.setDate(date.getDate() - days);
|
||||||
//Snowflake should have `generateFromTime` method? Or similar?
|
//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.
|
//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)
|
if (roles.length && members.length)
|
||||||
members = members.filter((user) =>
|
members = members.filter((user) => user.roles?.some((role) => roles.includes(role.id)));
|
||||||
user.roles?.some((role) => roles.includes(role.id)),
|
|
||||||
);
|
|
||||||
|
|
||||||
const me = await Member.findOneOrFail({
|
const me = await Member.findOneOrFail({
|
||||||
where: { id: user_id, guild_id },
|
where: { id: user_id, guild_id },
|
||||||
@ -73,8 +66,8 @@ const inactiveMembers = async (
|
|||||||
member.roles?.some(
|
member.roles?.some(
|
||||||
(role) =>
|
(role) =>
|
||||||
role.position < myHighestRole || //roles higher than me can't be kicked
|
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;
|
return members;
|
||||||
@ -95,15 +88,10 @@ router.get(
|
|||||||
let roles = req.query.include_roles;
|
let roles = req.query.include_roles;
|
||||||
if (typeof roles === "string") roles = [roles]; //express will return array otherwise
|
if (typeof roles === "string") roles = [roles]; //express will return array otherwise
|
||||||
|
|
||||||
const members = await inactiveMembers(
|
const members = await inactiveMembers(req.params.guild_id, req.user_id, days, roles as string[]);
|
||||||
req.params.guild_id,
|
|
||||||
req.user_id,
|
|
||||||
days,
|
|
||||||
roles as string[],
|
|
||||||
);
|
|
||||||
|
|
||||||
res.send({ pruned: members.length });
|
res.send({ pruned: members.length });
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
@ -127,19 +115,12 @@ router.post(
|
|||||||
if (typeof roles === "string") roles = [roles];
|
if (typeof roles === "string") roles = [roles];
|
||||||
|
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const members = await inactiveMembers(
|
const members = await inactiveMembers(guild_id, req.user_id, days, roles as string[]);
|
||||||
guild_id,
|
|
||||||
req.user_id,
|
|
||||||
days,
|
|
||||||
roles as string[],
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(members.map((x) => Member.removeFromGuild(x.id, guild_id)));
|
||||||
members.map((x) => Member.removeFromGuild(x.id, guild_id)),
|
|
||||||
);
|
|
||||||
|
|
||||||
res.send({ purged: members.length });
|
res.send({ purged: members.length });
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -38,13 +38,8 @@ router.get(
|
|||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||||
//TODO we should use an enum for guild's features and not hardcoded strings
|
//TODO we should use an enum for guild's features and not hardcoded strings
|
||||||
return res.json(
|
return res.json(await getVoiceRegions(getIpAdress(req), guild.features.includes("VIP_REGIONS")));
|
||||||
await getVoiceRegions(
|
}
|
||||||
getIpAdress(req),
|
|
||||||
guild.features.includes("VIP_REGIONS"),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -53,7 +53,7 @@ router.get(
|
|||||||
where: { guild_id, id: role_id },
|
where: { guild_id, id: role_id },
|
||||||
});
|
});
|
||||||
return res.json(role);
|
return res.json(role);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
@ -75,8 +75,7 @@ router.delete(
|
|||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { guild_id, role_id } = req.params;
|
const { guild_id, role_id } = req.params;
|
||||||
if (role_id === guild_id)
|
if (role_id === guild_id) throw new HTTPError("You can't delete the @everyone role");
|
||||||
throw new HTTPError("You can't delete the @everyone role");
|
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
Role.delete({
|
Role.delete({
|
||||||
@ -94,7 +93,7 @@ router.delete(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: check role hierarchy
|
// TODO: check role hierarchy
|
||||||
@ -123,11 +122,7 @@ router.patch(
|
|||||||
const { role_id, guild_id } = req.params;
|
const { role_id, guild_id } = req.params;
|
||||||
const body = req.body as RoleModifySchema;
|
const body = req.body as RoleModifySchema;
|
||||||
|
|
||||||
if (body.icon && body.icon.length)
|
if (body.icon && body.icon.length) body.icon = await handleFile(`/role-icons/${role_id}`, body.icon as string);
|
||||||
body.icon = await handleFile(
|
|
||||||
`/role-icons/${role_id}`,
|
|
||||||
body.icon as string,
|
|
||||||
);
|
|
||||||
else body.icon = undefined;
|
else body.icon = undefined;
|
||||||
|
|
||||||
const role = await Role.findOneOrFail({
|
const role = await Role.findOneOrFail({
|
||||||
@ -135,10 +130,7 @@ router.patch(
|
|||||||
});
|
});
|
||||||
role.assign({
|
role.assign({
|
||||||
...body,
|
...body,
|
||||||
permissions: String(
|
permissions: String((req.permission?.bitfield || 0n) & BigInt(body.permissions || "0")),
|
||||||
(req.permission?.bitfield || 0n) &
|
|
||||||
BigInt(body.permissions || "0"),
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -154,7 +146,7 @@ router.patch(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
res.json(role);
|
res.json(role);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -22,41 +22,31 @@ import { route } from "@spacebar/api";
|
|||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.patch(
|
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
|
||||||
route({ permission: "MANAGE_ROLES" }),
|
const { guild_id, role_id } = req.params;
|
||||||
async (req: Request, res: Response) => {
|
const { member_ids } = req.body;
|
||||||
// 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
|
// don't mess with @everyone
|
||||||
if (role_id == guild_id) throw DiscordApiErrors.INVALID_ROLE;
|
if (role_id == guild_id) throw DiscordApiErrors.INVALID_ROLE;
|
||||||
|
|
||||||
const members = await Member.find({
|
const members = await Member.find({
|
||||||
where: { guild_id },
|
where: { guild_id },
|
||||||
relations: ["roles"],
|
relations: ["roles"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const [add, remove] = partition(
|
const [add, remove] = partition(
|
||||||
members,
|
members,
|
||||||
(member) =>
|
(member) => member_ids.includes(member.id) && !member.roles.map((role) => role.id).includes(role_id)
|
||||||
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
|
// TODO (erkin): have a bulk add/remove function that adds the roles in a single txn
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
...add.map((member) =>
|
...add.map((member) => Member.addRole(member.id, guild_id, role_id)),
|
||||||
Member.addRole(member.id, guild_id, role_id),
|
...remove.map((member) => Member.removeRole(member.id, guild_id, role_id)),
|
||||||
),
|
]);
|
||||||
...remove.map((member) =>
|
|
||||||
Member.removeRole(member.id, guild_id, role_id),
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -68,8 +68,7 @@ router.post(
|
|||||||
const role_count = await Role.count({ where: { guild_id } });
|
const role_count = await Role.count({ where: { guild_id } });
|
||||||
const { maxRoles } = Config.get().limits.guild;
|
const { maxRoles } = Config.get().limits.guild;
|
||||||
|
|
||||||
if (role_count > maxRoles)
|
if (role_count > maxRoles) throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles);
|
||||||
throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles);
|
|
||||||
|
|
||||||
const role = Role.create({
|
const role = Role.create({
|
||||||
// values before ...body are default and can be overriden
|
// values before ...body are default and can be overriden
|
||||||
@ -80,10 +79,7 @@ router.post(
|
|||||||
...body,
|
...body,
|
||||||
guild_id: guild_id,
|
guild_id: guild_id,
|
||||||
managed: false,
|
managed: false,
|
||||||
permissions: String(
|
permissions: String((req.permission?.bitfield || 0n) & BigInt(body.permissions || "0")),
|
||||||
(req.permission?.bitfield || 0n) &
|
|
||||||
BigInt(body.permissions || "0"),
|
|
||||||
),
|
|
||||||
tags: undefined,
|
tags: undefined,
|
||||||
icon: undefined,
|
icon: undefined,
|
||||||
unicode_emoji: undefined,
|
unicode_emoji: undefined,
|
||||||
@ -112,7 +108,7 @@ router.post(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
res.json(role);
|
res.json(role);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -136,11 +132,7 @@ router.patch(
|
|||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const body = req.body as RolePositionUpdateSchema;
|
const body = req.body as RolePositionUpdateSchema;
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(body.map(async (x) => Role.update({ guild_id, id: x.id }, { position: x.position })));
|
||||||
body.map(async (x) =>
|
|
||||||
Role.update({ guild_id, id: x.id }, { position: x.position }),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
const roles = await Role.find({
|
const roles = await Role.find({
|
||||||
where: body.map((x) => ({ id: x.id, guild_id })),
|
where: body.map((x) => ({ id: x.id, guild_id })),
|
||||||
@ -155,12 +147,12 @@ router.patch(
|
|||||||
guild_id,
|
guild_id,
|
||||||
role: x,
|
role: x,
|
||||||
},
|
},
|
||||||
} as GuildRoleUpdateEvent),
|
} as GuildRoleUpdateEvent)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json(roles);
|
res.json(roles);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -50,7 +50,7 @@ router.get(
|
|||||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
|
||||||
res.json(await Sticker.find({ where: { guild_id } }));
|
res.json(await Sticker.find({ where: { guild_id } }));
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const bodyParser = multer({
|
const bodyParser = multer({
|
||||||
@ -102,7 +102,7 @@ router.post(
|
|||||||
await sendStickerUpdateEvent(guild_id);
|
await sendStickerUpdateEvent(guild_id);
|
||||||
|
|
||||||
res.json(sticker);
|
res.json(sticker);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
function getStickerFormat(mime_type: string) {
|
function getStickerFormat(mime_type: string) {
|
||||||
@ -116,9 +116,7 @@ function getStickerFormat(mime_type: string) {
|
|||||||
case "image/gif":
|
case "image/gif":
|
||||||
return StickerFormatType.GIF;
|
return StickerFormatType.GIF;
|
||||||
default:
|
default:
|
||||||
throw new HTTPError(
|
throw new HTTPError("invalid sticker format: must be png, apng or lottie");
|
||||||
"invalid sticker format: must be png, apng or lottie",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,9 +139,9 @@ router.get(
|
|||||||
res.json(
|
res.json(
|
||||||
await Sticker.findOneOrFail({
|
await Sticker.findOneOrFail({
|
||||||
where: { guild_id, id: sticker_id },
|
where: { guild_id, id: sticker_id },
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -175,7 +173,7 @@ router.patch(
|
|||||||
await sendStickerUpdateEvent(guild_id);
|
await sendStickerUpdateEvent(guild_id);
|
||||||
|
|
||||||
return res.json(sticker);
|
return res.json(sticker);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
async function sendStickerUpdateEvent(guild_id: string) {
|
async function sendStickerUpdateEvent(guild_id: string) {
|
||||||
@ -207,7 +205,7 @@ router.delete(
|
|||||||
await sendStickerUpdateEvent(guild_id);
|
await sendStickerUpdateEvent(guild_id);
|
||||||
|
|
||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -57,7 +57,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
return res.json(templates);
|
return res.json(templates);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
@ -102,7 +102,7 @@ router.post(
|
|||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
res.json(template);
|
res.json(template);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
@ -123,7 +123,7 @@ router.delete(
|
|||||||
});
|
});
|
||||||
|
|
||||||
res.json(template);
|
res.json(template);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
@ -148,7 +148,7 @@ router.put(
|
|||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
res.json(template);
|
res.json(template);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -173,7 +173,7 @@ router.patch(
|
|||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
res.json(template);
|
res.json(template);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,13 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { Channel, ChannelType, Guild, Invite, VanityUrlSchema } from "@spacebar/util";
|
||||||
Channel,
|
|
||||||
ChannelType,
|
|
||||||
Guild,
|
|
||||||
Invite,
|
|
||||||
VanityUrlSchema,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
|
|
||||||
@ -64,11 +58,9 @@ router.get(
|
|||||||
});
|
});
|
||||||
if (!invite || invite.length == 0) return res.json({ code: null });
|
if (!invite || invite.length == 0) return res.json({ code: null });
|
||||||
|
|
||||||
return res.json(
|
return res.json(invite.map((x) => ({ code: x.code, uses: x.uses })));
|
||||||
invite.map((x) => ({ code: x.code, uses: x.uses })),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -94,11 +86,9 @@ router.patch(
|
|||||||
const code = body.code?.replace(InviteRegex, "");
|
const code = body.code?.replace(InviteRegex, "");
|
||||||
|
|
||||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||||
if (!guild.features.includes("VANITY_URL"))
|
if (!guild.features.includes("VANITY_URL")) throw new HTTPError("Your guild doesn't support vanity urls");
|
||||||
throw new HTTPError("Your guild doesn't support vanity urls");
|
|
||||||
|
|
||||||
if (!code || code.length === 0)
|
if (!code || code.length === 0) throw new HTTPError("Code cannot be null or empty");
|
||||||
throw new HTTPError("Code cannot be null or empty");
|
|
||||||
|
|
||||||
const invite = await Invite.findOne({ where: { code } });
|
const invite = await Invite.findOne({ where: { code } });
|
||||||
if (invite) throw new HTTPError("Invite already exists");
|
if (invite) throw new HTTPError("Invite already exists");
|
||||||
@ -112,7 +102,7 @@ router.patch(
|
|||||||
{ guild_id },
|
{ guild_id },
|
||||||
{
|
{
|
||||||
code: code,
|
code: code,
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.json({ code });
|
return res.json({ code });
|
||||||
@ -132,7 +122,7 @@ router.patch(
|
|||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
return res.json({ code: code });
|
return res.json({ code: code });
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -52,14 +52,9 @@ router.patch(
|
|||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const body = req.body as VoiceStateUpdateSchema;
|
const body = req.body as VoiceStateUpdateSchema;
|
||||||
const { guild_id } = req.params;
|
const { guild_id } = req.params;
|
||||||
const user_id =
|
const user_id = req.params.user_id === "@me" ? req.user_id : req.params.user_id;
|
||||||
req.params.user_id === "@me" ? req.user_id : req.params.user_id;
|
|
||||||
|
|
||||||
const perms = await getPermission(
|
const perms = await getPermission(req.user_id, guild_id, body.channel_id);
|
||||||
req.user_id,
|
|
||||||
guild_id,
|
|
||||||
body.channel_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
From https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state
|
From https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state
|
||||||
@ -98,7 +93,7 @@ router.patch(
|
|||||||
} as VoiceStateUpdateEvent),
|
} as VoiceStateUpdateEvent),
|
||||||
]);
|
]);
|
||||||
return res.sendStatus(204);
|
return res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,12 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { Channel, Guild, GuildUpdateWelcomeScreenSchema, Member } from "@spacebar/util";
|
||||||
Channel,
|
|
||||||
Guild,
|
|
||||||
GuildUpdateWelcomeScreenSchema,
|
|
||||||
Member,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
@ -46,7 +41,7 @@ router.get(
|
|||||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||||
|
|
||||||
res.json(guild.welcome_screen);
|
res.json(guild.welcome_screen);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.patch(
|
router.patch(
|
||||||
@ -70,11 +65,9 @@ router.patch(
|
|||||||
|
|
||||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||||
|
|
||||||
if (body.enabled != undefined)
|
if (body.enabled != undefined) guild.welcome_screen.enabled = body.enabled;
|
||||||
guild.welcome_screen.enabled = body.enabled;
|
|
||||||
|
|
||||||
if (body.description != undefined)
|
if (body.description != undefined) guild.welcome_screen.description = body.description;
|
||||||
guild.welcome_screen.description = body.description;
|
|
||||||
|
|
||||||
if (body.welcome_channels != undefined) {
|
if (body.welcome_channels != undefined) {
|
||||||
// Ensure channels exist within the guild
|
// Ensure channels exist within the guild
|
||||||
@ -83,8 +76,8 @@ router.patch(
|
|||||||
Channel.findOneOrFail({
|
Channel.findOneOrFail({
|
||||||
where: { id: channel_id, guild_id },
|
where: { id: channel_id, guild_id },
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
}),
|
})
|
||||||
) || [],
|
) || []
|
||||||
);
|
);
|
||||||
guild.welcome_screen.welcome_channels = body.welcome_channels;
|
guild.welcome_screen.welcome_channels = body.welcome_channels;
|
||||||
}
|
}
|
||||||
@ -92,7 +85,7 @@ router.patch(
|
|||||||
await guild.save();
|
await guild.save();
|
||||||
|
|
||||||
res.status(200).json(guild.welcome_screen);
|
res.status(200).json(guild.welcome_screen);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -81,10 +81,8 @@ router.get(
|
|||||||
// Only return channels where @everyone has the CONNECT permission
|
// Only return channels where @everyone has the CONNECT permission
|
||||||
if (
|
if (
|
||||||
doc.permission_overwrites === undefined ||
|
doc.permission_overwrites === undefined ||
|
||||||
Permissions.channelPermission(
|
Permissions.channelPermission(doc.permission_overwrites, Permissions.FLAGS.CONNECT) ===
|
||||||
doc.permission_overwrites,
|
Permissions.FLAGS.CONNECT
|
||||||
Permissions.FLAGS.CONNECT,
|
|
||||||
) === Permissions.FLAGS.CONNECT
|
|
||||||
) {
|
) {
|
||||||
channels.push({
|
channels.push({
|
||||||
id: doc.id,
|
id: doc.id,
|
||||||
@ -110,7 +108,7 @@ router.get(
|
|||||||
|
|
||||||
res.set("Cache-Control", "public, max-age=300");
|
res.set("Cache-Control", "public, max-age=300");
|
||||||
return res.json(data);
|
return res.json(data);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -57,15 +57,8 @@ router.get(
|
|||||||
|
|
||||||
// Fetch parameter
|
// Fetch parameter
|
||||||
const style = req.query.style?.toString() || "shield";
|
const style = req.query.style?.toString() || "shield";
|
||||||
if (
|
if (!["shield", "banner1", "banner2", "banner3", "banner4"].includes(style)) {
|
||||||
!["shield", "banner1", "banner2", "banner3", "banner4"].includes(
|
throw new HTTPError("Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').", 400);
|
||||||
style,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
throw new HTTPError(
|
|
||||||
"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
|
|
||||||
400,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup canvas
|
// Setup canvas
|
||||||
@ -74,17 +67,7 @@ router.get(
|
|||||||
const sizeOf = require("image-size");
|
const sizeOf = require("image-size");
|
||||||
|
|
||||||
// TODO: Widget style templates need Spacebar branding
|
// TODO: Widget style templates need Spacebar branding
|
||||||
const source = path.join(
|
const source = path.join(__dirname, "..", "..", "..", "..", "..", "assets", "widget", `${style}.png`);
|
||||||
__dirname,
|
|
||||||
"..",
|
|
||||||
"..",
|
|
||||||
"..",
|
|
||||||
"..",
|
|
||||||
"..",
|
|
||||||
"assets",
|
|
||||||
"widget",
|
|
||||||
`${style}.png`,
|
|
||||||
);
|
|
||||||
if (!fs.existsSync(source)) {
|
if (!fs.existsSync(source)) {
|
||||||
throw new HTTPError("Widget template does not exist.", 400);
|
throw new HTTPError("Widget template does not exist.", 400);
|
||||||
}
|
}
|
||||||
@ -100,99 +83,32 @@ router.get(
|
|||||||
switch (style) {
|
switch (style) {
|
||||||
case "shield":
|
case "shield":
|
||||||
ctx.textAlign = "center";
|
ctx.textAlign = "center";
|
||||||
await drawText(
|
await drawText(ctx, 73, 13, "#FFFFFF", "thin 10px Verdana", presence);
|
||||||
ctx,
|
|
||||||
73,
|
|
||||||
13,
|
|
||||||
"#FFFFFF",
|
|
||||||
"thin 10px Verdana",
|
|
||||||
presence,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case "banner1":
|
case "banner1":
|
||||||
if (icon) await drawIcon(ctx, 20, 27, 50, icon);
|
if (icon) await drawIcon(ctx, 20, 27, 50, icon);
|
||||||
await drawText(
|
await drawText(ctx, 83, 51, "#FFFFFF", "12px Verdana", name, 22);
|
||||||
ctx,
|
await drawText(ctx, 83, 66, "#C9D2F0FF", "thin 11px Verdana", presence);
|
||||||
83,
|
|
||||||
51,
|
|
||||||
"#FFFFFF",
|
|
||||||
"12px Verdana",
|
|
||||||
name,
|
|
||||||
22,
|
|
||||||
);
|
|
||||||
await drawText(
|
|
||||||
ctx,
|
|
||||||
83,
|
|
||||||
66,
|
|
||||||
"#C9D2F0FF",
|
|
||||||
"thin 11px Verdana",
|
|
||||||
presence,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case "banner2":
|
case "banner2":
|
||||||
if (icon) await drawIcon(ctx, 13, 19, 36, icon);
|
if (icon) await drawIcon(ctx, 13, 19, 36, icon);
|
||||||
await drawText(
|
await drawText(ctx, 62, 34, "#FFFFFF", "12px Verdana", name, 15);
|
||||||
ctx,
|
await drawText(ctx, 62, 49, "#C9D2F0FF", "thin 11px Verdana", presence);
|
||||||
62,
|
|
||||||
34,
|
|
||||||
"#FFFFFF",
|
|
||||||
"12px Verdana",
|
|
||||||
name,
|
|
||||||
15,
|
|
||||||
);
|
|
||||||
await drawText(
|
|
||||||
ctx,
|
|
||||||
62,
|
|
||||||
49,
|
|
||||||
"#C9D2F0FF",
|
|
||||||
"thin 11px Verdana",
|
|
||||||
presence,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case "banner3":
|
case "banner3":
|
||||||
if (icon) await drawIcon(ctx, 20, 20, 50, icon);
|
if (icon) await drawIcon(ctx, 20, 20, 50, icon);
|
||||||
await drawText(
|
await drawText(ctx, 83, 44, "#FFFFFF", "12px Verdana", name, 27);
|
||||||
ctx,
|
await drawText(ctx, 83, 58, "#C9D2F0FF", "thin 11px Verdana", presence);
|
||||||
83,
|
|
||||||
44,
|
|
||||||
"#FFFFFF",
|
|
||||||
"12px Verdana",
|
|
||||||
name,
|
|
||||||
27,
|
|
||||||
);
|
|
||||||
await drawText(
|
|
||||||
ctx,
|
|
||||||
83,
|
|
||||||
58,
|
|
||||||
"#C9D2F0FF",
|
|
||||||
"thin 11px Verdana",
|
|
||||||
presence,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case "banner4":
|
case "banner4":
|
||||||
if (icon) await drawIcon(ctx, 21, 136, 50, icon);
|
if (icon) await drawIcon(ctx, 21, 136, 50, icon);
|
||||||
await drawText(
|
await drawText(ctx, 84, 156, "#FFFFFF", "13px Verdana", name, 27);
|
||||||
ctx,
|
await drawText(ctx, 84, 171, "#C9D2F0FF", "thin 12px Verdana", presence);
|
||||||
84,
|
|
||||||
156,
|
|
||||||
"#FFFFFF",
|
|
||||||
"13px Verdana",
|
|
||||||
name,
|
|
||||||
27,
|
|
||||||
);
|
|
||||||
await drawText(
|
|
||||||
ctx,
|
|
||||||
84,
|
|
||||||
171,
|
|
||||||
"#C9D2F0FF",
|
|
||||||
"thin 12px Verdana",
|
|
||||||
presence,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new HTTPError(
|
throw new HTTPError(
|
||||||
"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
|
"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("Content-Type", "image/png");
|
||||||
res.set("Cache-Control", "public, max-age=3600");
|
res.set("Cache-Control", "public, max-age=3600");
|
||||||
return res.send(buffer);
|
return res.send(buffer);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
async function drawIcon(
|
async function drawIcon(canvas: any, x: number, y: number, scale: number, icon: string) {
|
||||||
canvas: any,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
scale: number,
|
|
||||||
icon: string,
|
|
||||||
) {
|
|
||||||
const img = new (require("canvas").Image)();
|
const img = new (require("canvas").Image)();
|
||||||
img.src = icon;
|
img.src = icon;
|
||||||
|
|
||||||
@ -234,12 +144,11 @@ async function drawText(
|
|||||||
color: string,
|
color: string,
|
||||||
font: string,
|
font: string,
|
||||||
text: string,
|
text: string,
|
||||||
maxcharacters?: number,
|
maxcharacters?: number
|
||||||
) {
|
) {
|
||||||
canvas.fillStyle = color;
|
canvas.fillStyle = color;
|
||||||
canvas.font = font;
|
canvas.font = font;
|
||||||
if (text.length > (maxcharacters || 0) && maxcharacters)
|
if (text.length > (maxcharacters || 0) && maxcharacters) text = text.slice(0, maxcharacters) + "...";
|
||||||
text = text.slice(0, maxcharacters) + "...";
|
|
||||||
canvas.fillText(text, x, y);
|
canvas.fillText(text, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ router.get(
|
|||||||
enabled: guild.widget_enabled || false,
|
enabled: guild.widget_enabled || false,
|
||||||
channel_id: guild.widget_channel_id || null,
|
channel_id: guild.widget_channel_id || null,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// https://discord.com/developers/docs/resources/guild#modify-guild-widget
|
// https://discord.com/developers/docs/resources/guild#modify-guild-widget
|
||||||
@ -74,12 +74,12 @@ router.patch(
|
|||||||
{
|
{
|
||||||
widget_enabled: body.enabled,
|
widget_enabled: body.enabled,
|
||||||
widget_channel_id: body.channel_id,
|
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
|
// Widget invite for the widget_channel_id gets created as part of the /guilds/{guild.id}/widget.json request
|
||||||
|
|
||||||
return res.json(body);
|
return res.json(body);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,14 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { Config, DiscordApiErrors, Guild, GuildCreateSchema, Member, getRights } from "@spacebar/util";
|
||||||
Config,
|
|
||||||
DiscordApiErrors,
|
|
||||||
Guild,
|
|
||||||
GuildCreateSchema,
|
|
||||||
Member,
|
|
||||||
getRights,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
@ -73,7 +66,7 @@ router.post(
|
|||||||
await Member.addToGuild(req.user_id, guild.id);
|
await Member.addToGuild(req.user_id, guild.id);
|
||||||
|
|
||||||
res.status(201).json(guild);
|
res.status(201).json(guild);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -47,13 +47,11 @@ router.get(
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { allowDiscordTemplates, allowRaws, enabled } =
|
const { allowDiscordTemplates, allowRaws, enabled } = Config.get().templates;
|
||||||
Config.get().templates;
|
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
res.json({
|
res.json({
|
||||||
code: 403,
|
code: 403,
|
||||||
message:
|
message: "Template creation & usage is disabled on this instance.",
|
||||||
"Template creation & usage is disabled on this instance.",
|
|
||||||
}).sendStatus(403);
|
}).sendStatus(403);
|
||||||
|
|
||||||
const { code } = req.params;
|
const { code } = req.params;
|
||||||
@ -63,8 +61,7 @@ router.get(
|
|||||||
return res
|
return res
|
||||||
.json({
|
.json({
|
||||||
code: 403,
|
code: 403,
|
||||||
message:
|
message: "Discord templates cannot be used on this instance.",
|
||||||
"Discord templates cannot be used on this instance.",
|
|
||||||
})
|
})
|
||||||
.sendStatus(403);
|
.sendStatus(403);
|
||||||
const discordTemplateID = code.split("discord:", 2)[1];
|
const discordTemplateID = code.split("discord:", 2)[1];
|
||||||
@ -74,7 +71,7 @@ router.get(
|
|||||||
{
|
{
|
||||||
method: "get",
|
method: "get",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
return res.json(await discordTemplateData.json());
|
return res.json(await discordTemplateData.json());
|
||||||
}
|
}
|
||||||
@ -95,75 +92,70 @@ router.get(
|
|||||||
where: { code: code },
|
where: { code: code },
|
||||||
});
|
});
|
||||||
res.json(template);
|
res.json(template);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post("/:code", route({ requestBody: "GuildTemplateCreateSchema" }), async (req: Request, res: Response) => {
|
||||||
"/:code",
|
const {
|
||||||
route({ requestBody: "GuildTemplateCreateSchema" }),
|
enabled,
|
||||||
async (req: Request, res: Response) => {
|
allowTemplateCreation,
|
||||||
const {
|
// allowDiscordTemplates,
|
||||||
enabled,
|
// allowRaws,
|
||||||
allowTemplateCreation,
|
} = Config.get().templates;
|
||||||
// allowDiscordTemplates,
|
if (!enabled)
|
||||||
// allowRaws,
|
return res
|
||||||
} = Config.get().templates;
|
.json({
|
||||||
if (!enabled)
|
code: 403,
|
||||||
return res
|
message: "Template creation & usage is disabled on this instance.",
|
||||||
.json({
|
})
|
||||||
code: 403,
|
.sendStatus(403);
|
||||||
message:
|
if (!allowTemplateCreation)
|
||||||
"Template creation & usage is disabled on this instance.",
|
return res
|
||||||
})
|
.json({
|
||||||
.sendStatus(403);
|
code: 403,
|
||||||
if (!allowTemplateCreation)
|
message: "Template creation is disabled on this instance.",
|
||||||
return res
|
})
|
||||||
.json({
|
.sendStatus(403);
|
||||||
code: 403,
|
|
||||||
message: "Template creation is disabled on this instance.",
|
|
||||||
})
|
|
||||||
.sendStatus(403);
|
|
||||||
|
|
||||||
const { code } = req.params;
|
const { code } = req.params;
|
||||||
const body = req.body as GuildTemplateCreateSchema;
|
const body = req.body as GuildTemplateCreateSchema;
|
||||||
|
|
||||||
const { maxGuilds } = Config.get().limits.user;
|
const { maxGuilds } = Config.get().limits.user;
|
||||||
|
|
||||||
const guild_count = await Member.count({ where: { id: req.user_id } });
|
const guild_count = await Member.count({ where: { id: req.user_id } });
|
||||||
if (guild_count >= maxGuilds) {
|
if (guild_count >= maxGuilds) {
|
||||||
throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
|
throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = await Template.findOneOrFail({
|
const template = await Template.findOneOrFail({
|
||||||
where: { code: code },
|
where: { code: code },
|
||||||
});
|
});
|
||||||
|
|
||||||
const guild_id = Snowflake.generate();
|
const guild_id = Snowflake.generate();
|
||||||
|
|
||||||
const [guild] = await Promise.all([
|
const [guild] = await Promise.all([
|
||||||
Guild.create({
|
Guild.create({
|
||||||
...body,
|
...body,
|
||||||
...template.serialized_source_guild,
|
...template.serialized_source_guild,
|
||||||
id: guild_id,
|
id: guild_id,
|
||||||
owner_id: req.user_id,
|
owner_id: req.user_id,
|
||||||
}).save(),
|
}).save(),
|
||||||
Role.create({
|
Role.create({
|
||||||
id: guild_id,
|
id: guild_id,
|
||||||
guild_id: guild_id,
|
guild_id: guild_id,
|
||||||
color: 0,
|
color: 0,
|
||||||
hoist: false,
|
hoist: false,
|
||||||
managed: true,
|
managed: true,
|
||||||
mentionable: true,
|
mentionable: true,
|
||||||
name: "@everyone",
|
name: "@everyone",
|
||||||
permissions: BigInt("2251804225").toString(), // TODO: where did this come from?
|
permissions: BigInt("2251804225").toString(), // TODO: where did this come from?
|
||||||
position: 0,
|
position: 0,
|
||||||
}).save(),
|
}).save(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await Member.addToGuild(req.user_id, guild_id);
|
await Member.addToGuild(req.user_id, guild_id);
|
||||||
|
|
||||||
res.status(201).json({ id: guild.id });
|
res.status(201).json({ id: guild.id });
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -53,7 +53,7 @@ router.get(
|
|||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).send(invite);
|
res.status(200).send(invite);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
@ -89,21 +89,14 @@ router.post(
|
|||||||
where: { id: req.user_id },
|
where: { id: req.user_id },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (features.includes("INTERNAL_EMPLOYEE_ONLY") && (public_flags & 1) !== 1)
|
||||||
features.includes("INTERNAL_EMPLOYEE_ONLY") &&
|
throw new HTTPError("Only intended for the staff of this server.", 401);
|
||||||
(public_flags & 1) !== 1
|
if (features.includes("INVITES_DISABLED")) throw new HTTPError("Sorry, this guild has joins closed.", 403);
|
||||||
)
|
|
||||||
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);
|
const invite = await Invite.joinGuild(req.user_id, code);
|
||||||
|
|
||||||
res.json(invite);
|
res.json(invite);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// * cant use permission of route() function because path doesn't have guild_id/channel_id
|
// * 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 invite = await Invite.findOneOrFail({ where: { code } });
|
||||||
const { guild_id, channel_id } = invite;
|
const { guild_id, channel_id } = invite;
|
||||||
|
|
||||||
const permission = await getPermission(
|
const permission = await getPermission(req.user_id, guild_id, channel_id);
|
||||||
req.user_id,
|
|
||||||
guild_id,
|
|
||||||
channel_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
if (!permission.has("MANAGE_GUILD") && !permission.has("MANAGE_CHANNELS"))
|
||||||
!permission.has("MANAGE_GUILD") &&
|
throw new HTTPError("You missing the MANAGE_GUILD or MANAGE_CHANNELS permission", 401);
|
||||||
!permission.has("MANAGE_CHANNELS")
|
|
||||||
)
|
|
||||||
throw new HTTPError(
|
|
||||||
"You missing the MANAGE_GUILD or MANAGE_CHANNELS permission",
|
|
||||||
401,
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
Invite.delete({ code }),
|
Invite.delete({ code }),
|
||||||
@ -156,7 +139,7 @@ router.delete(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
res.json({ invite: invite });
|
res.json({ invite: invite });
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,11 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { Application, DiscordApiErrors, PublicUserProjection } from "@spacebar/util";
|
||||||
Application,
|
|
||||||
DiscordApiErrors,
|
|
||||||
PublicUserProjection,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
@ -40,9 +36,7 @@ router.get(
|
|||||||
where: { id: req.params.id },
|
where: { id: req.params.id },
|
||||||
relations: ["bot", "owner"],
|
relations: ["bot", "owner"],
|
||||||
select: {
|
select: {
|
||||||
owner: Object.fromEntries(
|
owner: Object.fromEntries(PublicUserProjection.map((x) => [x, true])),
|
||||||
PublicUserProjection.map((x) => [x, true]),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -51,9 +45,8 @@ router.get(
|
|||||||
res.json({
|
res.json({
|
||||||
...app,
|
...app,
|
||||||
owner: app.owner.toPublicUser(),
|
owner: app.owner.toPublicUser(),
|
||||||
install_params:
|
install_params: app.install_params !== null ? app.install_params : undefined,
|
||||||
app.install_params !== null ? app.install_params : undefined,
|
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -84,13 +84,7 @@ router.get(
|
|||||||
id: req.user_id,
|
id: req.user_id,
|
||||||
bot: false,
|
bot: false,
|
||||||
},
|
},
|
||||||
select: [
|
select: ["id", "username", "avatar", "discriminator", "public_flags"],
|
||||||
"id",
|
|
||||||
"username",
|
|
||||||
"avatar",
|
|
||||||
"discriminator",
|
|
||||||
"public_flags",
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const guilds = await Member.find({
|
const guilds = await Member.find({
|
||||||
@ -165,7 +159,7 @@ router.get(
|
|||||||
},
|
},
|
||||||
authorized: false,
|
authorized: false,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
@ -210,17 +204,9 @@ router.post(
|
|||||||
// TODO: captcha verification
|
// TODO: captcha verification
|
||||||
// TODO: MFA verification
|
// TODO: MFA verification
|
||||||
|
|
||||||
const perms = await getPermission(
|
const perms = await getPermission(req.user_id, body.guild_id, undefined, { member_relations: ["user"] });
|
||||||
req.user_id,
|
|
||||||
body.guild_id,
|
|
||||||
undefined,
|
|
||||||
{ member_relations: ["user"] },
|
|
||||||
);
|
|
||||||
// getPermission cache won't exist if we're owner
|
// getPermission cache won't exist if we're owner
|
||||||
if (
|
if (Object.keys(perms.cache || {}).length > 0 && perms.cache.member?.user.bot)
|
||||||
Object.keys(perms.cache || {}).length > 0 &&
|
|
||||||
perms.cache.member?.user.bot
|
|
||||||
)
|
|
||||||
throw DiscordApiErrors.UNAUTHORIZED;
|
throw DiscordApiErrors.UNAUTHORIZED;
|
||||||
perms.hasThrow("MANAGE_GUILD");
|
perms.hasThrow("MANAGE_GUILD");
|
||||||
|
|
||||||
@ -234,19 +220,14 @@ router.post(
|
|||||||
// TODO: use DiscordApiErrors
|
// TODO: use DiscordApiErrors
|
||||||
// findOneOrFail throws code 404
|
// findOneOrFail throws code 404
|
||||||
if (!app) throw new ApiError("Unknown Application", 10002, 404);
|
if (!app) throw new ApiError("Unknown Application", 10002, 404);
|
||||||
if (!app.bot)
|
if (!app.bot) throw new ApiError("OAuth2 application does not have a bot", 50010, 400);
|
||||||
throw new ApiError(
|
|
||||||
"OAuth2 application does not have a bot",
|
|
||||||
50010,
|
|
||||||
400,
|
|
||||||
);
|
|
||||||
|
|
||||||
await Member.addToGuild(app.id, body.guild_id);
|
await Member.addToGuild(app.id, body.guild_id);
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
location: "/oauth2/authorized", // redirect URL
|
location: "/oauth2/authorized", // redirect URL
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -48,7 +48,7 @@ router.get(
|
|||||||
tosPage: general.tosPage,
|
tosPage: general.tosPage,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -34,20 +34,14 @@ router.get(
|
|||||||
const { cdn, gateway, api } = Config.get();
|
const { cdn, gateway, api } = Config.get();
|
||||||
|
|
||||||
const IdentityForm = {
|
const IdentityForm = {
|
||||||
cdn:
|
cdn: cdn.endpointPublic || process.env.CDN || "http://localhost:3001",
|
||||||
cdn.endpointPublic ||
|
gateway: gateway.endpointPublic || process.env.GATEWAY || "ws://localhost:3001",
|
||||||
process.env.CDN ||
|
|
||||||
"http://localhost:3001",
|
|
||||||
gateway:
|
|
||||||
gateway.endpointPublic ||
|
|
||||||
process.env.GATEWAY ||
|
|
||||||
"ws://localhost:3001",
|
|
||||||
defaultApiVersion: api.defaultVersion ?? 9,
|
defaultApiVersion: api.defaultVersion ?? 9,
|
||||||
apiEndpoint: api.endpointPublic ?? "http://localhost:3001/api/",
|
apiEndpoint: api.endpointPublic ?? "http://localhost:3001/api/",
|
||||||
};
|
};
|
||||||
|
|
||||||
res.json(IdentityForm);
|
res.json(IdentityForm);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -33,7 +33,7 @@ router.get(
|
|||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { general } = Config.get();
|
const { general } = Config.get();
|
||||||
res.json(general);
|
res.json(general);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -33,7 +33,7 @@ router.get(
|
|||||||
async (req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
const { limits } = Config.get();
|
const { limits } = Config.get();
|
||||||
res.json(limits);
|
res.json(limits);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -17,14 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
import {
|
import { Config, getRights, Guild, Member, Message, User } from "@spacebar/util";
|
||||||
Config,
|
|
||||||
getRights,
|
|
||||||
Guild,
|
|
||||||
Member,
|
|
||||||
Message,
|
|
||||||
User,
|
|
||||||
} from "@spacebar/util";
|
|
||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@ -54,7 +47,7 @@ router.get(
|
|||||||
members: await Member.count(),
|
members: await Member.count(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -61,7 +61,7 @@ router.post(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return res.status(204);
|
return res.status(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -20,15 +20,11 @@ import { Router, Request, Response } from "express";
|
|||||||
import { route } from "@spacebar/api";
|
import { route } from "@spacebar/api";
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.get(
|
router.get("/scheduled-maintenances/upcoming.json", route({}), async (req: Request, res: Response) => {
|
||||||
"/scheduled-maintenances/upcoming.json",
|
res.json({
|
||||||
route({}),
|
page: {},
|
||||||
async (req: Request, res: Response) => {
|
scheduled_maintenances: {},
|
||||||
res.json({
|
});
|
||||||
page: {},
|
});
|
||||||
scheduled_maintenances: {},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -31,7 +31,7 @@ router.post(
|
|||||||
(req: Request, res: Response) => {
|
(req: Request, res: Response) => {
|
||||||
// TODO:
|
// TODO:
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user