summary refs log tree commit diff
path: root/src/cdn/routes
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-12-25 05:31:32 +0100
committerRory& <root@rory.gay>2025-12-25 07:11:26 +0100
commit19edf63e37e84f81e7168e7411285084a30002ba (patch)
treec924a7da07b3cdcfb5ac0a70932cd241c6a18e96 /src/cdn/routes
parentWIP nix stuff (diff)
downloadserver-ts-dev/emma-fuck-this.tar.xz
various crap i was working on github/dev/emma-fuck-this dev/emma-fuck-this
Diffstat (limited to 'src/cdn/routes')
-rw-r--r--src/cdn/routes/_spacebar/cdn/cloud-attachments.ts171
-rw-r--r--src/cdn/routes/_spacebar/cdn/upload.ts51
-rw-r--r--src/cdn/routes/attachments.ts115
-rw-r--r--src/cdn/routes/avatars.ts15
-rw-r--r--src/cdn/routes/badge-icons.ts5
-rw-r--r--src/cdn/routes/embed.ts8
-rw-r--r--src/cdn/routes/guild-profiles.ts10
-rw-r--r--src/cdn/routes/role-icons.ts10
8 files changed, 241 insertions, 144 deletions
diff --git a/src/cdn/routes/_spacebar/cdn/cloud-attachments.ts b/src/cdn/routes/_spacebar/cdn/cloud-attachments.ts
new file mode 100644

index 00000000..5da92880 --- /dev/null +++ b/src/cdn/routes/_spacebar/cdn/cloud-attachments.ts
@@ -0,0 +1,171 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +import { CloudAttachment, Config, hasValidSignature, NewUrlUserSignatureData, Snowflake, UrlSignResult } from "@spacebar/util"; +import { Request, Response, Router } from "express"; +import imageSize from "image-size"; +import { HTTPError } from "lambert-server"; +import { fileTypeFromBuffer } from "file-type"; +import { cache, multer, storage } from "../../../util"; + +const router = Router({ mergeParams: true }); + +const SANITIZED_CONTENT_TYPE = ["text/html", "text/mhtml", "multipart/related", "application/xhtml+xml"]; + +router.get("/:channel_id/:id/:filename", cache, async (req: Request, res: Response) => { + const { channel_id, id, filename } = req.params; + // const { format } = req.query; + + const path = `attachments/${channel_id}/${id}/${filename}`; + + const fullUrl = (req.headers["x-forwarded-proto"] ?? req.protocol) + "://" + (req.headers["x-forwarded-host"] ?? req.hostname) + req.originalUrl; + + if ( + Config.get().security.cdnSignUrls && + !hasValidSignature( + new NewUrlUserSignatureData({ + ip: req.ip, + userAgent: req.headers["user-agent"] as string, + }), + UrlSignResult.fromUrl(fullUrl), + ) + ) { + return res.status(404).send("This content is no longer available."); + } + + const file = await storage.get(path); + if (!file) throw new HTTPError("File not found"); + const type = await fileTypeFromBuffer(file); + let content_type = type?.mime || "application/octet-stream"; + + if (SANITIZED_CONTENT_TYPE.includes(content_type)) { + content_type = "application/octet-stream"; + } + + res.set("Content-Type", content_type); + + return res.send(file); +}); + +// "cloud attachments" +router.put("/:channel_id/:batch_id/:attachment_id/:filename", multer.single("file"), async (req: Request, res: Response) => { + const { channel_id, batch_id, attachment_id, filename } = req.params; + const att = await CloudAttachment.findOneOrFail({ + where: { + uploadFilename: `${channel_id}/${batch_id}/${attachment_id}/${filename}`, + channelId: channel_id, + userAttachmentId: attachment_id, + userFilename: filename, + }, + }); + + const maxLength = Config.get().cdn.maxAttachmentSize; + + console.log("[Cloud Upload] Uploading attachment", att.id, att.userFilename, `Max size: ${maxLength} bytes`); + + const chunks: Buffer[] = []; + let length = 0; + + req.on("data", (chunk) => { + console.log(`[Cloud Upload] Received chunk of size ${chunk.length} bytes`); + chunks.push(chunk); + length += chunk.length; + if (length > maxLength) { + res.status(413).send("File too large"); + req.destroy(); + } + }); + req.on("end", async () => { + console.log(`[Cloud Upload] Finished receiving file, total size ${length} bytes`); + const buffer = Buffer.concat(chunks); + const path = `attachments/${channel_id}/${batch_id}/${attachment_id}/${filename}`; + + await storage.set(path, buffer); + + let mimeType = att.userOriginalContentType; + if (att.userOriginalContentType === null) { + const ft = await fileTypeFromBuffer(buffer); + mimeType = att.contentType = ft?.mime || "application/octet-stream"; + } + + if (mimeType?.includes("image")) { + const dimensions = imageSize(buffer); + if (dimensions) { + att.width = dimensions.width; + att.height = dimensions.height; + } + } + + att.size = buffer.length; + await att.save(); + + console.log("[Cloud Upload] Saved attachment", att.id, att.userFilename); + res.status(200).end(); + }); +}); + +router.delete("/:channel_id/:batch_id/:attachment_id/:filename", async (req: Request, res: Response) => { + if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature"); + console.log("[Cloud Delete] Deleting attachment", req.params); + + const { channel_id, batch_id, attachment_id, filename } = req.params; + const path = `attachments/${channel_id}/${batch_id}/${attachment_id}/${filename}`; + + const att = await CloudAttachment.findOne({ + where: { + uploadFilename: `${channel_id}/${batch_id}/${attachment_id}/${filename}`, + channelId: channel_id, + userAttachmentId: attachment_id, + userFilename: filename, + }, + }); + + if (att) { + await att.remove(); + await storage.delete(path); + return res.send({ success: true }); + } + return res.status(404).send("Attachment not found"); +}); + +router.post("/:channel_id/:batch_id/:attachment_id/:filename/clone_to_message/:message_id", async (req: Request, res: Response) => { + if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature"); + console.log("[Cloud Clone] Cloning attachment to message", req.params); + + const { channel_id, batch_id, attachment_id, filename, message_id } = req.params; + const path = `attachments/${channel_id}/${batch_id}/${attachment_id}/${filename}`; + const newPath = `attachments/${channel_id}/${message_id}/${filename}`; + + const att = await CloudAttachment.findOne({ + where: { + uploadFilename: `${channel_id}/${batch_id}/${attachment_id}/${filename}`, + channelId: channel_id, + userAttachmentId: attachment_id, + userFilename: filename, + }, + }); + + if (att) { + await storage.clone(path, newPath); + return res.send({ success: true, new_path: newPath }); + } + + return res.status(404).send("Attachment not found"); +}); + +export default router; diff --git a/src/cdn/routes/_spacebar/cdn/upload.ts b/src/cdn/routes/_spacebar/cdn/upload.ts new file mode 100644
index 00000000..f706d15c --- /dev/null +++ b/src/cdn/routes/_spacebar/cdn/upload.ts
@@ -0,0 +1,51 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2025 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +import { CloudAttachment, Config, hasValidSignature, NewUrlUserSignatureData, Snowflake, UrlSignResult } from "@spacebar/util"; +import { Request, Response, Router } from "express"; +import imageSize from "image-size"; +import { HTTPError } from "lambert-server"; +import { fileTypeFromBuffer } from "file-type"; +import { cache, multer, storage } from "../../../util"; +import { CdnImageLimitsConfiguration } from "../../../../util/config/types"; + +const router = Router({ mergeParams: true }); + +const SANITIZED_CONTENT_TYPE = ["text/html", "text/mhtml", "multipart/related", "application/xhtml+xml"]; + +const limits = Config.get().cdn.limits; +function createImageUploadRoute(name: string, path: string, limits: CdnImageLimitsConfiguration) { + router.post(`/${name}/:user_id`, multer.single("file"), async (req: Request, res: Response) => {}); + console.log(`Registered image upload /_spacebar/cdn/upload/${name} (-> storage/${path}/) with limits:`, JSON.stringify(limits)); +} + +createImageUploadRoute("icon", "icons", limits.icon); +createImageUploadRoute("role-icon", "role-icons", limits.roleIcon); +createImageUploadRoute("emoji", "emojis", limits.emoji); +createImageUploadRoute("sticker", "stickers", limits.sticker); +createImageUploadRoute("banner", "banners", limits.banner); +createImageUploadRoute("splash", "splashs", limits.splash); +createImageUploadRoute("avatar", "avatars", limits.avatar); +createImageUploadRoute("discovery-splash", "discovery-splashes", limits.discoverySplash); +createImageUploadRoute("app-icon", "app-icons", limits.appIcon); +createImageUploadRoute("discover-splash", "discover-splashes", limits.discoverSplash); +createImageUploadRoute("team-icon", "team-icons", limits.teamIcon); +createImageUploadRoute("channel-icon", "channel-icons", limits.channelIcon); +createImageUploadRoute("guild-avatar", "guild-avatars", limits.guildAvatar); + +export default router; diff --git a/src/cdn/routes/attachments.ts b/src/cdn/routes/attachments.ts
index db2b9503..b10b3b0a 100644 --- a/src/cdn/routes/attachments.ts +++ b/src/cdn/routes/attachments.ts
@@ -16,14 +16,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { Config, hasValidSignature, NewUrlUserSignatureData, Snowflake, UrlSignResult } from "@spacebar/util"; +import { CloudAttachment, Config, hasValidSignature, NewUrlUserSignatureData, Snowflake, UrlSignResult } from "@spacebar/util"; import { Request, Response, Router } from "express"; import imageSize from "image-size"; import { HTTPError } from "lambert-server"; -import { multer } from "../util/multer"; -import { storage } from "../util/Storage"; -import { CloudAttachment } from "../../util/entities/CloudAttachment"; import { fileTypeFromBuffer } from "file-type"; +import { cache, multer, storage } from "../util"; const router = Router({ mergeParams: true }); @@ -69,7 +67,7 @@ router.post("/:channel_id", multer.single("file"), async (req: Request, res: Res return res.json(file); }); -router.get("/:channel_id/:id/:filename", async (req: Request, res: Response) => { +router.get("/:channel_id/:id/:filename", cache, async (req: Request, res: Response) => { const { channel_id, id, filename } = req.params; // const { format } = req.query; @@ -100,7 +98,6 @@ router.get("/:channel_id/:id/:filename", async (req: Request, res: Response) => } res.set("Content-Type", content_type); - res.set("Cache-Control", "public, max-age=31536000"); return res.send(file); }); @@ -116,110 +113,4 @@ router.delete("/:channel_id/:id/:filename", async (req: Request, res: Response) return res.send({ success: true }); }); -// "cloud attachments" -router.put("/:channel_id/:batch_id/:attachment_id/:filename", multer.single("file"), async (req: Request, res: Response) => { - const { channel_id, batch_id, attachment_id, filename } = req.params; - const att = await CloudAttachment.findOneOrFail({ - where: { - uploadFilename: `${channel_id}/${batch_id}/${attachment_id}/${filename}`, - channelId: channel_id, - userAttachmentId: attachment_id, - userFilename: filename, - }, - }); - - const maxLength = Config.get().cdn.maxAttachmentSize; - - console.log("[Cloud Upload] Uploading attachment", att.id, att.userFilename, `Max size: ${maxLength} bytes`); - - const chunks: Buffer[] = []; - let length = 0; - - req.on("data", (chunk) => { - console.log(`[Cloud Upload] Received chunk of size ${chunk.length} bytes`); - chunks.push(chunk); - length += chunk.length; - if (length > maxLength) { - res.status(413).send("File too large"); - req.destroy(); - } - }); - req.on("end", async () => { - console.log(`[Cloud Upload] Finished receiving file, total size ${length} bytes`); - const buffer = Buffer.concat(chunks); - const path = `attachments/${channel_id}/${batch_id}/${attachment_id}/${filename}`; - - await storage.set(path, buffer); - - let mimeType = att.userOriginalContentType; - if (att.userOriginalContentType === null) { - const ft = await fileTypeFromBuffer(buffer); - mimeType = att.contentType = ft?.mime || "application/octet-stream"; - } - - if (mimeType?.includes("image")) { - const dimensions = imageSize(buffer); - if (dimensions) { - att.width = dimensions.width; - att.height = dimensions.height; - } - } - - att.size = buffer.length; - await att.save(); - - console.log("[Cloud Upload] Saved attachment", att.id, att.userFilename); - res.status(200).end(); - }); -}); - -router.delete("/:channel_id/:batch_id/:attachment_id/:filename", async (req: Request, res: Response) => { - if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature"); - console.log("[Cloud Delete] Deleting attachment", req.params); - - const { channel_id, batch_id, attachment_id, filename } = req.params; - const path = `attachments/${channel_id}/${batch_id}/${attachment_id}/${filename}`; - - const att = await CloudAttachment.findOne({ - where: { - uploadFilename: `${channel_id}/${batch_id}/${attachment_id}/${filename}`, - channelId: channel_id, - userAttachmentId: attachment_id, - userFilename: filename, - }, - }); - - if (att) { - await att.remove(); - await storage.delete(path); - return res.send({ success: true }); - } - return res.status(404).send("Attachment not found"); -}); - -router.post("/:channel_id/:batch_id/:attachment_id/:filename/clone_to_message/:message_id", async (req: Request, res: Response) => { - if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature"); - console.log("[Cloud Clone] Cloning attachment to message", req.params); - - const { channel_id, batch_id, attachment_id, filename, message_id } = req.params; - const path = `attachments/${channel_id}/${batch_id}/${attachment_id}/${filename}`; - const newPath = `attachments/${channel_id}/${message_id}/${filename}`; - - const att = await CloudAttachment.findOne({ - where: { - uploadFilename: `${channel_id}/${batch_id}/${attachment_id}/${filename}`, - channelId: channel_id, - userAttachmentId: attachment_id, - userFilename: filename, - }, - }); - - if (att) { - await storage.clone(path, newPath); - return res.send({ success: true, new_path: newPath }); - } - - return res.status(404).send("Attachment not found"); -}); - export default router; diff --git a/src/cdn/routes/avatars.ts b/src/cdn/routes/avatars.ts
index c770007a..4a433b23 100644 --- a/src/cdn/routes/avatars.ts +++ b/src/cdn/routes/avatars.ts
@@ -18,19 +18,16 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; -import { multer } from "../util/multer"; +import { multer, storage, cache, ANIMATED_MIME_TYPES, STATIC_MIME_TYPES } from "../util"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon // TODO: generate different image types of icon // TODO: delete old icons -const ANIMATED_MIME_TYPES = ["image/apng", "image/gif", "image/gifv"]; -const STATIC_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/svg+xml", "image/svg"]; const ALLOWED_MIME_TYPES = [...ANIMATED_MIME_TYPES, ...STATIC_MIME_TYPES]; const router = Router({ mergeParams: true }); @@ -60,7 +57,7 @@ router.post("/:user_id", multer.single("file"), async (req: Request, res: Respon }); }); -router.get("/:user_id", async (req: Request, res: Response) => { +router.get("/:user_id", cache, async (req: Request, res: Response) => { let { user_id } = req.params; user_id = user_id.split(".")[0]; // remove .file extension const path = `avatars/${user_id}`; @@ -70,12 +67,11 @@ router.get("/:user_id", async (req: Request, res: Response) => { const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); - res.set("Cache-Control", "public, max-age=31536000"); return res.send(file); }); -export const getAvatar = async (req: Request, res: Response) => { +router.get("/:user_id/:hash", cache, async (req: Request, res: Response) => { const { user_id } = req.params; let { hash } = req.params; hash = hash.split(".")[0]; // remove .file extension @@ -86,12 +82,9 @@ export const getAvatar = async (req: Request, res: Response) => { const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); - res.set("Cache-Control", "public, max-age=31536000"); return res.send(file); -}; - -router.get("/:user_id/:hash", getAvatar); +}); router.delete("/:user_id/:id", async (req: Request, res: Response) => { if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature"); diff --git a/src/cdn/routes/badge-icons.ts b/src/cdn/routes/badge-icons.ts
index ea7314a9..ac7c9385 100644 --- a/src/cdn/routes/badge-icons.ts +++ b/src/cdn/routes/badge-icons.ts
@@ -17,13 +17,13 @@ */ import { Router, Response, Request } from "express"; -import { storage } from "../util/Storage"; import { HTTPError } from "lambert-server"; import { fileTypeFromBuffer } from "file-type"; +import { cache, storage } from "../util"; const router = Router({ mergeParams: true }); -router.get("/:badge_id", async (req: Request, res: Response) => { +router.get("/:badge_id", cache, async (req: Request, res: Response) => { const { badge_id } = req.params; const path = `badge-icons/${badge_id}`; @@ -32,7 +32,6 @@ router.get("/:badge_id", async (req: Request, res: Response) => { const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); - res.set("Cache-Control", "public, max-age=31536000, must-revalidate"); return res.send(file); }); diff --git a/src/cdn/routes/embed.ts b/src/cdn/routes/embed.ts
index 2a08da5a..f8e11ea4 100644 --- a/src/cdn/routes/embed.ts +++ b/src/cdn/routes/embed.ts
@@ -21,6 +21,7 @@ import fs from "fs/promises"; import { HTTPError } from "lambert-server"; import { join } from "path"; import { fileTypeFromBuffer } from "file-type"; +import { cache } from "../util"; const defaultAvatarHashMap = new Map([ ["0", "4a8562cf00887030c416d3ec2d46385a"], @@ -46,6 +47,7 @@ const router = Router({ mergeParams: true }); async function getFile(path: string) { try { + console.log("[CDN/Embed.ts] Trying to read file:", path); return await fs.readFile(path); } catch (error) { try { @@ -58,7 +60,7 @@ async function getFile(path: string) { } } -router.get("/avatars/:id", async (req: Request, res: Response) => { +router.get("/avatars/:id", cache, async (req: Request, res: Response) => { let { id } = req.params; id = id.split(".")[0]; // remove .file extension const hash = defaultAvatarHashMap.get(id); @@ -70,12 +72,11 @@ router.get("/avatars/:id", async (req: Request, res: Response) => { const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); - res.set("Cache-Control", "public, max-age=31536000"); return res.send(file); }); -router.get("/group-avatars/:id", async (req: Request, res: Response) => { +router.get("/group-avatars/:id", cache, async (req: Request, res: Response) => { let { id } = req.params; id = id.split(".")[0]; // remove .file extension const hash = defaultGroupDMAvatarHashMap.get(id); @@ -87,7 +88,6 @@ router.get("/group-avatars/:id", async (req: Request, res: Response) => { const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); - res.set("Cache-Control", "public, max-age=31536000"); return res.send(file); }); diff --git a/src/cdn/routes/guild-profiles.ts b/src/cdn/routes/guild-profiles.ts
index 6660b720..3c9eb5fb 100644 --- a/src/cdn/routes/guild-profiles.ts +++ b/src/cdn/routes/guild-profiles.ts
@@ -21,16 +21,14 @@ import crypto from "crypto"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; import { multer } from "../util/multer"; -import { storage } from "../util/Storage"; import { fileTypeFromBuffer } from "file-type"; +import { ANIMATED_MIME_TYPES, cache, STATIC_MIME_TYPES, storage } from "../util"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon // TODO: generate different image types of icon // TODO: delete old icons -const ANIMATED_MIME_TYPES = ["image/apng", "image/gif", "image/gifv"]; -const STATIC_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/svg+xml", "image/svg"]; const ALLOWED_MIME_TYPES = [...ANIMATED_MIME_TYPES, ...STATIC_MIME_TYPES]; const router = Router({ mergeParams: true }); @@ -60,7 +58,7 @@ router.post("/", multer.single("file"), async (req: Request, res: Response) => { }); }); -router.get("/", async (req: Request, res: Response) => { +router.get("/", cache, async (req: Request, res: Response) => { const { guild_id } = req.params; let { user_id } = req.params; user_id = user_id.split(".")[0]; // remove .file extension @@ -71,12 +69,11 @@ router.get("/", async (req: Request, res: Response) => { const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); - res.set("Cache-Control", "public, max-age=31536000"); return res.send(file); }); -router.get("/:hash", async (req: Request, res: Response) => { +router.get("/:hash", cache, async (req: Request, res: Response) => { const { guild_id, user_id } = req.params; let { hash } = req.params; hash = hash.split(".")[0]; // remove .file extension @@ -87,7 +84,6 @@ router.get("/:hash", async (req: Request, res: Response) => { const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); - res.set("Cache-Control", "public, max-age=31536000"); return res.send(file); }); diff --git a/src/cdn/routes/role-icons.ts b/src/cdn/routes/role-icons.ts
index 1e00f987..f987479d 100644 --- a/src/cdn/routes/role-icons.ts +++ b/src/cdn/routes/role-icons.ts
@@ -18,11 +18,10 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; -import { multer } from "../util/multer"; +import { cache, multer, STATIC_MIME_TYPES, storage } from "../util"; //Role icons ---> avatars.ts modified @@ -30,7 +29,6 @@ import { multer } from "../util/multer"; // 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({ mergeParams: true }); @@ -59,7 +57,7 @@ router.post("/:role_id", multer.single("file"), async (req: Request, res: Respon }); }); -router.get("/:role_id", async (req: Request, res: Response) => { +router.get("/:role_id", cache, async (req: Request, res: Response) => { const { role_id } = req.params; //role_id = role_id.split(".")[0]; // remove .file extension const path = `role-icons/${role_id}`; @@ -69,12 +67,11 @@ router.get("/:role_id", async (req: Request, res: Response) => { const type = await fileTypeFromBuffer(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) => { +router.get("/:role_id/:hash", cache, async (req: Request, res: Response) => { const { role_id, hash } = req.params; //hash = hash.split(".")[0]; // remove .file extension const requested_extension = hash.split(".")[1]; @@ -92,7 +89,6 @@ router.get("/:role_id/:hash", async (req: Request, res: Response) => { const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); - res.set("Cache-Control", "public, max-age=31536000, must-revalidate"); return res.send(file); });