1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-11-22 10:22:39 +01: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
contact_links:
- name: Fosscord Documentation
url: https://docs.fosscord.com/
about: Need documentation and examples for the Fosscord? Head over to Fosscord's official documentation.
- name: Discord's Developer Documentation
url: https://discord.com/developers/docs/intro
about: Need help with the Discord resources? Head here instead of asking on Fosscord!
- name: Fosscord' Official Discord server
url: https://discord.com/invite/Ms5Ev7S6bF
about: Need help with the server? Talk with us in our official server.
- name: Fosscord Documentation
url: https://docs.fosscord.com/
about: Need documentation and examples for the Fosscord? Head over to Fosscord's official documentation.
- name: Discord's Developer Documentation
url: https://discord.com/developers/docs/intro
about: Need help with the Discord resources? Head here instead of asking on Fosscord!
- name: Fosscord' Official Discord server
url: https://discord.com/invite/Ms5Ev7S6bF
about: Need help with the server? Talk with us in our official server.

View File

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

28
.vscode/launch.json vendored
View File

@ -1,17 +1,13 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${file}",
"outFiles": [
"${workspaceFolder}/**/*.js"
]
}
]
}
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": ["<node_internals>/**"],
"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",
"patch-package": "^6.4.7",
"picocolors": "^1.0.0",
"prettier": "^2.7.1",
"proxy-agent": "^5.0.0",
"reflect-metadata": "^0.1.13",
"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: [] };
let rights = [];
process.on('SIGINT', function() {
console.log("Caught interrupt signal");
process.exit();
process.on("SIGINT", function () {
console.log("Caught interrupt signal");
process.exit();
});
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();
if(fs.existsSync("package-lock.json")) fs.rmSync("package-lock.json");
if(fs.existsSync("yarn.lock")) fs.rmSync("yarn.lock");
if (fs.existsSync("package-lock.json")) fs.rmSync("package-lock.json");
if (fs.existsSync("yarn.lock")) fs.rmSync("yarn.lock");
async function main() {
printTitle("Step 1: Database setup");
@ -82,7 +82,7 @@ async function main() {
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(`PORT=${data.port}`);
data.env.push('THREADS=1')
data.env.push("THREADS=1");
printTitle("Step 4: Default rights");
console.log("Please enter the default rights for new users.");
@ -126,8 +126,9 @@ async function main() {
};
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?/.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?/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");
printTitle("Step 6: finalizing...");
//save
@ -140,13 +141,13 @@ async function main() {
console.log(" ==> Ensuring yarn is up to date (v3, not v1)...");
execIn("npx yarn set version stable", process.cwd());
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(', ')}...`);
execIn(`npx --yes yarn add -O ${data.extra_pkgs.join(' ')}`, process.cwd(), {stdio: "inherit"});
console.log(` ==> Installing extra packages: ${data.extra_pkgs.join(", ")}...`);
execIn(`npx --yes yarn add -O ${data.extra_pkgs.join(" ")}`, process.cwd(), { stdio: "inherit" });
console.log('==> Building...')
execIn('npx --yes yarn run build', process.cwd(), {stdio: "inherit"});
console.log("==> Building...");
execIn("npx --yes yarn run build", process.cwd(), { stdio: "inherit" });
printTitle("Step 6: run your instance!");
console.log("Installation is complete!");
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("uncaughtException", console.error);
import http from "http";
import * as Api from "@fosscord/api";
import * as Gateway from "@fosscord/gateway";
import { CDNServer } from "@fosscord/cdn";
import express from "express";
import { green, bold, yellow } from "picocolors";
import * as Gateway from "@fosscord/gateway";
import { Config, getOrInitialiseDatabase } from "@fosscord/util";
import * as Sentry from "@sentry/node";
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";
const app = express();
@ -26,12 +26,12 @@ const cdn = new CDNServer({ server, port, production, app });
const gateway = new Gateway.Server({ server, port, production });
//this is what has been added for the /stop API route
process.on('SIGTERM', () => {
setTimeout(()=>process.exit(0), 3000)
process.on("SIGTERM", () => {
setTimeout(() => process.exit(0), 3000);
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
async function main() {
@ -42,16 +42,15 @@ async function main() {
await Config.set({
cdn: {
endpointClient: "${location.host}",
endpointPrivate: `http://localhost:${port}`,
endpointPrivate: `http://localhost:${port}`
},
gateway: {
endpointClient:
'${location.protocol === "https:" ? "wss://" : "ws://"}${location.host}',
endpointClient: '${location.protocol === "https:" ? "wss://" : "ws://"}${location.host}',
endpointPrivate: `ws://localhost:${port}`,
...(!Config.get().gateway.endpointPublic && {
endpointPublic: `ws://localhost:${port}`,
}),
},
endpointPublic: `ws://localhost:${port}`
})
}
// regions: {
// default: "fosscord",
// useDefaultAsOptimal: true,
@ -70,15 +69,10 @@ async function main() {
//Sentry
if (Config.get().sentry.enabled) {
console.log(
`[Bundle] ${yellow("You are using Sentry! This may slightly impact performance on large loads!")}`
);
console.log(`[Bundle] ${yellow("You are using Sentry! This may slightly impact performance on large loads!")}`);
Sentry.init({
dsn: Config.get().sentry.endpoint,
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
new Tracing.Integrations.Express({ app }),
],
integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })],
tracesSampleRate: Config.get().sentry.traceSampleRate,
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 { ErrorHandler } from "./middlewares/ErrorHandler";
import { BodyParser } from "./middlewares/BodyParser";
import { Router, Request, Response, NextFunction } from "express";
import { NextFunction, Request, Response, Router } from "express";
import { Server, ServerOptions } from "lambert-server";
import morgan from "morgan";
import path from "path";
import { red } from "picocolors";
import { Authentication, CORS } from "./middlewares/";
import { BodyParser } from "./middlewares/BodyParser";
import { ErrorHandler } from "./middlewares/ErrorHandler";
import { initRateLimits } from "./middlewares/RateLimit";
import TestClient from "./middlewares/TestClient";
import { initTranslation } from "./middlewares/Translation";
import morgan from "morgan";
import { initInstance } from "./util/handlers/Instance";
import { red } from "picocolors"
export interface FosscordServerOptions extends ServerOptions {}
@ -85,8 +85,13 @@ export class FosscordServer extends Server {
this.app.use(ErrorHandler);
TestClient(this.app);
if (logRequests) console.log(red(`Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!`));
if (logRequests)
console.log(
red(
`Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!`
)
);
return super.start();
}
}
}

View File

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

View File

@ -1,6 +1,5 @@
import { checkToken, Config, HTTPError, Rights } from "@fosscord/util";
import { NextFunction, Request, Response } from "express";
import { HTTPError } from "@fosscord/util";
import { checkToken, Config, Rights } from "@fosscord/util";
export const NO_AUTHORIZATION_ROUTES = [
// Authentication routes
@ -10,7 +9,7 @@ export const NO_AUTHORIZATION_ROUTES = [
"/auth/mfa/totp",
// Routes with a seperate auth system
"/webhooks/",
// Public information endpoints
// Public information endpoints
"/ping",
"/gateway",
"/experiments",

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,5 @@
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
import { Application, OrmUtils, Team, trimSpecial, User } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
@ -8,4 +7,4 @@ router.get("/", route({}), async (req: Request, res: Response) => {
res.json([]).status(200);
});
export default router;
export default router;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 { OrmUtils } from "@fosscord/util";
import { emitEvent, getPermission, MessageAckEvent, OrmUtils, ReadState } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();

View File

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

View File

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

View File

@ -1,8 +1,10 @@
import { route } from "@fosscord/api";
import {
Channel,
emitEvent,
Emoji,
getPermission,
HTTPError,
Member,
Message,
MessageReactionAddEvent,
@ -13,9 +15,7 @@ import {
PublicUserProjection,
User
} from "@fosscord/util";
import { route } from "@fosscord/api";
import { Router, Response, Request } from "express";
import { HTTPError } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { In } from "typeorm";
const router = Router();
@ -101,48 +101,52 @@ router.get("/:emoji", route({ permission: "VIEW_CHANNEL" }), async (req: Request
res.json(users);
});
router.put("/:emoji/:user_id", route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }), async (req: Request, res: Response) => {
const { message_id, channel_id, user_id } = req.params;
if (user_id !== "@me") throw new HTTPError("Invalid user");
const emoji = getEmoji(req.params.emoji);
router.put(
"/:emoji/:user_id",
route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }),
async (req: Request, res: Response) => {
const { message_id, channel_id, user_id } = req.params;
if (user_id !== "@me") throw new HTTPError("Invalid user");
const emoji = getEmoji(req.params.emoji);
const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } });
const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
const message = await Message.findOneOrFail({ where: { id: message_id, channel_id } });
const already_added = message.reactions.find((x) => (x.emoji.id === emoji.id && emoji.id) || x.emoji.name === emoji.name);
if (!already_added) req.permission!.hasThrow("ADD_REACTIONS");
if (!already_added) req.permission!.hasThrow("ADD_REACTIONS");
if (emoji.id) {
const external_emoji = await Emoji.findOneOrFail({ where: { id: emoji.id } });
if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS");
emoji.animated = external_emoji.animated;
emoji.name = external_emoji.name;
}
if (already_added) {
if (already_added.user_ids.includes(req.user_id)) return res.sendStatus(204); // Do not throw an error ¯\_(ツ)_/¯ as discord also doesn't throw any error
already_added.count++;
} else message.reactions.push({ count: 1, emoji, user_ids: [req.user_id] });
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
if (emoji.id) {
const external_emoji = await Emoji.findOneOrFail({ where: { id: emoji.id } });
if (!already_added) req.permission!.hasThrow("USE_EXTERNAL_EMOJIS");
emoji.animated = external_emoji.animated;
emoji.name = external_emoji.name;
}
} 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) => {
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 { Channel, Config, emitEvent, getPermission, getRights, HTTPError, Message, MessageDeleteBulkEvent } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { In } from "typeorm";
const router: Router = Router();
@ -13,7 +12,7 @@ export default router;
// https://discord.com/developers/docs/resources/channel#bulk-delete-messages
router.post("/", route({ body: "BulkDeleteSchema" }), async (req: Request, res: Response) => {
const { channel_id } = req.params;
const channel = await Channel.findOneOrFail({where:{ id: channel_id} });
const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
if (!channel.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400);
const rights = await getRights(req.user_id);

View File

@ -1,4 +1,4 @@
import { Router, Response, Request } from "express";
import { handleMessage, postHandleMessage, route } from "@fosscord/api";
import {
Attachment,
Channel,
@ -7,16 +7,15 @@ import {
DmChannelDTO,
emitEvent,
getPermission,
getRights,
HTTPError,
Member,
Message,
MessageCreateEvent,
MessageCreateSchema,
Snowflake,
uploadFile,
Member,
MessageCreateSchema
uploadFile
} from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { handleMessage, postHandleMessage, route } from "@fosscord/api";
import { Request, Response, Router } from "express";
import multer from "multer";
import { FindManyOptions, LessThan, MoreThan } from "typeorm";
import { URL } from "url";
@ -69,23 +68,20 @@ router.get("/", async (req: Request, res: Response) => {
permissions.hasThrow("VIEW_CHANNEL");
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
let query: FindManyOptions<Message> & { where: { id?: any; }; } = {
let query: FindManyOptions<Message> & { where: { id?: any } } = {
order: { id: "DESC" },
take: limit,
where: { channel_id },
relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"]
};
if (after) {
if (after > new Snowflake()) return res.status(422);
query.where.id = MoreThan(after);
}
else if (before) {
} else if (before) {
if (before < req.params.channel_id) return res.status(422);
query.where.id = LessThan(before);
}
else if (around) {
} else if (around) {
query.where.id = [
MoreThan((BigInt(around) - BigInt(halfLimit)).toString()),
LessThan((BigInt(around) + BigInt(halfLimit)).toString())
@ -110,15 +106,14 @@ router.get("/", async (req: Request, res: Response) => {
const uri = y.proxy_url.startsWith("http") ? y.proxy_url : `https://example.org${y.proxy_url}`;
y.proxy_url = `${endpoint == null ? "" : endpoint}${new URL(uri).pathname}`;
});
/**
Some clients ( discord.js ) only check if a property exists within the response,
which causes erorrs when, say, the `application` property is `null`.
**/
for (let curr in x) {
if (x[curr] === null)
delete x[curr];
if (x[curr] === null) delete x[curr];
}
return x;
@ -130,7 +125,7 @@ router.get("/", async (req: Request, res: Response) => {
const messageUpload = multer({
limits: {
fileSize: 1024 * 1024 * 100,
fields: 10,
fields: 10
// files: 1
},
storage: multer.memoryStorage()
@ -162,16 +157,15 @@ router.post(
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients", "recipients.user"] });
if (!channel.isWritable()) {
throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400)
throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400);
}
const files = req.files as Express.Multer.File[] ?? [];
const files = (req.files as Express.Multer.File[]) ?? [];
for (let currFile of files) {
try {
const file: any = await uploadFile(`/attachments/${channel.id}`, currFile);
attachments.push({ ...file, proxy_url: file.url });
}
catch (error) {
} catch (error) {
return res.status(400).json(error);
}
}
@ -212,11 +206,11 @@ router.post(
})
);
}
//Defining member fields
//Defining member fields
var member = await Member.findOneOrFail({ where: { id: req.user_id }, relations: ["roles"] });
// TODO: This doesn't work either
// member.roles = member.roles.filter((role) => {
// member.roles = member.roles.filter((role) => {
// return role.id !== role.guild_id;
// }).map((role) => {
// return role.id;
@ -225,7 +219,7 @@ router.post(
// TODO: Figure this out
// delete message.member.last_message_id;
// delete message.member.index;
await Promise.all([
message.save(),
emitEvent({ event: "MESSAGE_CREATE", channel_id: channel_id, data: message } as MessageCreateEvent),
@ -233,9 +227,8 @@ router.post(
channel.save()
]);
postHandleMessage(message).catch((e) => { }); // no await as it shouldnt block the message send function and silently catch error
postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error
return res.json(message);
}
);

View File

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

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 { Channel, ChannelPinsUpdateEvent, Config, DiscordApiErrors, emitEvent, Message, MessageUpdateEvent } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();

View File

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

View File

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

View File

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

View File

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

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 { route } from "..";
import { Request, Response, Router } from "express";
import { Like } from "typeorm";
import { route } from "..";
const router = Router();

View File

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

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

View File

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

View File

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

View File

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

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 { Request, Response, Router } from "express";
import fetch from "node-fetch";
import ProxyAgent from "proxy-agent";
import { getGifApiKey, parseGifResult } from "./trending";
const router = Router();
@ -11,7 +11,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
const { q, media_format, locale } = req.query;
const apiKey = getGifApiKey();
const agent = new ProxyAgent();
const response = await fetch(`https://g.tenor.com/v1/search?q=${q}&media_format=${media_format}&locale=${locale}&key=${apiKey}`, {
@ -20,7 +20,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
headers: { "Content-Type": "application/json" }
});
const { results } = await response.json() as any;
const { results } = (await response.json()) as any;
res.json(results.map(parseGifResult)).status(200);
});

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 { Request, Response, Router } from "express";
import fetch from "node-fetch";
import ProxyAgent from "proxy-agent";
import { getGifApiKey, parseGifResult } from "./trending";
const router = Router();
@ -11,7 +11,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
const { media_format, locale } = req.query;
const apiKey = getGifApiKey();
const agent = new ProxyAgent();
const response = await fetch(`https://g.tenor.com/v1/trending?media_format=${media_format}&locale=${locale}&key=${apiKey}`, {
@ -20,7 +20,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
headers: { "Content-Type": "application/json" }
});
const { results } = await response.json() as any;
const { results } = (await response.json()) as any;
res.json(results.map(parseGifResult)).status(200);
});

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 { Config } from "@fosscord/util";
import { HTTPError } from "@fosscord/util";
import { Config, HTTPError } from "@fosscord/util";
import { Request, Response, Router } from "express";
import fetch from "node-fetch";
import ProxyAgent from "proxy-agent";
const router = Router();
@ -34,7 +33,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
const { media_format, locale } = req.query;
const apiKey = getGifApiKey();
const agent = new ProxyAgent();
const [responseSource, trendGifSource] = await Promise.all([
@ -50,8 +49,8 @@ router.get("/", route({}), async (req: Request, res: Response) => {
})
]);
const { tags } = await responseSource.json() as any;
const { results } = await trendGifSource.json() as any;
const { tags } = (await responseSource.json()) as any;
const { results } = (await trendGifSource.json()) as any;
res.json({
categories: tags.map((x: any) => ({ name: x.searchterm, src: x.image })),

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

View File

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

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

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 { Channel, ChannelModifySchema, ChannelReorderSchema, ChannelUpdateEvent, emitEvent, HTTPError } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => {

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

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 { 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();

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

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 { Request, Response, Router } from "express";
const router = Router();
//TODO: implement integrations list

View File

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

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 { OrmUtils } from "@fosscord/util";
import {
emitEvent,
Emoji,
getPermission,
getRights,
Guild,
GuildMemberUpdateEvent,
Member,
MemberChangeSchema,
OrmUtils,
Role,
Sticker
} from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();
@ -46,7 +56,6 @@ router.patch("/", route({ body: "MemberChangeSchema" }), async (req: Request, re
});
router.put("/", route({}), async (req: Request, res: Response) => {
// TODO: Lurker mode
const rights = await getRights(req.user_id);
@ -56,7 +65,7 @@ router.put("/", route({}), async (req: Request, res: Response) => {
member_id = req.user_id;
rights.hasThrow("JOIN_GUILDS");
} else {
// TODO: join others by controller
// TODO: join others by controller
}
let guild = await Guild.findOneOrFail({

View File

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

View File

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

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 { HTTPError, Member, PublicMemberProjection } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { MoreThan } from "typeorm";
import { HTTPError } from "@fosscord/util";
const router = Router();

View File

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

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 { Guild, Member, Snowflake } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { IsNull, LessThan } from "typeorm";
const router = Router();
//Returns all inactive members, respecting role hierarchy

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 { getVoiceRegions, route } from "@fosscord/api";
import { getIpAdress } from "@fosscord/api";
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 { HTTPError } from "@fosscord/util";
import { OrmUtils } from "@fosscord/util";
import {
emitEvent,
GuildRoleDeleteEvent,
GuildRoleUpdateEvent,
handleFile,
HTTPError,
Member,
OrmUtils,
Role,
RoleModifySchema
} from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();

View File

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

View File

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

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

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 { HTTPError } from "@fosscord/util";
import { OrmUtils } from "@fosscord/util";
import { Channel, ChannelType, Guild, HTTPError, Invite, OrmUtils, VanityUrlSchema } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router = Router();

View File

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

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 { Request, Response, Router } from "express";
const router = Router();
//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 { Guild, GuildUpdateWelcomeScreenSchema, HTTPError, Member } from "@fosscord/util";
import { Request, Response, Router } from "express";
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 { 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();

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 { Guild, HTTPError } from "@fosscord/util";
import { Request, Response, Router } from "express";
import fs from "fs";
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 { Guild, WidgetModifySchema } from "@fosscord/util";
import { Request, Response, Router } from "express";
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 { Config, DiscordApiErrors, getRights, Guild, GuildCreateSchema, Member } from "@fosscord/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
@ -12,7 +12,7 @@ router.post("/", route({ body: "GuildCreateSchema", right: "CREATE_GUILDS" }), a
const { maxGuilds } = Config.get().limits.user;
const guild_count = await Member.count({ where: { id: req.user_id } });
const rights = await getRights(req.user_id);
if ((guild_count >= maxGuilds)&&!rights.has("MANAGE_GUILDS")) {
if (guild_count >= maxGuilds && !rights.has("MANAGE_GUILDS")) {
throw DiscordApiErrors.MAXIMUM_GUILDS.withParams(maxGuilds);
}

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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