summary refs log tree commit diff
path: root/cdn
diff options
context:
space:
mode:
Diffstat (limited to 'cdn')
-rw-r--r--cdn/package-lock.json28
-rw-r--r--cdn/package.json4
-rw-r--r--cdn/src/Server.ts4
-rw-r--r--cdn/src/routes/role-icons.ts101
4 files changed, 125 insertions, 12 deletions
diff --git a/cdn/package-lock.json b/cdn/package-lock.json

index 367f411e..a6c2df2d 100644 --- a/cdn/package-lock.json +++ b/cdn/package-lock.json
@@ -7,7 +7,7 @@ "": { "name": "@fosscord/cdn", "version": "1.0.0", - "license": "ISC", + "license": "GPLV3", "dependencies": { "@aws-sdk/client-s3": "^3.36.1", "@aws-sdk/node-http-handler": "^3.36.0", @@ -28,7 +28,7 @@ "missing-native-js-functions": "^1.2.17", "multer": "^1.4.2", "nanocolors": "^0.2.12", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.7", "supertest": "^6.1.6", "typescript": "^4.1.2" }, @@ -59,10 +59,10 @@ "lambert-server": "^1.2.12", "missing-native-js-functions": "^1.2.18", "multer": "^1.4.3", - "nanocolors": "^0.2.12", "node-fetch": "^2.6.1", "patch-package": "^6.4.7", "pg": "^8.7.1", + "picocolors": "^1.0.0", "proxy-agent": "^5.0.0", "reflect-metadata": "^0.1.13", "typeorm": "^0.2.38", @@ -6101,14 +6101,22 @@ } }, "node_modules/node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "dependencies": { "whatwg-url": "^5.0.0" }, "engines": { "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, "node_modules/node-fetch/node_modules/tr46": { @@ -9140,10 +9148,10 @@ "lambert-server": "^1.2.12", "missing-native-js-functions": "^1.2.18", "multer": "^1.4.3", - "nanocolors": "^0.2.12", "node-fetch": "^2.6.1", "patch-package": "^6.4.7", "pg": "^8.7.1", + "picocolors": "^1.0.0", "proxy-agent": "^5.0.0", "reflect-metadata": "^0.1.13", "ts-node": "^10.2.1", @@ -12574,9 +12582,9 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", "requires": { "whatwg-url": "^5.0.0" }, diff --git a/cdn/package.json b/cdn/package.json
index fec43785..aedcc4bf 100644 --- a/cdn/package.json +++ b/cdn/package.json
@@ -15,7 +15,7 @@ }, "keywords": [], "author": "", - "license": "ISC", + "license": "GPLV3", "bugs": { "url": "https://github.com/fosscord/fosscord-server/issues" }, @@ -54,7 +54,7 @@ "missing-native-js-functions": "^1.2.17", "multer": "^1.4.2", "nanocolors": "^0.2.12", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.7", "supertest": "^6.1.6", "typescript": "^4.1.2" }, diff --git a/cdn/src/Server.ts b/cdn/src/Server.ts
index cac34a80..b8d71fa9 100644 --- a/cdn/src/Server.ts +++ b/cdn/src/Server.ts
@@ -2,6 +2,7 @@ import { Server, ServerOptions } from "lambert-server"; import { Config, initDatabase, registerRoutes } from "@fosscord/util"; import path from "path"; import avatarsRoute from "./routes/avatars"; +import iconsRoute from "./routes/role-icons"; import bodyParser from "body-parser"; export interface CDNServerOptions extends ServerOptions {} @@ -40,6 +41,9 @@ export class CDNServer extends Server { this.app.use("/icons/", avatarsRoute); this.log("verbose", "[Server] Route /icons registered"); + this.app.use("/role-icons/", iconsRoute); + this.log("verbose", "[Server] Route /role-icons registered"); + this.app.use("/emojis/", avatarsRoute); this.log("verbose", "[Server] Route /emojis registered"); diff --git a/cdn/src/routes/role-icons.ts b/cdn/src/routes/role-icons.ts new file mode 100644
index 00000000..12aae8a4 --- /dev/null +++ b/cdn/src/routes/role-icons.ts
@@ -0,0 +1,101 @@ +import { Router, Response, Request } from "express"; +import { Config, Snowflake } from "@fosscord/util"; +import { storage } from "../util/Storage"; +import FileType from "file-type"; +import { HTTPError } from "lambert-server"; +import crypto from "crypto"; +import { multer } from "../util/multer"; + +//Role icons ---> avatars.ts modified + +// TODO: check user rights and perks and animated pfp are allowed in the policies +// TODO: generate different sizes of icon +// TODO: generate different image types of icon + +const STATIC_MIME_TYPES = [ + "image/png", + "image/jpeg", + "image/webp", + "image/svg+xml", + "image/svg", +]; +const ALLOWED_MIME_TYPES = [...STATIC_MIME_TYPES]; + +const router = Router(); + +router.post( + "/:role_id", + multer.single("file"), + async (req: Request, res: Response) => { + if (req.headers.signature !== Config.get().security.requestSignature) + throw new HTTPError("Invalid request signature"); + if (!req.file) throw new HTTPError("Missing file"); + const { buffer, mimetype, size, originalname, fieldname } = req.file; + const { role_id } = req.params; + + var hash = crypto + .createHash("md5") + .update(Snowflake.generate()) + .digest("hex"); + + const type = await FileType.fromBuffer(buffer); + if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) + throw new HTTPError("Invalid file type"); + + const path = `role-icons/${role_id}/${hash}.png`; + const endpoint = + Config.get().cdn.endpointPublic || "http://localhost:3003"; + + await storage.set(path, buffer); + + return res.json({ + id: hash, + content_type: type.mime, + size, + url: `${endpoint}${req.baseUrl}/${role_id}/${hash}`, + }); + } +); + +router.get("/:role_id", async (req: Request, res: Response) => { + var { role_id } = req.params; + //role_id = role_id.split(".")[0]; // remove .file extension + const path = `role-icons/${role_id}`; + + const file = await storage.get(path); + if (!file) throw new HTTPError("not found", 404); + const type = await FileType.fromBuffer(file); + + res.set("Content-Type", type?.mime); + res.set("Cache-Control", "public, max-age=31536000, must-revalidate"); + + return res.send(file); +}); + +router.get("/:role_id/:hash", async (req: Request, res: Response) => { + var { role_id, hash } = req.params; + //hash = hash.split(".")[0]; // remove .file extension + const path = `role-icons/${role_id}/${hash}`; + + const file = await storage.get(path); + if (!file) throw new HTTPError("not found", 404); + const type = await FileType.fromBuffer(file); + + res.set("Content-Type", type?.mime); + res.set("Cache-Control", "public, max-age=31536000, must-revalidate"); + + return res.send(file); +}); + +router.delete("/:role_id/:id", async (req: Request, res: Response) => { + if (req.headers.signature !== Config.get().security.requestSignature) + throw new HTTPError("Invalid request signature"); + const { role_id, id } = req.params; + const path = `role-icons/${role_id}/${id}`; + + await storage.delete(path); + + return res.send({ success: true }); +}); + +export default router;