1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-09-20 01:31:34 +02:00
This commit is contained in:
TheArcaneBrony 2022-08-20 03:27:03 +02:00
parent 363d9887cf
commit 04dea8d788
No known key found for this signature in database
GPG Key ID: 32FC5AAADAD75A22
339 changed files with 3825 additions and 4225 deletions

View File

@ -1,11 +1,11 @@
blank_issues_enabled: true blank_issues_enabled: true
contact_links: contact_links:
- name: Fosscord Documentation - name: Fosscord Documentation
url: https://docs.fosscord.com/ url: https://docs.fosscord.com/
about: Need documentation and examples for the Fosscord? Head over to Fosscord's official documentation. about: Need documentation and examples for the Fosscord? Head over to Fosscord's official documentation.
- name: Discord's Developer Documentation - name: Discord's Developer Documentation
url: https://discord.com/developers/docs/intro url: https://discord.com/developers/docs/intro
about: Need help with the Discord resources? Head here instead of asking on Fosscord! about: Need help with the Discord resources? Head here instead of asking on Fosscord!
- name: Fosscord' Official Discord server - name: Fosscord' Official Discord server
url: https://discord.com/invite/Ms5Ev7S6bF url: https://discord.com/invite/Ms5Ev7S6bF
about: Need help with the server? Talk with us in our official server. about: Need help with the server? Talk with us in our official server.

View File

@ -1,13 +1,17 @@
## Notes ## Notes
## Additions ## Additions
- -
## Fixes ## Fixes
- -
## Download ## Download
- [Windows]()
- [MacOS]() - [Windows]()
- [Linux]() - [MacOS]()
- [Linux]()
After (extracting) and starting the server executable you can access your own Fosscord server on http://localhost:3001/ After (extracting) and starting the server executable you can access your own Fosscord server on http://localhost:3001/

26
.vscode/launch.json vendored
View File

@ -1,17 +1,13 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"name": "Launch Program", "name": "Launch Program",
"skipFiles": [ "skipFiles": ["<node_internals>/**"],
"<node_internals>/**" "program": "${file}",
], "outFiles": ["${workspaceFolder}/**/*.js"]
"program": "${file}", }
"outFiles": [ ]
"${workspaceFolder}/**/*.js"
]
}
]
} }

View File

@ -1,2 +1 @@
The Docker image is coming with the dashboard. The planned release date is 2022-12-24. The Docker image is coming with the dashboard. The planned release date is 2022-12-24.

View File

@ -87,6 +87,7 @@
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"patch-package": "^6.4.7", "patch-package": "^6.4.7",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"prettier": "^2.7.1",
"proxy-agent": "^5.0.0", "proxy-agent": "^5.0.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"typeorm": "^0.3.7", "typeorm": "^0.3.7",

View File

@ -8,9 +8,9 @@ const rl = readline.createInterface({ input: process.stdin, output: process.stdo
const data = { env: [], config: { register: {} }, extra_pkgs: [] }; const data = { env: [], config: { register: {} }, extra_pkgs: [] };
let rights = []; let rights = [];
process.on('SIGINT', function() { process.on("SIGINT", function () {
console.log("Caught interrupt signal"); console.log("Caught interrupt signal");
process.exit(); process.exit();
}); });
console.log("Welcome to Fosscord!"); console.log("Welcome to Fosscord!");
@ -18,8 +18,8 @@ console.log("Please remember this is pre-release software!");
console.log("We will guide you through some important setup steps."); console.log("We will guide you through some important setup steps.");
console.log(); console.log();
if(fs.existsSync("package-lock.json")) fs.rmSync("package-lock.json"); if (fs.existsSync("package-lock.json")) fs.rmSync("package-lock.json");
if(fs.existsSync("yarn.lock")) fs.rmSync("yarn.lock"); if (fs.existsSync("yarn.lock")) fs.rmSync("yarn.lock");
async function main() { async function main() {
printTitle("Step 1: Database setup"); printTitle("Step 1: Database setup");
@ -82,7 +82,7 @@ async function main() {
if (data.db != "sqlite") if (data.db != "sqlite")
data.env.push(`DATABASE=${data.db}://${data.db_user}:${data.db_pass}@${data.db_host}:${data.db_port}/${data.db_name}`); data.env.push(`DATABASE=${data.db}://${data.db_user}:${data.db_pass}@${data.db_host}:${data.db_port}/${data.db_name}`);
data.env.push(`PORT=${data.port}`); data.env.push(`PORT=${data.port}`);
data.env.push('THREADS=1') data.env.push("THREADS=1");
printTitle("Step 4: Default rights"); printTitle("Step 4: Default rights");
console.log("Please enter the default rights for new users."); console.log("Please enter the default rights for new users.");
@ -126,8 +126,9 @@ async function main() {
}; };
printTitle("Step 5: extra options"); printTitle("Step 5: extra options");
if(/y?/i.test(await ask("Use fast BCrypt implementation (requires a compiler) (Y/n): "))) data.extra_pkgs.push("bcrypt"); if (/y?/i.test(await ask("Use fast BCrypt implementation (requires a compiler) (Y/n): "))) data.extra_pkgs.push("bcrypt");
if(/y?/.test(await ask("Enable support for widgets (requires compiler, known to fail on some ARM devices.) (Y/n): "))) data.extra_pkgs.push("canvas"); if (/y?/.test(await ask("Enable support for widgets (requires compiler, known to fail on some ARM devices.) (Y/n): ")))
data.extra_pkgs.push("canvas");
printTitle("Step 6: finalizing..."); printTitle("Step 6: finalizing...");
//save //save
@ -140,13 +141,13 @@ async function main() {
console.log(" ==> Ensuring yarn is up to date (v3, not v1)..."); console.log(" ==> Ensuring yarn is up to date (v3, not v1)...");
execIn("npx yarn set version stable", process.cwd()); execIn("npx yarn set version stable", process.cwd());
console.log(" ==> Installing base packages"); console.log(" ==> Installing base packages");
execIn("npx --yes yarn install", process.cwd(), {stdio: "inherit"}); execIn("npx --yes yarn install", process.cwd(), { stdio: "inherit" });
console.log(` ==> Installing extra packages: ${data.extra_pkgs.join(', ')}...`); console.log(` ==> Installing extra packages: ${data.extra_pkgs.join(", ")}...`);
execIn(`npx --yes yarn add -O ${data.extra_pkgs.join(' ')}`, process.cwd(), {stdio: "inherit"}); execIn(`npx --yes yarn add -O ${data.extra_pkgs.join(" ")}`, process.cwd(), { stdio: "inherit" });
console.log('==> Building...') console.log("==> Building...");
execIn('npx --yes yarn run build', process.cwd(), {stdio: "inherit"}); execIn("npx --yes yarn run build", process.cwd(), { stdio: "inherit" });
printTitle("Step 6: run your instance!"); printTitle("Step 6: run your instance!");
console.log("Installation is complete!"); console.log("Installation is complete!");
console.log("You can now start your instance by running 'npm run start:bundle'!"); console.log("You can now start your instance by running 'npm run start:bundle'!");

View File

@ -1,15 +1,15 @@
process.on("unhandledRejection", console.error); process.on("unhandledRejection", console.error);
process.on("uncaughtException", console.error); process.on("uncaughtException", console.error);
import http from "http";
import * as Api from "@fosscord/api"; import * as Api from "@fosscord/api";
import * as Gateway from "@fosscord/gateway";
import { CDNServer } from "@fosscord/cdn"; import { CDNServer } from "@fosscord/cdn";
import express from "express"; import * as Gateway from "@fosscord/gateway";
import { green, bold, yellow } from "picocolors";
import { Config, getOrInitialiseDatabase } from "@fosscord/util"; import { Config, getOrInitialiseDatabase } from "@fosscord/util";
import * as Sentry from "@sentry/node"; import * as Sentry from "@sentry/node";
import * as Tracing from "@sentry/tracing"; import * as Tracing from "@sentry/tracing";
import express from "express";
import http from "http";
import { bold, green, yellow } from "picocolors";
// import { PluginLoader } from "@fosscord/util"; // import { PluginLoader } from "@fosscord/util";
const app = express(); const app = express();
@ -26,12 +26,12 @@ const cdn = new CDNServer({ server, port, production, app });
const gateway = new Gateway.Server({ server, port, production }); const gateway = new Gateway.Server({ server, port, production });
//this is what has been added for the /stop API route //this is what has been added for the /stop API route
process.on('SIGTERM', () => { process.on("SIGTERM", () => {
setTimeout(()=>process.exit(0), 3000) setTimeout(() => process.exit(0), 3000);
server.close(() => { server.close(() => {
console.log("Stop API has been successfully POSTed, SIGTERM sent") console.log("Stop API has been successfully POSTed, SIGTERM sent");
}) });
}) });
//this is what has been added for the /stop API route //this is what has been added for the /stop API route
async function main() { async function main() {
@ -42,16 +42,15 @@ async function main() {
await Config.set({ await Config.set({
cdn: { cdn: {
endpointClient: "${location.host}", endpointClient: "${location.host}",
endpointPrivate: `http://localhost:${port}`, endpointPrivate: `http://localhost:${port}`
}, },
gateway: { gateway: {
endpointClient: endpointClient: '${location.protocol === "https:" ? "wss://" : "ws://"}${location.host}',
'${location.protocol === "https:" ? "wss://" : "ws://"}${location.host}',
endpointPrivate: `ws://localhost:${port}`, endpointPrivate: `ws://localhost:${port}`,
...(!Config.get().gateway.endpointPublic && { ...(!Config.get().gateway.endpointPublic && {
endpointPublic: `ws://localhost:${port}`, endpointPublic: `ws://localhost:${port}`
}), })
}, }
// regions: { // regions: {
// default: "fosscord", // default: "fosscord",
// useDefaultAsOptimal: true, // useDefaultAsOptimal: true,
@ -70,15 +69,10 @@ async function main() {
//Sentry //Sentry
if (Config.get().sentry.enabled) { if (Config.get().sentry.enabled) {
console.log( console.log(`[Bundle] ${yellow("You are using Sentry! This may slightly impact performance on large loads!")}`);
`[Bundle] ${yellow("You are using Sentry! This may slightly impact performance on large loads!")}`
);
Sentry.init({ Sentry.init({
dsn: Config.get().sentry.endpoint, dsn: Config.get().sentry.endpoint,
integrations: [ integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })],
new Sentry.Integrations.Http({ tracing: true }),
new Tracing.Integrations.Express({ app }),
],
tracesSampleRate: Config.get().sentry.traceSampleRate, tracesSampleRate: Config.get().sentry.traceSampleRate,
environment: Config.get().sentry.environment environment: Config.get().sentry.environment
}); });

View File

@ -1,16 +1,16 @@
import { Server, ServerOptions } from "lambert-server";
import { Authentication, CORS } from "./middlewares/";
import { Config, getOrInitialiseDatabase, initEvent, registerRoutes } from "@fosscord/util"; import { Config, getOrInitialiseDatabase, initEvent, registerRoutes } from "@fosscord/util";
import { ErrorHandler } from "./middlewares/ErrorHandler"; import { NextFunction, Request, Response, Router } from "express";
import { BodyParser } from "./middlewares/BodyParser"; import { Server, ServerOptions } from "lambert-server";
import { Router, Request, Response, NextFunction } from "express"; import morgan from "morgan";
import path from "path"; import path from "path";
import { red } from "picocolors";
import { Authentication, CORS } from "./middlewares/";
import { BodyParser } from "./middlewares/BodyParser";
import { ErrorHandler } from "./middlewares/ErrorHandler";
import { initRateLimits } from "./middlewares/RateLimit"; import { initRateLimits } from "./middlewares/RateLimit";
import TestClient from "./middlewares/TestClient"; import TestClient from "./middlewares/TestClient";
import { initTranslation } from "./middlewares/Translation"; import { initTranslation } from "./middlewares/Translation";
import morgan from "morgan";
import { initInstance } from "./util/handlers/Instance"; import { initInstance } from "./util/handlers/Instance";
import { red } from "picocolors"
export interface FosscordServerOptions extends ServerOptions {} export interface FosscordServerOptions extends ServerOptions {}
@ -85,7 +85,12 @@ export class FosscordServer extends Server {
this.app.use(ErrorHandler); this.app.use(ErrorHandler);
TestClient(this.app); TestClient(this.app);
if (logRequests) console.log(red(`Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!`)); if (logRequests)
console.log(
red(
`Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!`
)
);
return super.start(); return super.start();
} }

View File

@ -1,3 +1,3 @@
export * from "./Server";
export * from "./middlewares/"; export * from "./middlewares/";
export * from "./Server";
export * from "./util/"; export * from "./util/";

View File

@ -1,6 +1,5 @@
import { checkToken, Config, HTTPError, Rights } from "@fosscord/util";
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
import { checkToken, Config, Rights } from "@fosscord/util";
export const NO_AUTHORIZATION_ROUTES = [ export const NO_AUTHORIZATION_ROUTES = [
// Authentication routes // Authentication routes

View File

@ -1,6 +1,6 @@
import { HTTPError } from "@fosscord/util";
import bodyParser, { OptionsJson } from "body-parser"; import bodyParser, { OptionsJson } from "body-parser";
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
export function BodyParser(opts?: OptionsJson) { export function BodyParser(opts?: OptionsJson) {
const jsonParser = bodyParser.json(opts); const jsonParser = bodyParser.json(opts);

View File

@ -1,6 +1,5 @@
import { ApiError, FieldError, HTTPError } from "@fosscord/util";
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
import { ApiError, FieldError } from "@fosscord/util";
const EntityNotFoundErrorRegex = /"(\w+)"/; const EntityNotFoundErrorRegex = /"(\w+)"/;
export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) { export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) {

View File

@ -1,6 +1,6 @@
import { Config, getRights, listenEvent, Rights } from "@fosscord/util";
import { NextFunction, Request, Response, Router } from "express";
import { getIpAdress } from "@fosscord/api"; import { getIpAdress } from "@fosscord/api";
import { Config, getRights, listenEvent } from "@fosscord/util";
import { NextFunction, Request, Response, Router } from "express";
import { API_PREFIX_TRAILING_SLASH } from "./Authentication"; import { API_PREFIX_TRAILING_SLASH } from "./Authentication";
// Docs: https://discord.com/developers/docs/topics/rate-limits // Docs: https://discord.com/developers/docs/topics/rate-limits
@ -163,7 +163,7 @@ export async function initRateLimits(app: Router) {
app.use("/auth/register", rateLimit({ onlyIp: true, success: true, ...routes.auth.register })); app.use("/auth/register", rateLimit({ onlyIp: true, success: true, ...routes.auth.register }));
} }
async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number; }) { async function hitRoute(opts: { executor_id: string; bucket_id: string; max_hits: number; window: number }) {
const id = opts.executor_id + opts.bucket_id; const id = opts.executor_id + opts.bucket_id;
let limit = Cache.get(id); let limit = Cache.get(id);
if (!limit) { if (!limit) {

View File

@ -1,13 +1,13 @@
import express, { Request, Response, Application } from "express";
import fs from "fs";
import path from "path";
import fetch, { Response as FetchResponse, Headers } from "node-fetch";
import ProxyAgent from 'proxy-agent';
import { Config } from "@fosscord/util"; import { Config } from "@fosscord/util";
import { AssetCacheItem } from "../util/entities/AssetCacheItem" import express, { Application, Request, Response } from "express";
import fs from "fs";
import fetch, { Headers, Response as FetchResponse } from "node-fetch";
import path from "path";
import { green } from "picocolors"; import { green } from "picocolors";
import ProxyAgent from "proxy-agent";
import { AssetCacheItem } from "../util/entities/AssetCacheItem";
const AssetsPath = path.join(__dirname, "..", "..", "..", "assets") const AssetsPath = path.join(__dirname, "..", "..", "..", "assets");
export default function TestClient(app: Application) { export default function TestClient(app: Application) {
const agent = new ProxyAgent(); const agent = new ProxyAgent();
@ -22,14 +22,13 @@ export default function TestClient(app: Application) {
//load asset cache //load asset cache
let newAssetCache: Map<string, AssetCacheItem> = new Map<string, AssetCacheItem>(); let newAssetCache: Map<string, AssetCacheItem> = new Map<string, AssetCacheItem>();
let assetCacheDir = path.join(AssetsPath, "cache"); let assetCacheDir = path.join(AssetsPath, "cache");
if(process.env.ASSET_CACHE_DIR) if (process.env.ASSET_CACHE_DIR) assetCacheDir = process.env.ASSET_CACHE_DIR;
assetCacheDir = process.env.ASSET_CACHE_DIR
console.log(`[TestClient] ${green(`Using asset cache path: ${assetCacheDir}`)}`) console.log(`[TestClient] ${green(`Using asset cache path: ${assetCacheDir}`)}`);
if(!fs.existsSync(assetCacheDir)) { if (!fs.existsSync(assetCacheDir)) {
fs.mkdirSync(assetCacheDir); fs.mkdirSync(assetCacheDir);
} }
if(fs.existsSync(path.join(assetCacheDir, "index.json"))) { if (fs.existsSync(path.join(assetCacheDir, "index.json"))) {
let rawdata = fs.readFileSync(path.join(assetCacheDir, "index.json")); let rawdata = fs.readFileSync(path.join(assetCacheDir, "index.json"));
newAssetCache = new Map<string, AssetCacheItem>(Object.entries(JSON.parse(rawdata.toString()))); newAssetCache = new Map<string, AssetCacheItem>(Object.entries(JSON.parse(rawdata.toString())));
} }
@ -40,13 +39,12 @@ export default function TestClient(app: Application) {
let response: FetchResponse; let response: FetchResponse;
let buffer: Buffer; let buffer: Buffer;
let assetCacheItem: AssetCacheItem = new AssetCacheItem(req.params.file); let assetCacheItem: AssetCacheItem = new AssetCacheItem(req.params.file);
if(newAssetCache.has(req.params.file)){ if (newAssetCache.has(req.params.file)) {
assetCacheItem = newAssetCache.get(req.params.file)!; assetCacheItem = newAssetCache.get(req.params.file)!;
assetCacheItem.Headers.forEach((value: any, name: any) => { assetCacheItem.Headers.forEach((value: any, name: any) => {
res.set(name, value); res.set(name, value);
}); });
} } else {
else {
console.log(`[TestClient] Downloading file not yet cached! Asset file: ${req.params.file}`); console.log(`[TestClient] Downloading file not yet cached! Asset file: ${req.params.file}`);
response = await fetch(`https://discord.com/assets/${req.params.file}`, { response = await fetch(`https://discord.com/assets/${req.params.file}`, {
agent, agent,
@ -77,7 +75,7 @@ export default function TestClient(app: Application) {
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24); res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
res.set("content-type", "text/html"); res.set("content-type", "text/html");
if(!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.") if (!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.");
res.send(fs.readFileSync(path.join(__dirname, "..", "..", "..", "assets", "developers.html"), { encoding: "utf8" })); res.send(fs.readFileSync(path.join(__dirname, "..", "..", "..", "assets", "developers.html"), { encoding: "utf8" }));
}); });
@ -86,15 +84,13 @@ export default function TestClient(app: Application) {
res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24); res.set("Cache-Control", "public, max-age=" + 60 * 60 * 24);
res.set("content-type", "text/html"); res.set("content-type", "text/html");
if(req.url.startsWith("/api") || req.url.startsWith("/__development")) return; if (req.url.startsWith("/api") || req.url.startsWith("/__development")) return;
if(!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.") if (!useTestClient) return res.send("Test client is disabled on this instance. Use a stand-alone client to connect this instance.");
if (req.url.startsWith("/invite")) return res.send(html.replace("9b2b7f0632acd0c5e781", "9f24f709a3de09b67c49")); if (req.url.startsWith("/invite")) return res.send(html.replace("9b2b7f0632acd0c5e781", "9f24f709a3de09b67c49"));
res.send(html); res.send(html);
}); });
} }
function applyEnv(html: string): string { function applyEnv(html: string): string {
@ -117,23 +113,29 @@ function applyPlugins(html: string): string {
// plugins // plugins
let files = fs.readdirSync(path.join(AssetsPath, "plugins")); let files = fs.readdirSync(path.join(AssetsPath, "plugins"));
let plugins = ""; let plugins = "";
files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script src='/assets/plugins/${x}'></script>\n`; }); files.forEach((x) => {
if (x.endsWith(".js")) plugins += `<script src='/assets/plugins/${x}'></script>\n`;
});
return html.replaceAll("<!-- plugin marker -->", plugins); return html.replaceAll("<!-- plugin marker -->", plugins);
} }
function applyInlinePlugins(html: string): string{ function applyInlinePlugins(html: string): string {
// inline plugins // inline plugins
let files = fs.readdirSync(path.join(AssetsPath, "inline-plugins")); let files = fs.readdirSync(path.join(AssetsPath, "inline-plugins"));
let plugins = ""; let plugins = "";
files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script src='/assets/inline-plugins/${x}'></script>\n\n`; }); files.forEach((x) => {
if (x.endsWith(".js")) plugins += `<script src='/assets/inline-plugins/${x}'></script>\n\n`;
});
return html.replaceAll("<!-- inline plugin marker -->", plugins); return html.replaceAll("<!-- inline plugin marker -->", plugins);
} }
function applyPreloadPlugins(html: string): string{ function applyPreloadPlugins(html: string): string {
//preload plugins //preload plugins
let files = fs.readdirSync(path.join(AssetsPath, "preload-plugins")); let files = fs.readdirSync(path.join(AssetsPath, "preload-plugins"));
let plugins = ""; let plugins = "";
files.forEach(x =>{if(x.endsWith(".js")) plugins += `<script>${fs.readFileSync(path.join(AssetsPath, "preload-plugins", x))}</script>\n`; }); files.forEach((x) => {
if (x.endsWith(".js")) plugins += `<script>${fs.readFileSync(path.join(AssetsPath, "preload-plugins", x))}</script>\n`;
});
return html.replaceAll("<!-- preload plugin marker -->", plugins); return html.replaceAll("<!-- preload plugin marker -->", plugins);
} }
@ -147,7 +149,7 @@ function stripHeaders(headers: Headers): Headers {
"expect-ct", "expect-ct",
"access-control-allow-origin", "access-control-allow-origin",
"content-encoding" "content-encoding"
].forEach(headerName => { ].forEach((headerName) => {
headers.delete(headerName); headers.delete(headerName);
}); });
return headers; return headers;

View File

@ -1,9 +1,9 @@
import { Router } from "express";
import fs from "fs"; import fs from "fs";
import path from "path";
import i18next from "i18next"; import i18next from "i18next";
import i18nextMiddleware from "i18next-http-middleware"; import i18nextMiddleware from "i18next-http-middleware";
import i18nextBackend from "i18next-node-fs-backend"; import i18nextBackend from "i18next-node-fs-backend";
import { Router } from "express"; import path from "path";
export async function initTranslation(router: Router) { export async function initTranslation(router: Router) {
const languages = fs.readdirSync(path.join(__dirname, "..", "..", "..", "assets", "locales")); const languages = fs.readdirSync(path.join(__dirname, "..", "..", "..", "assets", "locales"));

View File

@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
import { getConnection } from "typeorm"; import { getConnection } from "typeorm";
const router = Router(); const router = Router();

View File

@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
import { getConnection } from "typeorm"; import { getConnection } from "typeorm";
const router = Router(); const router = Router();

View File

@ -1,14 +1,14 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Application, Config, FieldErrors, generateToken, OrmUtils, Snowflake, trimSpecial, User, handleFile } from "@fosscord/util"; import { Application, Config, FieldErrors, generateToken, handleFile, OrmUtils, trimSpecial, User } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server"; import { HTTPError } from "lambert-server";
import { verifyToken } from "node-2fa"; import { verifyToken } from "node-2fa";
const router: Router = Router(); const router: Router = Router();
router.post("/", route({}), async (req: Request, res: Response) => { router.post("/", route({}), async (req: Request, res: Response) => {
const app = await Application.findOne({where: {id: req.params.id}}); const app = await Application.findOne({ where: { id: req.params.id } });
if(!app) return res.status(404); if (!app) return res.status(404);
const username = trimSpecial(app.name); const username = trimSpecial(app.name);
const discriminator = await User.generateDiscriminator(username); const discriminator = await User.generateDiscriminator(username);
if (!discriminator) { if (!discriminator) {
@ -16,8 +16,8 @@ router.post("/", route({}), async (req: Request, res: Response) => {
throw FieldErrors({ throw FieldErrors({
username: { username: {
code: "USERNAME_TOO_MANY_USERS", code: "USERNAME_TOO_MANY_USERS",
message: req?.t("auth:register.USERNAME_TOO_MANY_USERS"), message: req?.t("auth:register.USERNAME_TOO_MANY_USERS")
}, }
}); });
} }
@ -47,35 +47,35 @@ router.post("/", route({}), async (req: Request, res: Response) => {
flags: "0", flags: "0",
data: { data: {
hash: null, hash: null,
valid_tokens_since: new Date(), valid_tokens_since: new Date()
}, },
settings: {}, settings: {},
extended_settings: {}, extended_settings: {},
fingerprints: [], fingerprints: [],
notes: {}, notes: {}
}); });
await user.save(); await user.save();
app.bot = user; app.bot = user;
await app.save(); await app.save();
res.send().status(204) res.send().status(204);
}); });
router.post("/reset", route({}), async (req: Request, res: Response) => { router.post("/reset", route({}), async (req: Request, res: Response) => {
let bot = await User.findOne({where: {id: req.params.id}}); let bot = await User.findOne({ where: { id: req.params.id } });
let owner = await User.findOne({where: {id: req.user_id}}); let owner = await User.findOne({ where: { id: req.user_id } });
if(!bot) return res.status(404); if (!bot) return res.status(404);
if(owner?.totp_secret && (!req.body.code || verifyToken(owner.totp_secret, req.body.code))) { if (owner?.totp_secret && (!req.body.code || verifyToken(owner.totp_secret, req.body.code))) {
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008); 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() };
await bot.save(); await bot.save();
let token = await generateToken(bot.id); let token = await generateToken(bot.id);
res.json({token}).status(200); res.json({ token }).status(200);
}); });
router.patch("/", route({}), async (req: Request, res: Response) => { router.patch("/", route({}), async (req: Request, res: Response) => {
if (req.body.avatar) req.body.avatar = await handleFile(`/avatars/${req.params.id}`, req.body.avatar as string); if (req.body.avatar) req.body.avatar = await handleFile(`/avatars/${req.params.id}`, req.body.avatar as string);
let app = OrmUtils.mergeDeep(await User.findOne({where: {id: req.params.id}}), req.body); let app = OrmUtils.mergeDeep(await User.findOne({ where: { id: req.params.id } }), req.body);
await app.save(); await app.save();
res.json(app).status(200); res.json(app).status(200);
}); });

View File

@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,22 +1,22 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util"; import { Application, OrmUtils } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {
let results = await Application.findOne({where: {id: req.params.id}, relations: ["owner", "bot"] }); let results = await Application.findOne({ where: { id: req.params.id }, relations: ["owner", "bot"] });
res.json(results).status(200); res.json(results).status(200);
}); });
router.patch("/", route({}), async (req: Request, res: Response) => { router.patch("/", route({}), async (req: Request, res: Response) => {
delete req.body.icon; delete req.body.icon;
let app = OrmUtils.mergeDeep(await Application.findOne({where: {id: req.params.id}, relations: ["owner", "bot"]}), req.body); let app = OrmUtils.mergeDeep(await Application.findOne({ where: { id: req.params.id }, relations: ["owner", "bot"] }), req.body);
if(app.bot) { if (app.bot) {
app.bot.bio = req.body.description app.bot.bio = req.body.description;
app.bot?.save(); app.bot?.save();
} }
if(req.body.tags) app.tags = req.body.tags; if (req.body.tags) app.tags = req.body.tags;
await app.save(); await app.save();
res.json(app).status(200); res.json(app).status(200);
}); });
@ -26,5 +26,4 @@ router.post("/delete", route({}), async (req: Request, res: Response) => {
res.send().status(200); res.send().status(200);
}); });
export default router; export default router;

View File

@ -1,6 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util"; import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,6 +1,6 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util"; import { Application, OrmUtils, trimSpecial, User } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();
@ -11,14 +11,14 @@ export interface ApplicationCreateSchema {
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {
//TODO //TODO
let results = await Application.find({where: {owner: {id: req.user_id}}, relations: ["owner", "bot"] }); let results = await Application.find({ where: { owner: { id: req.user_id } }, relations: ["owner", "bot"] });
res.json(results).status(200); res.json(results).status(200);
}); });
router.post("/", route({}), async (req: Request, res: Response) => { router.post("/", route({}), async (req: Request, res: Response) => {
const body = req.body as ApplicationCreateSchema; const body = req.body as ApplicationCreateSchema;
const user = await User.findOne({where: {id: req.user_id}}) const user = await User.findOne({ where: { id: req.user_id } });
if(!user) res.status(420); if (!user) res.status(420);
let app = OrmUtils.mergeDeep(new Application(), { let app = OrmUtils.mergeDeep(new Application(), {
name: trimSpecial(body.name), name: trimSpecial(body.name),
description: "", description: "",

View File

@ -1,13 +1,12 @@
import { Router, Request, Response } from "express"; import { getIpAdress, IPAnalysis, route } from "@fosscord/api";
import { route } from "@fosscord/api"; import { Request, Response, Router } from "express";
import { getIpAdress, IPAnalysis } from "@fosscord/api";
const router = Router(); const router = Router();
router.get("/",route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {
//TODO //TODO
//Note: It's most likely related to legal. At the moment Discord hasn't finished this too //Note: It's most likely related to legal. At the moment Discord hasn't finished this too
const country_code = (await IPAnalysis(getIpAdress(req))).country_code; const country_code = (await IPAnalysis(getIpAdress(req))).country_code;
res.json({ consent_required: false, country_code: country_code, promotional_email_opt_in: { required: true, pre_checked: false}}); res.json({ consent_required: false, country_code: country_code, promotional_email_opt_in: { required: true, pre_checked: false } });
}); });
export default router; export default router;

View File

@ -1,7 +1,7 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { adjustEmail, Config, FieldErrors, generateToken, LoginSchema, User } from "@fosscord/util"; import { adjustEmail, Config, FieldErrors, generateToken, LoginSchema, User } from "@fosscord/util";
import crypto from "crypto"; import crypto from "crypto";
import { Request, Response, Router } from "express";
let bcrypt: any; let bcrypt: any;
try { try {
@ -64,9 +64,9 @@ router.post("/", route({ body: "LoginSchema" }), async (req: Request, res: Respo
return res.json({ return res.json({
ticket: ticket, ticket: ticket,
mfa: true, mfa: true,
sms: false, // TODO sms: false, // TODO
token: null, token: null
}) });
} }
const token = await generateToken(user.id); const token = await generateToken(user.id);

View File

@ -1,8 +1,8 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { BackupCode, FieldErrors, generateToken, TotpSchema, User } from "@fosscord/util"; import { BackupCode, generateToken, TotpSchema, User } from "@fosscord/util";
import { verifyToken } from "node-2fa"; import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server"; import { HTTPError } from "lambert-server";
import { verifyToken } from "node-2fa";
const router = Router(); const router = Router();
router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Response) => { router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Response) => {
@ -10,23 +10,17 @@ router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Respon
const user = await User.findOneOrFail({ const user = await User.findOneOrFail({
where: { where: {
totp_last_ticket: ticket, totp_last_ticket: ticket
}, },
select: [ select: ["id", "totp_secret", "settings"]
"id",
"totp_secret",
"settings",
],
}); });
const backup = await BackupCode.findOne({ where: { code: code, expired: false, consumed: false, user: { id: user.id } } }); const backup = await BackupCode.findOne({ where: { code: code, expired: false, consumed: false, user: { id: user.id } } });
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();
} }
@ -35,7 +29,7 @@ router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Respon
return res.json({ return res.json({
token: await generateToken(user.id), token: await generateToken(user.id),
user_settings: user.settings, user_settings: user.settings
}); });
}); });

View File

@ -1,4 +1,4 @@
import { Router, Response, Request } from "express"; import { Router } from "express";
const router: Router = Router(); const router: Router = Router();
// TODO: // TODO:

View File

@ -1,17 +1,16 @@
import { route } from "@fosscord/api";
import { import {
Channel, Channel,
ChannelDeleteEvent, ChannelDeleteEvent,
ChannelPermissionOverwriteType, ChannelModifySchema,
ChannelType, ChannelType,
ChannelUpdateEvent, ChannelUpdateEvent,
emitEvent, emitEvent,
Recipient,
handleFile, handleFile,
ChannelModifySchema OrmUtils,
Recipient
} from "@fosscord/util"; } from "@fosscord/util";
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util";
const router: Router = Router(); const router: Router = Router();
// TODO: delete channel // TODO: delete channel

View File

@ -1,45 +1,45 @@
import { Router, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { random } from "@fosscord/api"; import { Channel, emitEvent, Guild, HTTPError, Invite, InviteCreateEvent, OrmUtils, PublicInviteRelation, User } from "@fosscord/util";
import { Channel, Invite, InviteCreateEvent, emitEvent, User, Guild, PublicInviteRelation } from "@fosscord/util"; import { Request, Response, Router } from "express";
import { isTextChannel } from "./messages"; import { isTextChannel } from "./messages";
import { OrmUtils } from "@fosscord/util";
const router: Router = Router(); const router: Router = Router();
router.post("/", route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT_INVITE", right: "CREATE_INVITES" }), router.post(
async (req: Request, res: Response) => { "/",
const { user_id } = req; route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT_INVITE", right: "CREATE_INVITES" }),
const { channel_id } = req.params; async (req: Request, res: Response) => {
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, select: ["id", "name", "type", "guild_id"] }); const { user_id } = req;
isTextChannel(channel.type); const { channel_id } = req.params;
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, select: ["id", "name", "type", "guild_id"] });
isTextChannel(channel.type);
if (!channel.guild_id) { if (!channel.guild_id) {
throw new HTTPError("This channel doesn't exist", 404); throw new HTTPError("This channel doesn't exist", 404);
}
const { guild_id } = channel;
const expires_at = new Date(req.body.max_age * 1000 + Date.now());
const invite = await OrmUtils.mergeDeep(new Invite(), {
temporary: req.body.temporary || true,
max_uses: req.body.max_uses,
max_age: req.body.max_age,
expires_at,
guild_id,
channel_id,
inviter_id: user_id
}).save();
//TODO: check this, removed toJSON call
const data = JSON.parse(JSON.stringify(invite));
data.inviter = await User.getPublicUser(req.user_id);
data.guild = await Guild.findOne({ where: { id: guild_id } });
data.channel = channel;
await emitEvent({ event: "INVITE_CREATE", data, guild_id } as InviteCreateEvent);
res.status(201).send(data);
} }
const { guild_id } = channel; );
const expires_at = new Date(req.body.max_age * 1000 + Date.now());
const invite = await OrmUtils.mergeDeep(new Invite(),{
temporary: req.body.temporary || true,
max_uses: req.body.max_uses,
max_age: req.body.max_age,
expires_at,
guild_id,
channel_id,
inviter_id: user_id
}).save();
//TODO: check this, removed toJSON call
const data = JSON.parse(JSON.stringify(invite));
data.inviter = await User.getPublicUser(req.user_id);
data.guild = await Guild.findOne({ where: { id: guild_id } });
data.channel = channel;
await emitEvent({ event: "INVITE_CREATE", data, guild_id } as InviteCreateEvent);
res.status(201).send(data);
});
router.get("/", route({ permission: "MANAGE_CHANNELS" }), async (req: Request, res: Response) => { router.get("/", route({ permission: "MANAGE_CHANNELS" }), async (req: Request, res: Response) => {
const { channel_id } = req.params; const { channel_id } = req.params;

View File

@ -1,7 +1,6 @@
import { emitEvent, getPermission, MessageAckEvent, ReadState, Snowflake } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util"; import { emitEvent, getPermission, MessageAckEvent, OrmUtils, ReadState } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,25 +1,22 @@
import { handleMessage, postHandleMessage, route } from "@fosscord/api";
import { import {
Attachment, Attachment,
Channel, Channel,
Embed,
DiscordApiErrors,
emitEvent, emitEvent,
FosscordApiErrors, FosscordApiErrors,
getPermission, getPermission,
getRights, getRights,
Message, HTTPError,
Message,
MessageCreateEvent, MessageCreateEvent,
MessageCreateSchema,
MessageDeleteEvent, MessageDeleteEvent,
MessageUpdateEvent, MessageUpdateEvent,
Snowflake, Snowflake,
uploadFile, uploadFile
MessageCreateSchema
} from "@fosscord/util"; } from "@fosscord/util";
import { Router, Response, Request } from "express"; import { Request, Response, Router } from "express";
import multer from "multer"; import multer from "multer";
import { route } from "@fosscord/api";
import { handleMessage, postHandleMessage } from "@fosscord/api";
import { HTTPError } from "@fosscord/util";
const router = Router(); const router = Router();
// TODO: message content/embed string length limit // TODO: message content/embed string length limit
@ -33,50 +30,53 @@ const messageUpload = multer({
storage: multer.memoryStorage() storage: multer.memoryStorage()
}); // max upload 50 mb }); // max upload 50 mb
router.patch("/", route({ body: "MessageCreateSchema", permission: "SEND_MESSAGES", right: "SEND_MESSAGES" }), async (req: Request, res: Response) => { router.patch(
const { message_id, channel_id } = req.params; "/",
let body = req.body as MessageCreateSchema; route({ body: "MessageCreateSchema", permission: "SEND_MESSAGES", right: "SEND_MESSAGES" }),
async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params;
let body = req.body as MessageCreateSchema;
const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] }); const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, relations: ["attachments"] });
const permissions = await getPermission(req.user_id, undefined, channel_id); const permissions = await getPermission(req.user_id, undefined, channel_id);
const rights = await getRights(req.user_id); const rights = await getRights(req.user_id);
if ((req.user_id !== message.author_id)) { if (req.user_id !== message.author_id) {
if (!rights.has("MANAGE_MESSAGES")) { if (!rights.has("MANAGE_MESSAGES")) {
permissions.hasThrow("MANAGE_MESSAGES"); permissions.hasThrow("MANAGE_MESSAGES");
body = { flags: body.flags }; body = { flags: body.flags };
// guild admins can only suppress embeds of other messages, no such restriction imposed to instance-wide admins // guild admins can only suppress embeds of other messages, no such restriction imposed to instance-wide admins
} }
} else rights.hasThrow("SELF_EDIT_MESSAGES"); } else rights.hasThrow("SELF_EDIT_MESSAGES");
const new_message = await handleMessage({ const new_message = await handleMessage({
...message, ...message,
// TODO: should message_reference be overridable? // TODO: should message_reference be overridable?
// @ts-ignore // @ts-ignore
message_reference: message.message_reference, message_reference: message.message_reference,
...body, ...body,
author_id: message.author_id, author_id: message.author_id,
channel_id,
id: message_id,
edited_timestamp: new Date()
});
await Promise.all([
new_message!.save(),
await emitEvent({
event: "MESSAGE_UPDATE",
channel_id, channel_id,
data: { ...new_message, nonce: undefined } id: message_id,
} as MessageUpdateEvent) edited_timestamp: new Date()
]); });
postHandleMessage(message); await Promise.all([
new_message!.save(),
await emitEvent({
event: "MESSAGE_UPDATE",
channel_id,
data: { ...new_message, nonce: undefined }
} as MessageUpdateEvent)
]);
return res.json(message); postHandleMessage(message);
});
return res.json(message);
}
);
// Backfill message with specific timestamp // Backfill message with specific timestamp
router.put( router.put(
@ -103,13 +103,13 @@ router.put(
throw new HTTPError("Message IDs must be positive integers", 400); throw new HTTPError("Message IDs must be positive integers", 400);
} }
const snowflake = Snowflake.deconstruct(message_id) const snowflake = Snowflake.deconstruct(message_id);
if (Date.now() < snowflake.timestamp) { if (Date.now() < snowflake.timestamp) {
// message is in the future // message is in the future
throw FosscordApiErrors.CANNOT_BACKFILL_TO_THE_FUTURE; throw FosscordApiErrors.CANNOT_BACKFILL_TO_THE_FUTURE;
} }
const exists = await Message.findOne({ where: { id: message_id, channel_id: channel_id }}); const exists = await Message.findOne({ where: { id: message_id, channel_id: channel_id } });
if (exists) { if (exists) {
throw FosscordApiErrors.CANNOT_REPLACE_BY_BACKFILL; throw FosscordApiErrors.CANNOT_REPLACE_BY_BACKFILL;
} }
@ -136,11 +136,11 @@ router.put(
channel_id, channel_id,
attachments, attachments,
edited_timestamp: undefined, edited_timestamp: undefined,
timestamp: new Date(snowflake.timestamp), timestamp: new Date(snowflake.timestamp)
}); });
//Fix for the client bug //Fix for the client bug
delete message.member delete message.member;
await Promise.all([ await Promise.all([
message.save(), message.save(),
@ -148,7 +148,7 @@ router.put(
channel.save() channel.save()
]); ]);
postHandleMessage(message).catch((e) => { }); // no await as it shouldnt block the message send function and silently catch error postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error
return res.json(message); return res.json(message);
} }
@ -174,7 +174,7 @@ router.delete("/", route({}), async (req: Request, res: Response) => {
const rights = await getRights(req.user_id); const rights = await getRights(req.user_id);
if ((message.author_id !== req.user_id)) { if (message.author_id !== req.user_id) {
if (!rights.has("MANAGE_MESSAGES")) { if (!rights.has("MANAGE_MESSAGES")) {
const permission = await getPermission(req.user_id, channel.guild_id, channel_id); const permission = await getPermission(req.user_id, channel.guild_id, channel_id);
permission.hasThrow("MANAGE_MESSAGES"); permission.hasThrow("MANAGE_MESSAGES");

View File

@ -1,8 +1,10 @@
import { route } from "@fosscord/api";
import { import {
Channel, Channel,
emitEvent, emitEvent,
Emoji, Emoji,
getPermission, getPermission,
HTTPError,
Member, Member,
Message, Message,
MessageReactionAddEvent, MessageReactionAddEvent,
@ -13,9 +15,7 @@ import {
PublicUserProjection, PublicUserProjection,
User User
} from "@fosscord/util"; } from "@fosscord/util";
import { route } from "@fosscord/api"; import { Request, Response, Router } from "express";
import { Router, Response, Request } from "express";
import { HTTPError } from "@fosscord/util";
import { In } from "typeorm"; import { In } from "typeorm";
const router = Router(); const router = Router();
@ -101,48 +101,52 @@ router.get("/:emoji", route({ permission: "VIEW_CHANNEL" }), async (req: Request
res.json(users); res.json(users);
}); });
router.put("/:emoji/:user_id", route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }), async (req: Request, res: Response) => { router.put(
const { message_id, channel_id, user_id } = req.params; "/:emoji/:user_id",
if (user_id !== "@me") throw new HTTPError("Invalid user"); route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }),
const emoji = getEmoji(req.params.emoji); async (req: Request, res: Response) => {
const { message_id, channel_id, user_id } = req.params;
if (user_id !== "@me") throw new HTTPError("Invalid user");
const emoji = getEmoji(req.params.emoji);
const channel = await Channel.findOneOrFail({ where: { id: channel_id } }); const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } }); const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } });
const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name); const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
if (!already_added) req.permission!.hasThrow("ADD_REACTIONS"); if (!already_added) req.permission!.hasThrow("ADD_REACTIONS");
if (emoji.id) { if (emoji.id) {
const external_emoji = await Emoji.findOneOrFail({ where: { id: emoji.id } }); const external_emoji = await Emoji.findOneOrFail({ where: { id: emoji.id } });
if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS"); if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS");
emoji.animated = external_emoji.animated; emoji.animated = external_emoji.animated;
emoji.name = external_emoji.name; emoji.name = external_emoji.name;
}
if (already_added) {
if (already_added.user_ids.includes(req.user_id)) return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error
already_added.count++;
} else message.reactions.push({ count: 1, emoji, user_ids: [req.user_id] });
await message.save();
const member = channel.guild_id && (await Member.findOneOrFail({ where: { id: req.user_id } }));
await emitEvent({
event: "MESSAGE_REACTION_ADD",
channel_id,
data: {
user_id: req.user_id,
channel_id,
message_id,
guild_id: channel.guild_id,
emoji,
member
} }
} as MessageReactionAddEvent);
res.sendStatus(204); if (already_added) {
}); if (already_added.user_ids.includes(req.user_id)) return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error
already_added.count++;
} else message.reactions.push({ count: 1, emoji, user_ids: [req.user_id] });
await message.save();
const member = channel.guild_id && (await Member.findOneOrFail({ where: { id: req.user_id } }));
await emitEvent({
event: "MESSAGE_REACTION_ADD",
channel_id,
data: {
user_id: req.user_id,
channel_id,
message_id,
guild_id: channel.guild_id,
emoji,
member
}
} as MessageReactionAddEvent);
res.sendStatus(204);
}
);
router.delete("/:emoji/:user_id", route({}), async (req: Request, res: Response) => { router.delete("/:emoji/:user_id", route({}), async (req: Request, res: Response) => {
let { message_id, channel_id, user_id } = req.params; let { message_id, channel_id, user_id } = req.params;

View File

@ -1,7 +1,6 @@
import { Router, Response, Request } from "express";
import { Channel, Config, emitEvent, getPermission, getRights, MessageDeleteBulkEvent, Message } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Channel, Config, emitEvent, getPermission, getRights, HTTPError, Message, MessageDeleteBulkEvent } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { In } from "typeorm"; import { In } from "typeorm";
const router: Router = Router(); const router: Router = Router();
@ -13,7 +12,7 @@ export default router;
// https://discord.com/developers/docs/resources/channel#bulk-delete-messages // https://discord.com/developers/docs/resources/channel#bulk-delete-messages
router.post("/", route({ body: "BulkDeleteSchema" }), async (req: Request, res: Response) => { router.post("/", route({ body: "BulkDeleteSchema" }), async (req: Request, res: Response) => {
const { channel_id } = req.params; const { channel_id } = req.params;
const channel = await Channel.findOneOrFail({where:{ id: channel_id} }); const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
if (!channel.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400); if (!channel.guild_id) 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);

View File

@ -1,4 +1,4 @@
import { Router, Response, Request } from "express"; import { handleMessage, postHandleMessage, route } from "@fosscord/api";
import { import {
Attachment, Attachment,
Channel, Channel,
@ -7,16 +7,15 @@ import {
DmChannelDTO, DmChannelDTO,
emitEvent, emitEvent,
getPermission, getPermission,
getRights, HTTPError,
Member,
Message, Message,
MessageCreateEvent, MessageCreateEvent,
MessageCreateSchema,
Snowflake, Snowflake,
uploadFile, uploadFile
Member,
MessageCreateSchema
} from "@fosscord/util"; } from "@fosscord/util";
import { HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express";
import { handleMessage, postHandleMessage, route } from "@fosscord/api";
import multer from "multer"; import multer from "multer";
import { FindManyOptions, LessThan, MoreThan } from "typeorm"; import { FindManyOptions, LessThan, MoreThan } from "typeorm";
import { URL } from "url"; import { URL } from "url";
@ -69,23 +68,20 @@ router.get("/", async (req: Request, res: Response) => {
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([]);
let query: FindManyOptions<Message> & { where: { id?: any; }; } = { let query: FindManyOptions<Message> & { where: { id?: any } } = {
order: { id: "DESC" }, order: { id: "DESC" },
take: limit, take: limit,
where: { channel_id }, where: { channel_id },
relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"] relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"]
}; };
if (after) { if (after) {
if (after > new Snowflake()) return res.status(422); if (after > new Snowflake()) return res.status(422);
query.where.id = MoreThan(after); query.where.id = MoreThan(after);
} } else if (before) {
else if (before) {
if (before < req.params.channel_id) return res.status(422); if (before < req.params.channel_id) return res.status(422);
query.where.id = LessThan(before); query.where.id = LessThan(before);
} } else if (around) {
else if (around) {
query.where.id = [ query.where.id = [
MoreThan((BigInt(around) - BigInt(halfLimit)).toString()), MoreThan((BigInt(around) - BigInt(halfLimit)).toString()),
LessThan((BigInt(around) + BigInt(halfLimit)).toString()) LessThan((BigInt(around) + BigInt(halfLimit)).toString())
@ -117,8 +113,7 @@ router.get("/", async (req: Request, res: Response) => {
**/ **/
for (let curr in x) { for (let curr in x) {
if (x[curr] === null) if (x[curr] === null) delete x[curr];
delete x[curr];
} }
return x; return x;
@ -130,7 +125,7 @@ router.get("/", async (req: Request, res: Response) => {
const messageUpload = multer({ const messageUpload = multer({
limits: { limits: {
fileSize: 1024 * 1024 * 100, fileSize: 1024 * 1024 * 100,
fields: 10, fields: 10
// files: 1 // files: 1
}, },
storage: multer.memoryStorage() storage: multer.memoryStorage()
@ -162,16 +157,15 @@ router.post(
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients", "recipients.user"] }); const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients", "recipients.user"] });
if (!channel.isWritable()) { if (!channel.isWritable()) {
throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400) throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400);
} }
const files = req.files as Express.Multer.File[] ?? []; const files = (req.files as Express.Multer.File[]) ?? [];
for (let currFile of files) { for (let currFile of files) {
try { try {
const file: any = await uploadFile(`/attachments/${channel.id}`, currFile); const file: any = await uploadFile(`/attachments/${channel.id}`, currFile);
attachments.push({ ...file, proxy_url: file.url }); attachments.push({ ...file, proxy_url: file.url });
} } catch (error) {
catch (error) {
return res.status(400).json(error); return res.status(400).json(error);
} }
} }
@ -213,10 +207,10 @@ router.post(
); );
} }
//Defining member fields //Defining member fields
var member = await Member.findOneOrFail({ where: { id: req.user_id }, relations: ["roles"] }); var member = await Member.findOneOrFail({ where: { id: req.user_id }, relations: ["roles"] });
// TODO: This doesn't work either // TODO: This doesn't work either
// member.roles = member.roles.filter((role) => { // member.roles = member.roles.filter((role) => {
// return role.id !== role.guild_id; // return role.id !== role.guild_id;
// }).map((role) => { // }).map((role) => {
// return role.id; // return role.id;
@ -233,9 +227,8 @@ router.post(
channel.save() channel.save()
]); ]);
postHandleMessage(message).catch((e) => { }); // no await as it shouldnt block the message send function and silently catch error postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error
return res.json(message); return res.json(message);
} }
); );

View File

@ -1,17 +1,15 @@
import { route } from "@fosscord/api";
import { import {
Channel, Channel,
ChannelPermissionOverwrite, ChannelPermissionOverwrite,
ChannelPermissionOverwriteSchema, ChannelPermissionOverwriteSchema,
ChannelPermissionOverwriteType,
ChannelUpdateEvent, ChannelUpdateEvent,
emitEvent, emitEvent,
getPermission, HTTPError,
Member, Member,
Role Role
} from "@fosscord/util"; } from "@fosscord/util";
import { Router, Response, Request } from "express"; import { Request, Response, Router } from "express";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api";
const router: Router = Router(); const router: Router = Router();
@ -22,7 +20,7 @@ router.put(
const { channel_id, overwrite_id } = req.params; const { channel_id, overwrite_id } = req.params;
const body = req.body as ChannelPermissionOverwriteSchema; const body = req.body as ChannelPermissionOverwriteSchema;
let channel = await Channel.findOneOrFail({ where: {id: channel_id} }); let channel = await Channel.findOneOrFail({ where: { id: channel_id } });
if (!channel.guild_id) throw new HTTPError("Channel not found", 404); if (!channel.guild_id) throw new HTTPError("Channel not found", 404);
if (body.type === 0) { if (body.type === 0) {

View File

@ -1,16 +1,6 @@
import {
Channel,
ChannelPinsUpdateEvent,
Config,
emitEvent,
getPermission,
Message,
MessageUpdateEvent,
DiscordApiErrors
} from "@fosscord/util";
import { Router, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Channel, ChannelPinsUpdateEvent, Config, DiscordApiErrors, emitEvent, Message, MessageUpdateEvent } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,10 +1,18 @@
import { HTTPError, PurgeSchema } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import {
Channel,
Config,
emitEvent,
getPermission,
getRights,
HTTPError,
Message,
MessageDeleteBulkEvent,
PurgeSchema
} from "@fosscord/util";
import { Request, Response, Router } from "express";
import { Between, FindManyOptions, In, Not } from "typeorm";
import { isTextChannel } from "./messages"; import { isTextChannel } from "./messages";
import { FindManyOptions, Between, Not } from "typeorm";
import { Channel, Config, emitEvent, getPermission, getRights, Message, MessageDeleteBulkEvent } from "@fosscord/util";
import { Router, Response, Request } from "express";
import { In } from "typeorm";
const router: Router = Router(); const router: Router = Router();
@ -13,7 +21,12 @@ export default router;
/** /**
TODO: apply the delete bit by bit to prevent client and database stress TODO: apply the delete bit by bit to prevent client and database stress
**/ **/
router.post("/",route({ /*body: "PurgeSchema",*/ }), async (req: Request, res: Response) => { router.post(
"/",
route({
/*body: "PurgeSchema",*/
}),
async (req: Request, res: Response) => {
const { channel_id } = req.params; const { channel_id } = req.params;
const channel = await Channel.findOneOrFail({ where: { id: channel_id } }); const channel = await Channel.findOneOrFail({ where: { id: channel_id } });

View File

@ -1,4 +1,4 @@
import { Request, Response, Router } from "express"; import { route } from "@fosscord/api";
import { import {
Channel, Channel,
ChannelRecipientAddEvent, ChannelRecipientAddEvent,
@ -6,12 +6,12 @@ import {
DiscordApiErrors, DiscordApiErrors,
DmChannelDTO, DmChannelDTO,
emitEvent, emitEvent,
OrmUtils,
PublicUserProjection, PublicUserProjection,
Recipient, Recipient,
User User
} from "@fosscord/util"; } from "@fosscord/util";
import { route } from "@fosscord/api"; import { Request, Response, Router } from "express";
import { OrmUtils } from "@fosscord/util";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,6 +1,6 @@
import { Channel, emitEvent, Member, TypingStartEvent } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Router, Request, Response } from "express"; import { Channel, emitEvent, Member, TypingStartEvent } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,9 +1,7 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Channel, Config, getPermission, trimSpecial, Webhook } from "@fosscord/util"; import { Channel, Config, DiscordApiErrors, HTTPError, trimSpecial, Webhook } from "@fosscord/util";
import { HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express";
import { isTextChannel } from "./messages/index"; import { isTextChannel } from "./messages/index";
import { DiscordApiErrors } from "@fosscord/util";
const router: Router = Router(); const router: Router = Router();
//TODO: implement webhooks //TODO: implement webhooks

View File

@ -1,8 +1,8 @@
import { Guild, Config } from "@fosscord/util"; import { Config, Guild } from "@fosscord/util";
import { Router, Request, Response } from "express"; import { Request, Response, Router } from "express";
import { route } from "..";
import { Like } from "typeorm"; import { Like } from "typeorm";
import { route } from "..";
const router = Router(); const router = Router();

View File

@ -1,5 +1,5 @@
import { Categories } from "@fosscord/util"; import { Categories } from "@fosscord/util";
import { Router, Response, Request } from "express"; import { Request, Response, Router } from "express";
import { route } from ".."; import { route } from "..";
const router = Router(); const router = Router();
@ -10,7 +10,7 @@ router.get("/categories", route({}), async (req: Request, res: Response) => {
const { locale, primary_only } = req.query; const { locale, primary_only } = req.query;
const out = primary_only ? await Categories.find() : await Categories.find({ where: {is_primary: true} }); const out = primary_only ? await Categories.find() : await Categories.find({ where: { is_primary: true } });
res.send(out); res.send(out);
}); });

View File

@ -1,6 +1,6 @@
import { Router, Response, Request } from "express"; import { Config, Release } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { route } from ".."; import { route } from "..";
import { Release, Config } from "@fosscord/util";
const router = Router(); const router = Router();
@ -10,7 +10,7 @@ router.get("/:branch", route({}), async (req: Request, res: Response) => {
const { platform } = req.query; const { platform } = req.query;
//TODO //TODO
if(!platform || !["linux", "osx", "win"].includes(platform.toString())) return res.status(404) if (!platform || !["linux", "osx", "win"].includes(platform.toString())) return res.status(404);
const release = await Release.findOneOrFail({ where: { name: client.releases.upstreamVersion } }); const release = await Release.findOneOrFail({ where: { name: client.releases.upstreamVersion } });

View File

@ -1,11 +1,11 @@
import { Router, Response, Request } from "express"; import { Request, Response, Router } from "express";
import { route } from ".."; import { route } from "..";
const router = Router(); const router = Router();
router.get("/", route({}), (req: Request, res: Response) => { router.get("/", route({}), (req: Request, res: Response) => {
// TODO: // TODO:
res.send({ fingerprint: "", assignments: [], guild_experiments:[] }); res.send({ fingerprint: "", assignments: [], guild_experiments: [] });
}); });
export default router; export default router;

View File

@ -1,6 +1,6 @@
import { Config } from "@fosscord/util";
import { Router, Response, Request } from "express";
import { route, RouteOptions } from "@fosscord/api"; import { route, RouteOptions } from "@fosscord/api";
import { Config } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,6 +1,6 @@
import { Config } from "@fosscord/util";
import { Router, Response, Request } from "express";
import { route, RouteOptions } from "@fosscord/api"; import { route, RouteOptions } from "@fosscord/api";
import { Config } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,7 +1,7 @@
import { Router, Response, Request } from "express";
import fetch from "node-fetch";
import ProxyAgent from 'proxy-agent';
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
import fetch from "node-fetch";
import ProxyAgent from "proxy-agent";
import { getGifApiKey, parseGifResult } from "./trending"; import { getGifApiKey, parseGifResult } from "./trending";
const router = Router(); const router = Router();
@ -20,7 +20,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
headers: { "Content-Type": "application/json" } headers: { "Content-Type": "application/json" }
}); });
const { results } = await response.json() as any; const { results } = (await response.json()) as any;
res.json(results.map(parseGifResult)).status(200); res.json(results.map(parseGifResult)).status(200);
}); });

View File

@ -1,7 +1,7 @@
import { Router, Response, Request } from "express";
import fetch from "node-fetch";
import ProxyAgent from 'proxy-agent';
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
import fetch from "node-fetch";
import ProxyAgent from "proxy-agent";
import { getGifApiKey, parseGifResult } from "./trending"; import { getGifApiKey, parseGifResult } from "./trending";
const router = Router(); const router = Router();
@ -20,7 +20,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
headers: { "Content-Type": "application/json" } headers: { "Content-Type": "application/json" }
}); });
const { results } = await response.json() as any; const { results } = (await response.json()) as any;
res.json(results.map(parseGifResult)).status(200); res.json(results.map(parseGifResult)).status(200);
}); });

View File

@ -1,9 +1,8 @@
import { Router, Response, Request } from "express";
import fetch from "node-fetch";
import ProxyAgent from 'proxy-agent';
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Config } from "@fosscord/util"; import { Config, HTTPError } from "@fosscord/util";
import { HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express";
import fetch from "node-fetch";
import ProxyAgent from "proxy-agent";
const router = Router(); const router = Router();
@ -50,8 +49,8 @@ router.get("/", route({}), async (req: Request, res: Response) => {
}) })
]); ]);
const { tags } = await responseSource.json() as any; const { tags } = (await responseSource.json()) as any;
const { results } = await trendGifSource.json() as any; const { results } = (await trendGifSource.json()) as any;
res.json({ res.json({
categories: tags.map((x: any) => ({ name: x.searchterm, src: x.image })), categories: tags.map((x: any) => ({ name: x.searchterm, src: x.image })),

View File

@ -1,8 +1,8 @@
import { Guild, Config } from "@fosscord/util"; import { Config, Guild } from "@fosscord/util";
import { Router, Request, Response } from "express"; import { Request, Response, Router } from "express";
import { Like } from "typeorm";
import { route } from ".."; import { route } from "..";
import {Like} from "typeorm"
const router = Router(); const router = Router();
@ -13,12 +13,12 @@ router.get("/", route({}), async (req: Request, res: Response) => {
// TODO: implement this with default typeorm query // TODO: implement this with default typeorm query
// const guilds = await Guild.find({ where: { features: "DISCOVERABLE" } }); //, take: Math.abs(Number(limit)) }); // const guilds = await Guild.find({ where: { features: "DISCOVERABLE" } }); //, take: Math.abs(Number(limit)) });
const genLoadId = (size: Number) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join(''); const genLoadId = (size: Number) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join("");
const guilds = showAllGuilds const guilds = showAllGuilds
? await Guild.find({ take: Math.abs(Number(limit || 24)) }) ? await Guild.find({ take: Math.abs(Number(limit || 24)) })
: await Guild.find({ where: { features: Like('%DISCOVERABLE%') }, take: Math.abs(Number(limit || 24)) }); : await Guild.find({ where: { features: Like("%DISCOVERABLE%") }, take: Math.abs(Number(limit || 24)) });
res.send({ recommended_guilds: guilds, load_id: `server_recs/${genLoadId(32)}`}).status(200); res.send({ recommended_guilds: guilds, load_id: `server_recs/${genLoadId(32)}` }).status(200);
}); });
export default router; export default router;

View File

@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
//TODO: implement audit logs //TODO: implement audit logs

View File

@ -1,8 +1,18 @@
import { Request, Response, Router } from "express";
import { DiscordApiErrors, emitEvent, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, Guild, Ban, User, Member, BanRegistrySchema, BanModeratorSchema } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { getIpAdress, route } from "@fosscord/api"; import { getIpAdress, route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util"; import {
Ban,
BanModeratorSchema,
BanRegistrySchema,
DiscordApiErrors,
emitEvent,
GuildBanAddEvent,
GuildBanRemoveEvent,
HTTPError,
Member,
OrmUtils,
User
} from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();
@ -44,7 +54,7 @@ router.get("/:user", route({ permission: "BAN_MEMBERS" }), async (req: Request,
const { guild_id } = req.params; const { guild_id } = req.params;
const user_id = req.params.ban; const user_id = req.params.ban;
let ban = await Ban.findOneOrFail({ where: { guild_id, user_id } }) as BanRegistrySchema; let ban = (await Ban.findOneOrFail({ where: { guild_id, user_id } })) as BanRegistrySchema;
if (ban.user_id === ban.executor_id) throw DiscordApiErrors.UNKNOWN_BAN; if (ban.user_id === ban.executor_id) throw DiscordApiErrors.UNKNOWN_BAN;
// pretend self-bans don't exist to prevent victim chasing // pretend self-bans don't exist to prevent victim chasing
@ -53,7 +63,7 @@ router.get("/:user", route({ permission: "BAN_MEMBERS" }), async (req: Request,
ban = ban as BanModeratorSchema; ban = ban as BanModeratorSchema;
delete ban.ip delete ban.ip;
return res.json(ban); return res.json(ban);
}); });
@ -62,14 +72,14 @@ router.put("/:user_id", route({ body: "BanCreateSchema", permission: "BAN_MEMBER
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 ( (req.user_id === banned_user_id) && (banned_user_id === req.permission!.cache.guild?.owner_id)) if (req.user_id === banned_user_id && banned_user_id === req.permission!.cache.guild?.owner_id)
throw new HTTPError("You are the guild owner, hence can't ban yourself", 403); throw new HTTPError("You are the guild owner, hence can't ban yourself", 403);
if (req.permission!.cache.guild?.owner_id === banned_user_id) throw new HTTPError("You can't ban the owner", 400); if (req.permission!.cache.guild?.owner_id === banned_user_id) throw new HTTPError("You can't ban the owner", 400);
const banned_user = await User.getPublicUser(banned_user_id); const banned_user = await User.getPublicUser(banned_user_id);
const ban = OrmUtils.mergeDeep(new Ban(),{ const ban = OrmUtils.mergeDeep(new Ban(), {
user_id: banned_user_id, user_id: banned_user_id,
guild_id: guild_id, guild_id: guild_id,
ip: getIpAdress(req), ip: getIpAdress(req),
@ -93,7 +103,7 @@ router.put("/:user_id", route({ body: "BanCreateSchema", permission: "BAN_MEMBER
return res.json(ban); return res.json(ban);
}); });
router.put("/@me", route({ body: "BanCreateSchema"}), async (req: Request, res: Response) => { router.put("/@me", route({ body: "BanCreateSchema" }), async (req: Request, res: Response) => {
const { guild_id } = req.params; const { guild_id } = req.params;
const banned_user = await User.getPublicUser(req.params.user_id); const banned_user = await User.getPublicUser(req.params.user_id);

View File

@ -1,7 +1,6 @@
import { Router, Response, Request } from "express";
import { Channel, ChannelUpdateEvent, getPermission, emitEvent, ChannelModifySchema, ChannelReorderSchema } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Channel, ChannelModifySchema, ChannelReorderSchema, ChannelUpdateEvent, emitEvent, HTTPError } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {

View File

@ -1,7 +1,6 @@
import { Channel, emitEvent, GuildDeleteEvent, Guild, Member, Message, Role, Invite, Emoji } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { emitEvent, Guild, GuildDeleteEvent, HTTPError } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,38 +1,36 @@
import { Guild, Config } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {
const { guild_id } = req.params; const { guild_id } = req.params;
// TODO: // TODO:
// Load from database // Load from database
// Admin control, but for now it allows anyone to be discoverable // Admin control, but for now it allows anyone to be discoverable
res.send({ res.send({
guild_id: guild_id, guild_id: guild_id,
safe_environment: true, safe_environment: true,
healthy: true, healthy: true,
health_score_pending: false, health_score_pending: false,
size: true, size: true,
nsfw_properties: {}, nsfw_properties: {},
protected: true, protected: true,
sufficient: true, sufficient: true,
sufficient_without_grace_period: true, sufficient_without_grace_period: true,
valid_rules_channel: true, valid_rules_channel: true,
retention_healthy: true, retention_healthy: true,
engagement_healthy: true, engagement_healthy: true,
age: true, age: true,
minimum_age: 0, minimum_age: 0,
health_score: { health_score: {
avg_nonnew_participators: 0, avg_nonnew_participators: 0,
avg_nonnew_communicators: 0, avg_nonnew_communicators: 0,
num_intentful_joiners: 0, num_intentful_joiners: 0,
perc_ret_w1_intentful: 0 perc_ret_w1_intentful: 0
}, },
minimum_size: 0 minimum_size: 0
}); });
}); });

View File

@ -1,7 +1,19 @@
import { Router, Request, Response } from "express";
import { Config, DiscordApiErrors, emitEvent, Emoji, EmojiCreateSchema, EmojiModifySchema, GuildEmojisUpdateEvent, handleFile, Member, Snowflake, User } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util"; import {
Config,
DiscordApiErrors,
emitEvent,
Emoji,
EmojiCreateSchema,
EmojiModifySchema,
GuildEmojisUpdateEvent,
handleFile,
Member,
OrmUtils,
Snowflake,
User
} from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,8 +1,18 @@
import { Request, Response, Router } from "express";
import { DiscordApiErrors, emitEvent, getPermission, getRights, Guild, GuildUpdateEvent, GuildUpdateSchema, handleFile, Member } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util"; import {
DiscordApiErrors,
emitEvent,
getPermission,
getRights,
Guild,
GuildUpdateEvent,
GuildUpdateSchema,
handleFile,
HTTPError,
Member,
OrmUtils
} from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
@ -21,15 +31,14 @@ router.get("/", route({}), async (req: Request, res: Response) => {
return res.send(guild); return res.send(guild);
}); });
router.patch("/", route({ body: "GuildUpdateSchema"}), async (req: Request, res: Response) => { router.patch("/", route({ body: "GuildUpdateSchema" }), async (req: Request, res: Response) => {
const body = req.body as GuildUpdateSchema; const body = req.body as GuildUpdateSchema;
const { guild_id } = req.params; const { guild_id } = req.params;
const rights = await getRights(req.user_id); const rights = await getRights(req.user_id);
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("MANAGE_GUILD"); throw DiscordApiErrors.MISSING_PERMISSIONS.withParams("MANAGE_GUILD");
// TODO: guild update check image // TODO: guild update check image

View File

@ -1,7 +1,5 @@
import { Router, Response, Request } from "express";
import { Channel, ChannelUpdateEvent, getPermission, emitEvent } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
//TODO: implement integrations list //TODO: implement integrations list

View File

@ -1,5 +1,5 @@
import { getPermission, Invite, PublicInviteRelation } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Invite, PublicInviteRelation } from "@fosscord/util";
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,8 +1,18 @@
import { Request, Response, Router } from "express";
import { Member, getPermission, getRights, Role, GuildMemberUpdateEvent, emitEvent, Sticker, Emoji, Rights, Guild, MemberChangeSchema } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util"; import {
emitEvent,
Emoji,
getPermission,
getRights,
Guild,
GuildMemberUpdateEvent,
Member,
MemberChangeSchema,
OrmUtils,
Role,
Sticker
} from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
@ -46,7 +56,6 @@ router.patch("/", route({ body: "MemberChangeSchema" }), async (req: Request, re
}); });
router.put("/", route({}), async (req: Request, res: Response) => { router.put("/", route({}), async (req: Request, res: Response) => {
// TODO: Lurker mode // TODO: Lurker mode
const rights = await getRights(req.user_id); const rights = await getRights(req.user_id);

View File

@ -1,5 +1,5 @@
import { getPermission, Member, PermissionResolvable } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { getPermission, Member, PermissionResolvable } from "@fosscord/util";
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,5 +1,5 @@
import { getPermission, Member } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Member } from "@fosscord/util";
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,8 +1,7 @@
import { Request, Response, Router } from "express";
import { Guild, Member, PublicMemberProjection } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { HTTPError, Member, PublicMemberProjection } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { MoreThan } from "typeorm"; import { MoreThan } from "typeorm";
import { HTTPError } from "@fosscord/util";
const router = Router(); const router = Router();

View File

@ -1,5 +1,5 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
router.get("/subscriptions", route({}), async (req: Request, res: Response) => { router.get("/subscriptions", route({}), async (req: Request, res: Response) => {

View File

@ -1,7 +1,7 @@
import { Router, Request, Response } from "express";
import { Guild, Member, Snowflake } from "@fosscord/util";
import { LessThan, IsNull } from "typeorm";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Guild, Member, Snowflake } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { IsNull, LessThan } from "typeorm";
const router = Router(); const router = Router();
//Returns all inactive members, respecting role hierarchy //Returns all inactive members, respecting role hierarchy

View File

@ -1,7 +1,6 @@
import { Config, Guild, Member } from "@fosscord/util"; import { getIpAdress, getVoiceRegions, route } from "@fosscord/api";
import { Guild } from "@fosscord/util";
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
import { getVoiceRegions, route } from "@fosscord/api";
import { getIpAdress } from "@fosscord/api";
const router = Router(); const router = Router();

View File

@ -1,8 +1,16 @@
import { Router, Request, Response } from "express";
import { Role, Member, GuildRoleUpdateEvent, GuildRoleDeleteEvent, emitEvent, handleFile, RoleModifySchema } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { HTTPError } from "@fosscord/util"; import {
import { OrmUtils } from "@fosscord/util"; emitEvent,
GuildRoleDeleteEvent,
GuildRoleUpdateEvent,
handleFile,
HTTPError,
Member,
OrmUtils,
Role,
RoleModifySchema
} from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,21 +1,18 @@
import { Request, Response, Router } from "express"; import { route } from "@fosscord/api";
import { import {
Role,
getPermission,
Member,
GuildRoleCreateEvent,
GuildRoleUpdateEvent,
GuildRoleDeleteEvent,
emitEvent,
Config, Config,
DiscordApiErrors, DiscordApiErrors,
handleFile, emitEvent,
getPermission,
GuildRoleCreateEvent,
GuildRoleUpdateEvent,
Member,
OrmUtils,
Role,
RoleModifySchema, RoleModifySchema,
RolePositionUpdateSchema RolePositionUpdateSchema
} from "@fosscord/util"; } from "@fosscord/util";
import { HTTPError } from "@fosscord/util"; import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util";
const router: Router = Router(); const router: Router = Router();
@ -38,7 +35,7 @@ router.post("/", route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" })
if (role_count > maxRoles) throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles); if (role_count > maxRoles) throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles);
let role: Role = OrmUtils.mergeDeep(new Role(),{ let role: Role = OrmUtils.mergeDeep(new Role(), {
// values before ...body are default and can be overriden // values before ...body are default and can be overriden
position: 0, position: 0,
hoist: false, hoist: false,

View File

@ -1,20 +1,19 @@
import { route } from "@fosscord/api";
import { import {
emitEvent, emitEvent,
GuildStickersUpdateEvent, GuildStickersUpdateEvent,
handleFile, HTTPError,
Member, Member,
ModifyGuildStickerSchema, ModifyGuildStickerSchema,
OrmUtils,
Snowflake, Snowflake,
Sticker, Sticker,
StickerFormatType, StickerFormatType,
StickerType, StickerType,
uploadFile uploadFile
} from "@fosscord/util"; } from "@fosscord/util";
import { Router, Request, Response } from "express"; import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import multer from "multer"; import multer from "multer";
import { HTTPError } from "@fosscord/util";
import { OrmUtils } from "@fosscord/util";
const router = Router(); const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {

View File

@ -1,9 +1,6 @@
import { generateCode, route } from "@fosscord/api";
import { Guild, HTTPError, OrmUtils, Template } from "@fosscord/util";
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
import { Guild, Template } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api";
import { generateCode } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util";
const router: Router = Router(); const router: Router = Router();
@ -75,7 +72,12 @@ router.patch("/:code", route({ body: "TemplateModifySchema", permission: "MANAGE
const { code, guild_id } = req.params; const { code, guild_id } = req.params;
const { name, description } = req.body; const { name, description } = req.body;
const template = await OrmUtils.mergeDeep(new Template(), { code, name: name, description: description, source_guild_id: guild_id }).save(); const template = await OrmUtils.mergeDeep(new Template(), {
code,
name: name,
description: description,
source_guild_id: guild_id
}).save();
res.json(template); res.json(template);
}); });

View File

@ -1,8 +1,6 @@
import { Channel, ChannelType, getPermission, Guild, Invite, trimSpecial, VanityUrlSchema } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { HTTPError } from "@fosscord/util"; import { Channel, ChannelType, Guild, HTTPError, Invite, OrmUtils, VanityUrlSchema } from "@fosscord/util";
import { OrmUtils } from "@fosscord/util"; import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,7 +1,16 @@
import { Channel, ChannelType, DiscordApiErrors, emitEvent, getPermission, VoiceState, VoiceStateUpdateEvent, VoiceStateUpdateSchema } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import {
Channel,
ChannelType,
DiscordApiErrors,
emitEvent,
getPermission,
OrmUtils,
VoiceState,
VoiceStateUpdateEvent,
VoiceStateUpdateSchema
} from "@fosscord/util";
import { Request, Response, Router } from "express"; import { Request, Response, Router } from "express";
import { OrmUtils } from "@fosscord/util";
const router = Router(); const router = Router();
router.patch("/", route({ body: "VoiceStateUpdateSchema" }), async (req: Request, res: Response) => { router.patch("/", route({ body: "VoiceStateUpdateSchema" }), async (req: Request, res: Response) => {

View File

@ -1,7 +1,5 @@
import { Router, Response, Request } from "express";
import { Channel, ChannelUpdateEvent, getPermission, emitEvent } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
//TODO: implement webhooks //TODO: implement webhooks

View File

@ -1,7 +1,6 @@
import { Request, Response, Router } from "express";
import { Guild, getPermission, Snowflake, Member, GuildUpdateWelcomeScreenSchema } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Guild, GuildUpdateWelcomeScreenSchema, HTTPError, Member } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,8 +1,6 @@
import { Request, Response, Router } from "express";
import { Config, Permissions, Guild, Invite, Channel, Member } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { random, route } from "@fosscord/api"; import { random, route } from "@fosscord/api";
import { OrmUtils } from "@fosscord/util"; import { Channel, Guild, HTTPError, Invite, Member, OrmUtils, Permissions } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,7 +1,6 @@
import { Request, Response, Router } from "express";
import { Guild } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Guild, HTTPError } from "@fosscord/util";
import { Request, Response, Router } from "express";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";

View File

@ -1,6 +1,6 @@
import { Request, Response, Router } from "express";
import { Guild, WidgetModifySchema } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Guild, WidgetModifySchema } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,6 +1,6 @@
import { Router, Request, Response } from "express";
import { Role, Guild, Snowflake, Config, getRights, Member, Channel, DiscordApiErrors, handleFile, GuildCreateSchema } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Config, DiscordApiErrors, getRights, Guild, GuildCreateSchema, Member } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();
@ -12,7 +12,7 @@ router.post("/", route({ body: "GuildCreateSchema", right: "CREATE_GUILDS" }), a
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 } });
const rights = await getRights(req.user_id); const rights = await getRights(req.user_id);
if ((guild_count >= maxGuilds)&&!rights.has("MANAGE_GUILDS")) { if (guild_count >= maxGuilds && !rights.has("MANAGE_GUILDS")) {
throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds); throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
} }

View File

@ -1,6 +1,6 @@
import { Request, Response, Router } from "express";
import { Template, Guild, Role, Snowflake, Config, User, Member, DiscordApiErrors, OrmUtils, GuildTemplateCreateSchema } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Config, DiscordApiErrors, Guild, GuildTemplateCreateSchema, Member, OrmUtils, Role, Snowflake, Template } from "@fosscord/util";
import { Request, Response, Router } from "express";
import fetch from "node-fetch"; import fetch from "node-fetch";
const router: Router = Router(); const router: Router = Router();
@ -11,7 +11,8 @@ router.get("/:code", route({}), async (req: Request, res: Response) => {
const { code } = req.params; const { code } = req.params;
if (code.startsWith("discord:")) { if (code.startsWith("discord:")) {
if (!allowDiscordTemplates) return res.json({ code: 403, message: "Discord templates cannot be used on this instance." }).sendStatus(403); if (!allowDiscordTemplates)
return res.json({ code: 403, message: "Discord templates cannot be used on this instance." }).sendStatus(403);
const discordTemplateID = code.split("discord:", 2)[1]; const discordTemplateID = code.split("discord:", 2)[1];
const discordTemplateData = await fetch(`https://discord.com/api/v9/guilds/templates/${discordTemplateID}`, { const discordTemplateData = await fetch(`https://discord.com/api/v9/guilds/templates/${discordTemplateID}`, {
@ -22,7 +23,7 @@ router.get("/:code", route({}), async (req: Request, res: Response) => {
} }
if (code.startsWith("external:")) { if (code.startsWith("external:")) {
if (!allowRaws) return res.json({ code: 403, message: "Importing raws is disabled on this instance." }).sendStatus(403); if (!allowRaws) return res.json({ code: 403, message: "Importing raws is disabled on this instance." }).sendStatus(403);
return res.json(code.split("external:", 2)[1]); return res.json(code.split("external:", 2)[1]);
} }
@ -57,18 +58,20 @@ router.post("/:code", route({ body: "GuildTemplateCreateSchema" }), async (req:
id: guild_id, id: guild_id,
owner_id: req.user_id owner_id: req.user_id
}).save(), }).save(),
(OrmUtils.mergeDeep(new Role(), { (
id: guild_id, OrmUtils.mergeDeep(new Role(), {
guild_id: guild_id, id: guild_id,
color: 0, guild_id: guild_id,
hoist: false, color: 0,
managed: true, hoist: false,
mentionable: true, managed: true,
name: "@everyone", mentionable: true,
permissions: BigInt("2251804225"), name: "@everyone",
position: 0, permissions: BigInt("2251804225"),
tags: null position: 0,
}) as Role).save() tags: null
}) as Role
).save()
]); ]);
await Member.addToGuild(req.user_id, guild_id); await Member.addToGuild(req.user_id, guild_id);

View File

@ -1,7 +1,6 @@
import { Router, Request, Response } from "express";
import { emitEvent, getPermission, Guild, Invite, InviteDeleteEvent, User, PublicInviteRelation } from "@fosscord/util";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { HTTPError } from "@fosscord/util"; import { emitEvent, getPermission, Guild, HTTPError, Invite, InviteDeleteEvent, PublicInviteRelation, User } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();
@ -13,14 +12,15 @@ router.get("/:code", route({}), async (req: Request, res: Response) => {
res.status(200).send(invite); res.status(200).send(invite);
}); });
router.post("/:code", route({right: "USE_MASS_INVITES"}), async (req: Request, res: Response) => { router.post("/:code", route({ right: "USE_MASS_INVITES" }), async (req: Request, res: Response) => {
const { code } = req.params; const { code } = req.params;
const { guild_id } = await Invite.findOneOrFail({ where: { code } }) const { guild_id } = await Invite.findOneOrFail({ where: { code } });
const { features } = await Guild.findOneOrFail({ where: { id: guild_id} }); const { features } = await Guild.findOneOrFail({ where: { id: guild_id } });
const { public_flags } = await User.findOneOrFail({ where: { id: req.user_id } }); const { public_flags } = await User.findOneOrFail({ where: { id: req.user_id } });
if(features.includes("INTERNAL_EMPLOYEE_ONLY") && (public_flags & 1) !== 1) throw new HTTPError("Only intended for the staff of this server.", 401); if (features.includes("INTERNAL_EMPLOYEE_ONLY") && (public_flags & 1) !== 1)
if(features.includes("INVITES_CLOSED")) 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_CLOSED")) 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);

View File

@ -1,5 +1,5 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,39 +1,36 @@
import { Guild, Config } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {
const { guild_id } = req.params; const { guild_id } = req.params;
// TODO: // TODO:
// Load from database // Load from database
// Admin control, but for now it allows anyone to be discoverable // Admin control, but for now it allows anyone to be discoverable
res.send({ res.send({
guild_id: guild_id, guild_id: guild_id,
safe_environment: true, safe_environment: true,
healthy: true, healthy: true,
health_score_pending: false, health_score_pending: false,
size: true, size: true,
nsfw_properties: {}, nsfw_properties: {},
protected: true, protected: true,
sufficient: true, sufficient: true,
sufficient_without_grace_period: true, sufficient_without_grace_period: true,
valid_rules_channel: true, valid_rules_channel: true,
retention_healthy: true, retention_healthy: true,
engagement_healthy: true, engagement_healthy: true,
age: true, age: true,
minimum_age: 0, minimum_age: 0,
health_score: { health_score: {
avg_nonnew_participators: 0, avg_nonnew_participators: 0,
avg_nonnew_communicators: 0, avg_nonnew_communicators: 0,
num_intentful_joiners: 0, num_intentful_joiners: 0,
perc_ret_w1_intentful: 0 perc_ret_w1_intentful: 0
}, },
minimum_size: 0 minimum_size: 0
}); });
}); });

View File

@ -1,6 +1,6 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Config } from "@fosscord/util"; import { Config } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
@ -18,8 +18,8 @@ router.get("/", route({}), (req: Request, res: Response) => {
correspondenceUserID: general.correspondenceUserID, correspondenceUserID: general.correspondenceUserID,
frontPage: general.frontPage, frontPage: general.frontPage,
tosPage: general.tosPage, tosPage: general.tosPage
}, }
}); });
}); });

View File

@ -1,16 +1,15 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Config } from "@fosscord/util"; import { Config } from "@fosscord/util";
import { config } from "dotenv" import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
router.get("/",route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {
const { cdn, gateway } = Config.get(); const { cdn, gateway } = Config.get();
const IdentityForm = { const IdentityForm = {
cdn: cdn.endpointPublic || process.env.CDN || "http://localhost:3001", cdn: cdn.endpointPublic || process.env.CDN || "http://localhost:3001",
gateway: gateway.endpointPublic || process.env.GATEWAY || "ws://localhost:3002" gateway: gateway.endpointPublic || process.env.GATEWAY || "ws://localhost:3002"
}; };
res.json(IdentityForm); res.json(IdentityForm);
}); });

View File

@ -1,10 +1,9 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Config } from "@fosscord/util"; import { Config } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => {
router.get("/",route({}), async (req: Request, res: Response) => {
const { general } = Config.get(); const { general } = Config.get();
res.json(general); res.json(general);
}); });

View File

@ -1,9 +1,9 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Config } from "@fosscord/util"; import { Config } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
router.get("/",route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {
const { limits } = Config.get(); const { limits } = Config.get();
res.json(limits); res.json(limits);
}); });

View File

@ -1,12 +1,12 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
router.get("/scheduled-maintenances/upcoming.json",route({}), async (req: Request, res: Response) => { router.get("/scheduled-maintenances/upcoming.json", route({}), async (req: Request, res: Response) => {
res.json({ res.json({
"page": {}, page: {},
"scheduled_maintenances": {} scheduled_maintenances: {}
}); });
}); });
export default router; export default router;

View File

@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,6 +1,6 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { StickerPack } from "@fosscord/util"; import { StickerPack } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,6 +1,6 @@
import { Sticker } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Sticker } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => { router.get("/", route({}), async (req: Request, res: Response) => {

View File

@ -1,22 +1,21 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { User } from "@fosscord/util"; import { User } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();
router.post("/", route({}), async (req: Request, res: Response) => { router.post("/", route({}), async (req: Request, res: Response) => {
//EXPERIMENTAL: have an "OPERATOR" platform permission implemented for this API route //EXPERIMENTAL: have an "OPERATOR" platform permission implemented for this API route
const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["rights"] }); const user = await User.findOneOrFail({ where: { id: req.user_id }, select: ["rights"] });
if((Number(user.rights) << Number(0))%Number(2)==Number(1)) { if ((Number(user.rights) << Number(0)) % Number(2) == Number(1)) {
console.log("user that POSTed to the API was ALLOWED"); console.log("user that POSTed to the API was ALLOWED");
console.log(user.rights); console.log(user.rights);
res.sendStatus(200) res.sendStatus(200);
process.kill(process.pid, 'SIGTERM') process.kill(process.pid, "SIGTERM");
} } else {
else {
console.log("operation failed"); console.log("operation failed");
console.log(user.rights); console.log(user.rights);
res.sendStatus(403) res.sendStatus(403);
} }
}); });

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router: Router = Router(); const router: Router = Router();

View File

@ -1,5 +1,5 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api"; import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router(); const router = Router();

Some files were not shown because too many files have changed in this diff Show More