summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--api/package-lock.json41
-rw-r--r--api/package.json10
-rw-r--r--api/src/util/Config.ts372
-rw-r--r--cdn/package.json5
-rw-r--r--gateway/package.json4
-rw-r--r--gateway/src/util/Config.ts41
-rw-r--r--util/package-lock.json146
-rw-r--r--util/package.json12
8 files changed, 193 insertions, 438 deletions
diff --git a/api/package-lock.json b/api/package-lock.json
index 3308a6be..6305b089 100644
--- a/api/package-lock.json
+++ b/api/package-lock.json
@@ -19,7 +19,6 @@
 				"atomically": "^1.7.0",
 				"bcrypt": "^5.0.1",
 				"body-parser": "^1.19.0",
-				"canvas": "^2.8.0",
 				"cheerio": "^1.0.0-rc.9",
 				"dot-prop": "^6.0.1",
 				"dotenv": "^8.2.0",
@@ -2703,7 +2702,10 @@
 			"version": "2.8.0",
 			"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.8.0.tgz",
 			"integrity": "sha512-gLTi17X8WY9Cf5GZ2Yns8T5lfBOcGgFehDFb+JQwDqdOoBOcECS9ZWMEAqMSVcMYwXD659J8NyzjRY/2aE+C2Q==",
+			"dev": true,
 			"hasInstallScript": true,
+			"optional": true,
+			"peer": true,
 			"dependencies": {
 				"@mapbox/node-pre-gyp": "^1.0.0",
 				"nan": "^2.14.0",
@@ -3686,6 +3688,9 @@
 			"version": "4.2.1",
 			"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
 			"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
 			"dependencies": {
 				"mimic-response": "^2.0.0"
 			},
@@ -7184,6 +7189,9 @@
 			"version": "2.1.0",
 			"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
 			"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
 			"engines": {
 				"node": ">=8"
 			},
@@ -7515,7 +7523,10 @@
 		"node_modules/nan": {
 			"version": "2.15.0",
 			"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
-			"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
+			"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
+			"dev": true,
+			"optional": true,
+			"peer": true
 		},
 		"node_modules/nanoassert": {
 			"version": "1.1.0",
@@ -9528,6 +9539,7 @@
 			"version": "1.0.1",
 			"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
 			"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+			"dev": true,
 			"funding": [
 				{
 					"type": "github",
@@ -9547,6 +9559,9 @@
 			"version": "3.1.0",
 			"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
 			"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
 			"dependencies": {
 				"decompress-response": "^4.2.0",
 				"once": "^1.3.1",
@@ -13762,6 +13777,9 @@
 			"version": "2.8.0",
 			"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.8.0.tgz",
 			"integrity": "sha512-gLTi17X8WY9Cf5GZ2Yns8T5lfBOcGgFehDFb+JQwDqdOoBOcECS9ZWMEAqMSVcMYwXD659J8NyzjRY/2aE+C2Q==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
 			"requires": {
 				"@mapbox/node-pre-gyp": "^1.0.0",
 				"nan": "^2.14.0",
@@ -14609,6 +14627,9 @@
 			"version": "4.2.1",
 			"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
 			"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
 			"requires": {
 				"mimic-response": "^2.0.0"
 			}
@@ -17420,7 +17441,10 @@
 		"mimic-response": {
 			"version": "2.1.0",
 			"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
-			"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA=="
+			"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
+			"dev": true,
+			"optional": true,
+			"peer": true
 		},
 		"minimalistic-assert": {
 			"version": "1.0.1",
@@ -17682,7 +17706,10 @@
 		"nan": {
 			"version": "2.15.0",
 			"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
-			"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
+			"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
+			"dev": true,
+			"optional": true,
+			"peer": true
 		},
 		"nanoassert": {
 			"version": "1.1.0",
@@ -19304,12 +19331,16 @@
 		"simple-concat": {
 			"version": "1.0.1",
 			"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
-			"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
+			"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+			"dev": true
 		},
 		"simple-get": {
 			"version": "3.1.0",
 			"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
 			"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
+			"dev": true,
+			"optional": true,
+			"peer": true,
 			"requires": {
 				"decompress-response": "^4.2.0",
 				"once": "^1.3.1",
diff --git a/api/package.json b/api/package.json
index 967c9df8..7d957ed8 100644
--- a/api/package.json
+++ b/api/package.json
@@ -41,7 +41,6 @@
 		"atomically": "^1.7.0",
 		"bcrypt": "^5.0.1",
 		"body-parser": "^1.19.0",
-		"canvas": "^2.8.0",
 		"cheerio": "^1.0.0-rc.9",
 		"dot-prop": "^6.0.1",
 		"dotenv": "^8.2.0",
@@ -61,10 +60,7 @@
 		"mongoose-long": "^0.3.2",
 		"multer": "^1.4.2",
 		"node-fetch": "^2.6.1",
-		"require_optional": "^1.0.1"
-	},
-	"devDependencies": {
-		"0x": "^4.10.2",
+		"require_optional": "^1.0.1",
 		"@types/amqplib": "^0.8.1",
 		"@types/bcrypt": "^5.0.0",
 		"@types/express": "^4.17.9",
@@ -74,10 +70,6 @@
 		"@types/node": "^14.17.9",
 		"@types/node-fetch": "^2.5.7",
 		"@zerollup/ts-transform-paths": "^1.7.18",
-		"caxa": "^2.1.0",
-		"jest": "^26.6.3",
-		"saslprep": "^1.0.3",
-		"ts-node": "^9.1.1",
 		"ts-node-dev": "^1.1.6",
 		"typescript": "^4.1.2"
 	}
diff --git a/api/src/util/Config.ts b/api/src/util/Config.ts
deleted file mode 100644
index c86afbe7..00000000
--- a/api/src/util/Config.ts
+++ /dev/null
@@ -1,372 +0,0 @@
-// @ts-nocheck
-import Ajv, { JSONSchemaType } from "ajv";
-import { getConfigPathForFile } from "@fosscord/util/dist/util/Config";
-import { Config } from "@fosscord/util";
-
-export interface RateLimitOptions {
-	count: number;
-	timespan: number;
-}
-
-export interface DefaultOptions {
-	gateway: string;
-	general: {
-		instance_id: string;
-	};
-	permissions: {
-		user: {
-			createGuilds: boolean;
-		};
-	};
-	limits: {
-		user: {
-			maxGuilds: number;
-			maxUsername: number;
-			maxFriends: number;
-		};
-		guild: {
-			maxRoles: number;
-			maxMembers: number;
-			maxChannels: number;
-			maxChannelsInCategory: number;
-			hideOfflineMember: number;
-		};
-		message: {
-			characters: number;
-			ttsCharacters: number;
-			maxReactions: number;
-			maxAttachmentSize: number;
-			maxBulkDelete: number;
-		};
-		channel: {
-			maxPins: number;
-			maxTopic: number;
-		};
-		rate: {
-			ip: {
-				enabled: boolean;
-				count: number;
-				timespan: number;
-			};
-			routes: {
-				auth?: {
-					login?: RateLimitOptions;
-					register?: RateLimitOptions;
-				};
-				channel?: string;
-				// TODO: rate limit configuration for all routes
-			};
-		};
-	};
-	security: {
-		jwtSecret: string;
-		forwadedFor: string | null;
-		captcha: {
-			enabled: boolean;
-			service: "recaptcha" | "hcaptcha" | null; // TODO: hcaptcha, custom
-			sitekey: string | null;
-			secret: string | null;
-		};
-	};
-	login: {
-		requireCaptcha: boolean;
-	};
-	register: {
-		email: {
-			necessary: boolean;
-			allowlist: boolean;
-			blocklist: boolean;
-			domains: string[];
-		};
-		dateOfBirth: {
-			necessary: boolean;
-			minimum: number; // in years
-		};
-		requireCaptcha: boolean;
-		requireInvite: boolean;
-		allowNewRegistration: boolean;
-		allowMultipleAccounts: boolean;
-		password: {
-			minLength: number;
-			minNumbers: number;
-			minUpperCase: number;
-			minSymbols: number;
-			blockInsecureCommonPasswords: boolean; // TODO: efficiently save password blocklist in database
-		};
-	};
-}
-
-const schema: JSONSchemaType<DefaultOptions> & {
-	definitions: {
-		rateLimitOptions: JSONSchemaType<RateLimitOptions>;
-	};
-} = {
-	type: "object",
-	definitions: {
-		rateLimitOptions: {
-			type: "object",
-			properties: {
-				count: { type: "number" },
-				timespan: { type: "number" }
-			},
-			required: ["count", "timespan"]
-		}
-	},
-	properties: {
-		gateway: {
-			type: "string"
-		},
-		general: {
-			type: "object",
-			properties: {
-				instance_id: {
-					type: "string"
-				}
-			},
-			required: ["instance_id"],
-			additionalProperties: false
-		},
-		permissions: {
-			type: "object",
-			properties: {
-				user: {
-					type: "object",
-					properties: {
-						createGuilds: {
-							type: "boolean"
-						}
-					},
-					required: ["createGuilds"],
-					additionalProperties: false
-				}
-			},
-			required: ["user"],
-			additionalProperties: false
-		},
-		limits: {
-			type: "object",
-			properties: {
-				user: {
-					type: "object",
-					properties: {
-						maxFriends: {
-							type: "number"
-						},
-						maxGuilds: {
-							type: "number"
-						},
-						maxUsername: {
-							type: "number"
-						}
-					},
-					required: ["maxFriends", "maxGuilds", "maxUsername"],
-					additionalProperties: false
-				},
-				guild: {
-					type: "object",
-					properties: {
-						maxRoles: {
-							type: "number"
-						},
-						maxMembers: {
-							type: "number"
-						},
-						maxChannels: {
-							type: "number"
-						},
-						maxChannelsInCategory: {
-							type: "number"
-						},
-						hideOfflineMember: {
-							type: "number"
-						}
-					},
-					required: ["maxRoles", "maxMembers", "maxChannels", "maxChannelsInCategory", "hideOfflineMember"],
-					additionalProperties: false
-				},
-				message: {
-					type: "object",
-					properties: {
-						characters: {
-							type: "number"
-						},
-						ttsCharacters: {
-							type: "number"
-						},
-						maxReactions: {
-							type: "number"
-						},
-						maxAttachmentSize: {
-							type: "number"
-						},
-						maxBulkDelete: {
-							type: "number"
-						}
-					},
-					required: ["characters", "ttsCharacters", "maxReactions", "maxAttachmentSize", "maxBulkDelete"],
-					additionalProperties: false
-				},
-				channel: {
-					type: "object",
-					properties: {
-						maxPins: {
-							type: "number"
-						},
-						maxTopic: {
-							type: "number"
-						}
-					},
-					required: ["maxPins", "maxTopic"],
-					additionalProperties: false
-				},
-				rate: {
-					type: "object",
-					properties: {
-						ip: {
-							type: "object",
-							properties: {
-								enabled: { type: "boolean" },
-								count: { type: "number" },
-								timespan: { type: "number" }
-							},
-							required: ["enabled", "count", "timespan"],
-							additionalProperties: false
-						},
-						routes: {
-							type: "object",
-							properties: {
-								auth: {
-									type: "object",
-									properties: {
-										login: { $ref: "#/definitions/rateLimitOptions" },
-										register: { $ref: "#/definitions/rateLimitOptions" }
-									},
-									nullable: true,
-									required: [],
-									additionalProperties: false
-								},
-								channel: {
-									type: "string",
-									nullable: true
-								}
-							},
-							required: [],
-							additionalProperties: false
-						}
-					},
-					required: ["ip", "routes"]
-				}
-			},
-			required: ["channel", "guild", "message", "rate", "user"],
-			additionalProperties: false
-		},
-		security: {
-			type: "object",
-			properties: {
-				jwtSecret: {
-					type: "string"
-				},
-				forwadedFor: {
-					type: "string",
-					nullable: true
-				},
-				captcha: {
-					type: "object",
-					properties: {
-						enabled: { type: "boolean" },
-						service: {
-							type: "string",
-							enum: ["hcaptcha", "recaptcha", null],
-							nullable: true
-						},
-						sitekey: {
-							type: "string",
-							nullable: true
-						},
-						secret: {
-							type: "string",
-							nullable: true
-						}
-					},
-					required: ["enabled", "secret", "service", "sitekey"],
-					additionalProperties: false
-				}
-			},
-			required: ["captcha", "forwadedFor", "jwtSecret"],
-			additionalProperties: false
-		},
-		login: {
-			type: "object",
-			properties: {
-				requireCaptcha: { type: "boolean" }
-			},
-			required: ["requireCaptcha"],
-			additionalProperties: false
-		},
-		register: {
-			type: "object",
-			properties: {
-				email: {
-					type: "object",
-					properties: {
-						necessary: { type: "boolean" },
-						allowlist: { type: "boolean" },
-						blocklist: { type: "boolean" },
-						domains: {
-							type: "array",
-							items: {
-								type: "string"
-							}
-						}
-					},
-					required: ["allowlist", "blocklist", "domains", "necessary"],
-					additionalProperties: false
-				},
-				dateOfBirth: {
-					type: "object",
-					properties: {
-						necessary: { type: "boolean" },
-						minimum: { type: "number" }
-					},
-					required: ["minimum", "necessary"],
-					additionalProperties: false
-				},
-				requireCaptcha: { type: "boolean" },
-				requireInvite: { type: "boolean" },
-				allowNewRegistration: { type: "boolean" },
-				allowMultipleAccounts: { type: "boolean" },
-				password: {
-					type: "object",
-					properties: {
-						minLength: { type: "number" },
-						minNumbers: { type: "number" },
-						minUpperCase: { type: "number" },
-						minSymbols: { type: "number" },
-						blockInsecureCommonPasswords: { type: "boolean" }
-					},
-					required: ["minLength", "minNumbers", "minUpperCase", "minSymbols", "blockInsecureCommonPasswords"],
-					additionalProperties: false
-				}
-			},
-			required: [
-				"allowMultipleAccounts",
-				"allowNewRegistration",
-				"dateOfBirth",
-				"email",
-				"password",
-				"requireCaptcha",
-				"requireInvite"
-			],
-			additionalProperties: false
-		}
-	},
-	required: ["gateway", "general", "limits", "login", "permissions", "register", "security"],
-	additionalProperties: false
-};
-
-const ajv = new Ajv();
-const validator = ajv.compile(schema);
-
-const configPath = getConfigPathForFile("fosscord", "api", ".json");
-
-export const apiConfig = new Config<DefaultOptions>({ path: configPath, schemaValidator: validator, schema: schema });
diff --git a/cdn/package.json b/cdn/package.json
index 0a34f85f..7aa188a9 100644
--- a/cdn/package.json
+++ b/cdn/package.json
@@ -37,9 +37,8 @@
 		"missing-native-js-functions": "^1.0.8",
 		"multer": "^1.4.2",
 		"node-fetch": "^2.6.1",
-		"uuid": "^8.3.2"
-	},
-	"devDependencies": {
+		"uuid": "^8.3.2",
+		"typescript": "^4.1.2",
 		"@types/body-parser": "^1.19.0",
 		"@types/btoa": "^1.2.3",
 		"@types/dotenv": "^8.2.0",
diff --git a/gateway/package.json b/gateway/package.json
index 5a401e9f..6d340351 100644
--- a/gateway/package.json
+++ b/gateway/package.json
@@ -24,9 +24,7 @@
 		"node-fetch": "^2.6.1",
 		"typescript": "^4.2.3",
 		"uuid": "^8.3.2",
-		"ws": "^7.4.2"
-	},
-	"devDependencies": {
+		"ws": "^7.4.2",
 		"@types/amqplib": "^0.8.1",
 		"@types/jsonwebtoken": "^8.5.0",
 		"@types/mongoose-autopopulate": "^0.10.1",
diff --git a/gateway/src/util/Config.ts b/gateway/src/util/Config.ts
deleted file mode 100644
index e99c89f7..00000000
--- a/gateway/src/util/Config.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-// @ts-nocheck
-import { Config } from "@fosscord/util";
-import { getConfigPathForFile } from "@fosscord/util/dist/util/Config";
-import Ajv, { JSONSchemaType } from "ajv";
-
-export interface DefaultOptions {
-	endpoint?: string;
-	security: {
-		jwtSecret: string;
-	};
-}
-
-const schema: JSONSchemaType<DefaultOptions> = {
-	type: "object",
-	properties: {
-		endpoint: {
-			type: "string",
-			nullable: true,
-		},
-		security: {
-			type: "object",
-			properties: {
-				jwtSecret: {
-					type: "string",
-				},
-			},
-			required: ["jwtSecret"],
-		},
-	},
-	required: ["security"],
-};
-
-const ajv = new Ajv();
-const validator = ajv.compile(schema);
-
-const configPath = getConfigPathForFile("fosscord", "gateway", ".json");
-export const gatewayConfig = new Config<DefaultOptions>({
-	path: configPath,
-	schemaValidator: validator,
-	schema: schema,
-});
diff --git a/util/package-lock.json b/util/package-lock.json
index 4c678510..f58019b6 100644
--- a/util/package-lock.json
+++ b/util/package-lock.json
@@ -9,6 +9,9 @@
 			"version": "1.3.55",
 			"hasInstallScript": true,
 			"license": "GPLV3",
+			"dependencies": {
+				"node-fetch": "^2.6.1"
+			},
 			"devDependencies": {
 				"@types/amqplib": "^0.8.1",
 				"@types/jsonwebtoken": "^8.5.0",
@@ -16,6 +19,7 @@
 				"@types/mongoose-autopopulate": "^0.10.1",
 				"@types/mongoose-lean-virtuals": "^0.5.1",
 				"@types/node": "^14.17.9",
+				"@types/node-fetch": "^2.5.12",
 				"ajv": "^8.5.0",
 				"amqplib": "^0.8.0",
 				"dot-prop": "^6.0.1",
@@ -106,6 +110,16 @@
 			"integrity": "sha512-CMjgRNsks27IDwI785YMY0KLt3co/c0cQ5foxHYv/shC2w8oOnVwz5Ubq1QG5KzrcW+AXk6gzdnxIkDnTvzu3g==",
 			"dev": true
 		},
+		"node_modules/@types/node-fetch": {
+			"version": "2.5.12",
+			"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz",
+			"integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==",
+			"dev": true,
+			"dependencies": {
+				"@types/node": "*",
+				"form-data": "^3.0.0"
+			}
+		},
 		"node_modules/ajv": {
 			"version": "8.6.2",
 			"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz",
@@ -139,6 +153,12 @@
 				"node": ">=10"
 			}
 		},
+		"node_modules/asynckit": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+			"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+			"dev": true
+		},
 		"node_modules/bitsyntax": {
 			"version": "0.1.0",
 			"resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.1.0.tgz",
@@ -238,6 +258,18 @@
 			"integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==",
 			"dev": true
 		},
+		"node_modules/combined-stream": {
+			"version": "1.0.8",
+			"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+			"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+			"dev": true,
+			"dependencies": {
+				"delayed-stream": "~1.0.0"
+			},
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
 		"node_modules/core-util-is": {
 			"version": "1.0.2",
 			"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -253,6 +285,15 @@
 				"ms": "2.0.0"
 			}
 		},
+		"node_modules/delayed-stream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+			"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+			"dev": true,
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
 		"node_modules/denque": {
 			"version": "1.5.0",
 			"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
@@ -301,6 +342,20 @@
 			"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
 			"dev": true
 		},
+		"node_modules/form-data": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+			"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+			"dev": true,
+			"dependencies": {
+				"asynckit": "^0.4.0",
+				"combined-stream": "^1.0.8",
+				"mime-types": "^2.1.12"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
 		"node_modules/inherits": {
 			"version": "2.0.4",
 			"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -432,6 +487,27 @@
 			"dev": true,
 			"optional": true
 		},
+		"node_modules/mime-db": {
+			"version": "1.49.0",
+			"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
+			"integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/mime-types": {
+			"version": "2.1.32",
+			"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
+			"integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
+			"dev": true,
+			"dependencies": {
+				"mime-db": "1.49.0"
+			},
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
 		"node_modules/missing-native-js-functions": {
 			"version": "1.2.10",
 			"resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.2.10.tgz",
@@ -590,6 +666,14 @@
 			"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
 			"dev": true
 		},
+		"node_modules/node-fetch": {
+			"version": "2.6.1",
+			"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+			"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
+			"engines": {
+				"node": "4.x || >=6.0.0"
+			}
+		},
 		"node_modules/optional-require": {
 			"version": "1.1.7",
 			"resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.7.tgz",
@@ -853,6 +937,16 @@
 			"integrity": "sha512-CMjgRNsks27IDwI785YMY0KLt3co/c0cQ5foxHYv/shC2w8oOnVwz5Ubq1QG5KzrcW+AXk6gzdnxIkDnTvzu3g==",
 			"dev": true
 		},
+		"@types/node-fetch": {
+			"version": "2.5.12",
+			"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz",
+			"integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==",
+			"dev": true,
+			"requires": {
+				"@types/node": "*",
+				"form-data": "^3.0.0"
+			}
+		},
 		"ajv": {
 			"version": "8.6.2",
 			"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz",
@@ -879,6 +973,12 @@
 				"url-parse": "~1.5.1"
 			}
 		},
+		"asynckit": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+			"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+			"dev": true
+		},
 		"bitsyntax": {
 			"version": "0.1.0",
 			"resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.1.0.tgz",
@@ -980,6 +1080,15 @@
 			"integrity": "sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==",
 			"dev": true
 		},
+		"combined-stream": {
+			"version": "1.0.8",
+			"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+			"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+			"dev": true,
+			"requires": {
+				"delayed-stream": "~1.0.0"
+			}
+		},
 		"core-util-is": {
 			"version": "1.0.2",
 			"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -995,6 +1104,12 @@
 				"ms": "2.0.0"
 			}
 		},
+		"delayed-stream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+			"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+			"dev": true
+		},
 		"denque": {
 			"version": "1.5.0",
 			"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
@@ -1031,6 +1146,17 @@
 			"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
 			"dev": true
 		},
+		"form-data": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+			"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+			"dev": true,
+			"requires": {
+				"asynckit": "^0.4.0",
+				"combined-stream": "^1.0.8",
+				"mime-types": "^2.1.12"
+			}
+		},
 		"inherits": {
 			"version": "2.0.4",
 			"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -1157,6 +1283,21 @@
 			"dev": true,
 			"optional": true
 		},
+		"mime-db": {
+			"version": "1.49.0",
+			"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
+			"integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==",
+			"dev": true
+		},
+		"mime-types": {
+			"version": "2.1.32",
+			"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
+			"integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
+			"dev": true,
+			"requires": {
+				"mime-db": "1.49.0"
+			}
+		},
 		"missing-native-js-functions": {
 			"version": "1.2.10",
 			"resolved": "https://registry.npmjs.org/missing-native-js-functions/-/missing-native-js-functions-1.2.10.tgz",
@@ -1274,6 +1415,11 @@
 			"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
 			"dev": true
 		},
+		"node-fetch": {
+			"version": "2.6.1",
+			"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+			"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
+		},
 		"optional-require": {
 			"version": "1.1.7",
 			"resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.7.tgz",
diff --git a/util/package.json b/util/package.json
index a33df2ba..8f32c1e3 100644
--- a/util/package.json
+++ b/util/package.json
@@ -25,22 +25,24 @@
 		"url": "https://github.com/fosscord/fosscord-server-util/issues"
 	},
 	"homepage": "https://docs.fosscord.com/",
-	"devDependencies": {
+	"dependencies": {
 		"@types/amqplib": "^0.8.1",
 		"@types/jsonwebtoken": "^8.5.0",
 		"@types/mongodb": "^3.6.9",
 		"@types/mongoose-autopopulate": "^0.10.1",
 		"@types/mongoose-lean-virtuals": "^0.5.1",
 		"@types/node": "^14.17.9",
+		"@types/node-fetch": "^2.5.12",
 		"ajv": "^8.5.0",
-		"amqplib": "^0.8.0",
 		"dot-prop": "^6.0.1",
 		"env-paths": "^2.2.1",
+		"typescript": "^4.1.3",
+		"amqplib": "^0.8.0",
 		"jsonwebtoken": "^8.5.1",
+		"mongoose-autopopulate": "^0.12.3",
+		"mongoose": "^5.13.7",
 		"missing-native-js-functions": "^1.2.2",
 		"mongodb": "^3.6.9",
-		"mongoose": "^5.13.7",
-		"mongoose-autopopulate": "^0.12.3",
-		"typescript": "^4.1.3"
+		"node-fetch": "^2.6.1"
 	}
 }