summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--api/assets/schemas.json255
-rw-r--r--api/client_test/index.html2
-rw-r--r--api/src/middlewares/Authentication.ts4
-rw-r--r--api/src/middlewares/ErrorHandler.ts7
-rw-r--r--api/src/routes/discoverable-guilds.ts2
-rw-r--r--api/src/routes/guilds/#guild_id/integrations.ts10
-rw-r--r--api/src/routes/template.ts.disabled2
-rw-r--r--api/src/routes/webhooks/#webhook_id/index.ts89
-rw-r--r--api/src/util/route.ts8
-rw-r--r--util/src/entities/Webhook.ts6
-rw-r--r--util/src/util/Regex.ts2
11 files changed, 18 insertions, 369 deletions
diff --git a/api/assets/schemas.json b/api/assets/schemas.json
index 88558cfa..9c34f968 100644
--- a/api/assets/schemas.json
+++ b/api/assets/schemas.json
@@ -1770,6 +1770,10 @@
             }
         },
         "additionalProperties": false,
+        "required": [
+            "avatar",
+            "name"
+        ],
         "definitions": {
             "ChannelType": {
                 "enum": [
@@ -7442,256 +7446,5 @@
             }
         },
         "$schema": "http://json-schema.org/draft-07/schema#"
-    },
-    "WebhookModifySchema": {
-        "type": "object",
-        "properties": {
-            "name": {
-                "type": "string"
-            },
-            "avatar": {
-                "type": "string"
-            }
-        },
-        "additionalProperties": false,
-        "definitions": {
-            "ChannelType": {
-                "enum": [
-                    0,
-                    1,
-                    10,
-                    11,
-                    12,
-                    13,
-                    2,
-                    3,
-                    4,
-                    5,
-                    6
-                ],
-                "type": "number"
-            },
-            "ChannelPermissionOverwriteType": {
-                "enum": [
-                    0,
-                    1
-                ],
-                "type": "number"
-            },
-            "Embed": {
-                "type": "object",
-                "properties": {
-                    "title": {
-                        "type": "string"
-                    },
-                    "type": {
-                        "enum": [
-                            "article",
-                            "gifv",
-                            "image",
-                            "link",
-                            "rich",
-                            "video"
-                        ],
-                        "type": "string"
-                    },
-                    "description": {
-                        "type": "string"
-                    },
-                    "url": {
-                        "type": "string"
-                    },
-                    "timestamp": {
-                        "type": "string",
-                        "format": "date-time"
-                    },
-                    "color": {
-                        "type": "integer"
-                    },
-                    "footer": {
-                        "type": "object",
-                        "properties": {
-                            "text": {
-                                "type": "string"
-                            },
-                            "icon_url": {
-                                "type": "string"
-                            },
-                            "proxy_icon_url": {
-                                "type": "string"
-                            }
-                        },
-                        "additionalProperties": false,
-                        "required": [
-                            "text"
-                        ]
-                    },
-                    "image": {
-                        "$ref": "#/definitions/EmbedImage"
-                    },
-                    "thumbnail": {
-                        "$ref": "#/definitions/EmbedImage"
-                    },
-                    "video": {
-                        "$ref": "#/definitions/EmbedImage"
-                    },
-                    "provider": {
-                        "type": "object",
-                        "properties": {
-                            "name": {
-                                "type": "string"
-                            },
-                            "url": {
-                                "type": "string"
-                            }
-                        },
-                        "additionalProperties": false
-                    },
-                    "author": {
-                        "type": "object",
-                        "properties": {
-                            "name": {
-                                "type": "string"
-                            },
-                            "url": {
-                                "type": "string"
-                            },
-                            "icon_url": {
-                                "type": "string"
-                            },
-                            "proxy_icon_url": {
-                                "type": "string"
-                            }
-                        },
-                        "additionalProperties": false
-                    },
-                    "fields": {
-                        "type": "array",
-                        "items": {
-                            "type": "object",
-                            "properties": {
-                                "name": {
-                                    "type": "string"
-                                },
-                                "value": {
-                                    "type": "string"
-                                },
-                                "inline": {
-                                    "type": "boolean"
-                                }
-                            },
-                            "additionalProperties": false,
-                            "required": [
-                                "name",
-                                "value"
-                            ]
-                        }
-                    }
-                },
-                "additionalProperties": false
-            },
-            "EmbedImage": {
-                "type": "object",
-                "properties": {
-                    "url": {
-                        "type": "string"
-                    },
-                    "proxy_url": {
-                        "type": "string"
-                    },
-                    "height": {
-                        "type": "integer"
-                    },
-                    "width": {
-                        "type": "integer"
-                    }
-                },
-                "additionalProperties": false
-            },
-            "ChannelModifySchema": {
-                "type": "object",
-                "properties": {
-                    "name": {
-                        "maxLength": 100,
-                        "type": "string"
-                    },
-                    "type": {
-                        "$ref": "#/definitions/ChannelType"
-                    },
-                    "topic": {
-                        "type": "string"
-                    },
-                    "bitrate": {
-                        "type": "integer"
-                    },
-                    "user_limit": {
-                        "type": "integer"
-                    },
-                    "rate_limit_per_user": {
-                        "type": "integer"
-                    },
-                    "position": {
-                        "type": "integer"
-                    },
-                    "permission_overwrites": {
-                        "type": "array",
-                        "items": {
-                            "type": "object",
-                            "properties": {
-                                "id": {
-                                    "type": "string"
-                                },
-                                "type": {
-                                    "$ref": "#/definitions/ChannelPermissionOverwriteType"
-                                },
-                                "allow": {
-                                    "type": "bigint"
-                                },
-                                "deny": {
-                                    "type": "bigint"
-                                }
-                            },
-                            "additionalProperties": false,
-                            "required": [
-                                "allow",
-                                "deny",
-                                "id",
-                                "type"
-                            ]
-                        }
-                    },
-                    "parent_id": {
-                        "type": "string"
-                    },
-                    "id": {
-                        "type": "string"
-                    },
-                    "nsfw": {
-                        "type": "boolean"
-                    },
-                    "rtc_region": {
-                        "type": "string"
-                    },
-                    "default_auto_archive_duration": {
-                        "type": "integer"
-                    }
-                },
-                "additionalProperties": false,
-                "required": [
-                    "name",
-                    "type"
-                ]
-            },
-            "RelationshipType": {
-                "enum": [
-                    1,
-                    2,
-                    3,
-                    4
-                ],
-                "type": "number"
-            }
-        },
-        "$schema": "http://json-schema.org/draft-07/schema#"
     }
 }
\ No newline at end of file
diff --git a/api/client_test/index.html b/api/client_test/index.html
index 335b477c..ac66df06 100644
--- a/api/client_test/index.html
+++ b/api/client_test/index.html
@@ -11,7 +11,7 @@
 			window.__OVERLAY__ = /overlay/.test(location.pathname);
 			window.__BILLING_STANDALONE__ = /^\/billing/.test(location.pathname);
 			window.GLOBAL_ENV = {
-				API_ENDPOINT: `//${location.host}/api`,
+				API_ENDPOINT: "/api",
 				API_VERSION: 9,
 				GATEWAY_ENDPOINT: `${location.protocol === "https:" ? "wss://" : "ws://"}${location.hostname}:3002`,
 				WEBAPP_ENDPOINT: "",
diff --git a/api/src/middlewares/Authentication.ts b/api/src/middlewares/Authentication.ts
index 32307f42..a300c786 100644
--- a/api/src/middlewares/Authentication.ts
+++ b/api/src/middlewares/Authentication.ts
@@ -5,11 +5,11 @@ import { checkToken, Config } from "@fosscord/util";
 export const NO_AUTHORIZATION_ROUTES = [
 	"/auth/login",
 	"/auth/register",
+	"/webhooks/",
 	"/ping",
 	"/gateway",
 	"/experiments",
-	/\/guilds\/\d+\/widget\.(json|png)/,
-	/\/webhooks\/\d+\/\w+/ // only exclude webhook calls with webhook token
+	/\/guilds\/\d+\/widget\.(json|png)/
 ];
 
 export const API_PREFIX = /^\/api(\/v\d+)?/;
diff --git a/api/src/middlewares/ErrorHandler.ts b/api/src/middlewares/ErrorHandler.ts
index 338da8d5..d288f3fb 100644
--- a/api/src/middlewares/ErrorHandler.ts
+++ b/api/src/middlewares/ErrorHandler.ts
@@ -1,10 +1,9 @@
 import { NextFunction, Request, Response } from "express";
 import { HTTPError } from "lambert-server";
+import { EntityNotFoundError } from "typeorm";
 import { FieldError } from "@fosscord/api";
 import { ApiError } from "@fosscord/util";
 
-const EntityNotFoundErrorRegex = /"(\w+)"/;
-
 export function ErrorHandler(error: Error, req: Request, res: Response, next: NextFunction) {
 	if (!error) return next();
 
@@ -19,8 +18,8 @@ export function ErrorHandler(error: Error, req: Request, res: Response, next: Ne
 			code = error.code;
 			message = error.message;
 			httpcode = error.httpStatus;
-		} else if (error.name === "EntityNotFoundError") {
-			message = `${error.message.match(EntityNotFoundErrorRegex)?.[1] || "Item"} could not be found`;
+		} else if (error instanceof EntityNotFoundError) {
+			message = `${(error as any).stringifyTarget || "Item"} could not be found`;
 			code = 404;
 		} else if (error instanceof FieldError) {
 			code = Number(error.code);
diff --git a/api/src/routes/discoverable-guilds.ts b/api/src/routes/discoverable-guilds.ts
index 71789123..f667eb2a 100644
--- a/api/src/routes/discoverable-guilds.ts
+++ b/api/src/routes/discoverable-guilds.ts
@@ -10,7 +10,7 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 	// ! this only works using SQL querys
 	// 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" LIKE 'COMMUNITY'`, take: Math.abs(Number(limit) || 50) });
+	const guilds = await Guild.find({ where: `"features" LIKE 'COMMUNITY'`, take: Math.abs(Number(limit)) });
 	res.send({ guilds: guilds });
 });
 
diff --git a/api/src/routes/guilds/#guild_id/integrations.ts b/api/src/routes/guilds/#guild_id/integrations.ts
deleted file mode 100644
index f6b8e99d..00000000
--- a/api/src/routes/guilds/#guild_id/integrations.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { route } from "@fosscord/api";
-import { Router, Request, Response } from "express";
-const router = Router();
-
-router.get("/", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
-	// TODO: integrations (followed channels, youtube, twitch)
-	res.send([]);
-});
-
-export default router;
diff --git a/api/src/routes/template.ts.disabled b/api/src/routes/template.ts.disabled
index 524e981b..ad785f10 100644
--- a/api/src/routes/template.ts.disabled
+++ b/api/src/routes/template.ts.disabled
@@ -4,7 +4,7 @@ import { Router, Request, Response } from "express";
 const router = Router();
 
 router.get("/", async (req: Request, res: Response) => {
-	res.json({});
+	res.send({});
 });
 
 export default router;
diff --git a/api/src/routes/webhooks/#webhook_id/index.ts b/api/src/routes/webhooks/#webhook_id/index.ts
deleted file mode 100644
index e9b40ebf..00000000
--- a/api/src/routes/webhooks/#webhook_id/index.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import { Channel, Config, emitEvent, JWTOptions, Webhook, WebhooksUpdateEvent } from "@fosscord/util";
-import { route, Authentication, handleFile } from "@fosscord/api";
-import { Router, Request, Response, NextFunction } from "express";
-import jwt from "jsonwebtoken";
-import { HTTPError } from "lambert-server";
-const router = Router();
-
-export interface WebhookModifySchema {
-	name?: string;
-	avatar?: string;
-	// channel_id?: string; // TODO
-}
-
-function validateWebhookToken(req: Request, res: Response, next: NextFunction) {
-	const { jwtSecret } = Config.get().security;
-
-	jwt.verify(req.params.token, jwtSecret, JWTOptions, async (err, decoded: any) => {
-		if (err) return next(new HTTPError("Invalid Token", 401));
-		next();
-	});
-}
-
-router.get("/", route({}), async (req: Request, res: Response) => {
-	res.json(await Webhook.findOneOrFail({ id: req.params.webhook_id }));
-});
-
-router.get("/:token", route({}), validateWebhookToken, async (req: Request, res: Response) => {
-	res.json(await Webhook.findOneOrFail({ id: req.params.webhook_id }));
-});
-
-router.patch("/", route({ body: "WebhookModifySchema", permission: "MANAGE_WEBHOOKS" }), (req: Request, res: Response) => {
-	return updateWebhook(req, res);
-});
-
-router.patch("/:token", route({ body: "WebhookModifySchema" }), validateWebhookToken, (req: Request, res: Response) => {
-	return updateWebhook(req, res);
-});
-
-async function updateWebhook(req: Request, res: Response) {
-	const webhook = await Webhook.findOneOrFail({ id: req.params.webhook_id });
-	if (req.body.channel_id) await Channel.findOneOrFail({ id: req.body.channel_id, guild_id: webhook.guild_id });
-
-	webhook.assign({
-		...req.body,
-		avatar: await handleFile(`/icons/${req.params.webhook_id}`, req.body.avatar)
-	});
-
-	await Promise.all([
-		emitEvent({
-			event: "WEBHOOKS_UPDATE",
-			channel_id: webhook.channel_id,
-			data: {
-				channel_id: webhook.channel_id,
-				guild_id: webhook.guild_id
-			}
-		} as WebhooksUpdateEvent),
-		webhook.save()
-	]);
-
-	res.json(webhook);
-}
-
-router.delete("/", route({ permission: "MANAGE_WEBHOOKS" }), async (req: Request, res: Response) => {
-	return deleteWebhook(req, res);
-});
-
-router.delete("/:token", route({}), validateWebhookToken, (req: Request, res: Response) => {
-	return deleteWebhook(req, res);
-});
-
-async function deleteWebhook(req: Request, res: Response) {
-	const webhook = await Webhook.findOneOrFail({ id: req.params.webhook_id });
-
-	await Promise.all([
-		emitEvent({
-			event: "WEBHOOKS_UPDATE",
-			channel_id: webhook.channel_id,
-			data: {
-				channel_id: webhook.channel_id,
-				guild_id: webhook.guild_id
-			}
-		} as WebhooksUpdateEvent),
-		webhook.remove()
-	]);
-
-	res.sendStatus(204);
-}
-
-export default router;
diff --git a/api/src/util/route.ts b/api/src/util/route.ts
index 1e2beb5d..6cd8f622 100644
--- a/api/src/util/route.ts
+++ b/api/src/util/route.ts
@@ -1,4 +1,4 @@
-import { DiscordApiErrors, Event, EventData, getPermission, PermissionResolvable, Permissions, Webhook } from "@fosscord/util";
+import { DiscordApiErrors, Event, EventData, getPermission, PermissionResolvable, Permissions } from "@fosscord/util";
 import { NextFunction, Request, Response } from "express";
 import fs from "fs";
 import path from "path";
@@ -54,13 +54,9 @@ export function route(opts: RouteOptions) {
 	return async (req: Request, res: Response, next: NextFunction) => {
 		if (opts.permission) {
 			const required = new Permissions(opts.permission);
-			if (req.params.webhook_id) {
-				const webhook = await Webhook.findOneOrFail({ id: req.params.webhook_id });
-				req.params.channel_id = webhook.channel_id;
-				req.params.guild_id = webhook.guild_id;
-			}
 			const permission = await getPermission(req.user_id, req.params.guild_id, req.params.channel_id);
 
+			// bitfield comparison: check if user lacks certain permission
 			if (!permission.has(required)) {
 				throw DiscordApiErrors.MISSING_PERMISSIONS.withParams(opts.permission as string);
 			}
diff --git a/util/src/entities/Webhook.ts b/util/src/entities/Webhook.ts
index d0d98804..12ba0d08 100644
--- a/util/src/entities/Webhook.ts
+++ b/util/src/entities/Webhook.ts
@@ -18,13 +18,13 @@ export class Webhook extends BaseClass {
 	@Column({ type: "simple-enum", enum: WebhookType })
 	type: WebhookType;
 
-	@Column()
-	name: string;
+	@Column({ nullable: true })
+	name?: string;
 
 	@Column({ nullable: true })
 	avatar?: string;
 
-	@Column({ nullable: true, select: false })
+	@Column({ nullable: true })
 	token?: string;
 
 	@Column({ nullable: true })
diff --git a/util/src/util/Regex.ts b/util/src/util/Regex.ts
index b5d23b7f..83fc9fe8 100644
--- a/util/src/util/Regex.ts
+++ b/util/src/util/Regex.ts
@@ -1,5 +1,5 @@
 export const DOUBLE_WHITE_SPACE = /\s\s+/g;
-export const SPECIAL_CHAR = /[@#\r\n\t\f\v]/gu;
+export const SPECIAL_CHAR = /[@#`:\r\n\t\f\v\p{C}]/gu;
 export const CHANNEL_MENTION = /<#(\d+)>/g;
 export const USER_MENTION = /<@!?(\d+)>/g;
 export const ROLE_MENTION = /<@&(\d+)>/g;