diff --git a/src/cdn/Server.ts b/src/cdn/Server.ts
index dff76494..42323556 100644
--- a/src/cdn/Server.ts
+++ b/src/cdn/Server.ts
@@ -17,12 +17,11 @@
*/
import { Server, ServerOptions } from "lambert-server";
-import { Attachment, Config, initDatabase, registerRoutes } from "@spacebar/util";
+import { Config, initDatabase, registerRoutes } from "@spacebar/util";
import { CORS, BodyParser } from "@spacebar/api";
import path from "path";
import guildProfilesRoute from "./routes/guild-profiles";
import morgan from "morgan";
-import { Like } from "typeorm";
export type CDNServerOptions = ServerOptions;
@@ -36,7 +35,6 @@ export class CDNServer extends Server {
async start() {
await initDatabase();
await Config.init();
- await this.cleanupSignaturesInDb();
const logRequests = process.env["LOG_REQUESTS"] != undefined;
if (logRequests) {
@@ -73,23 +71,4 @@ export class CDNServer extends Server {
async stop() {
return super.stop();
}
-
- async cleanupSignaturesInDb() {
- console.log("[CDN] Cleaning up signatures in database");
- const attachmentsToFix = await Attachment.find({
- where: { url: Like("%?ex=%") },
- });
- if (attachmentsToFix.length === 0) {
- console.log("[CDN] No attachments to fix");
- return;
- }
-
- console.log("[CDN] Found", attachmentsToFix.length, " attachments to fix");
- for (const attachment of attachmentsToFix) {
- attachment.url = attachment.url.split("?ex=")[0];
- attachment.proxy_url = attachment.proxy_url?.split("?ex=")[0];
- await attachment.save();
- console.log(`[CDN] Fixed attachment ${attachment.id}`);
- }
- }
}
diff --git a/src/cdn/routes/attachments.ts b/src/cdn/routes/attachments.ts
index 70825cf6..d17ac43c 100644
--- a/src/cdn/routes/attachments.ts
+++ b/src/cdn/routes/attachments.ts
@@ -16,7 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-import { Config, hasValidSignature, NewUrlUserSignatureData, Snowflake, UrlSignResult } from "@spacebar/util";
+import { Attachment, Config, hasValidSignature, NewUrlUserSignatureData, Snowflake, UrlSignResult } from "@spacebar/util";
import { Request, Response, Router } from "express";
import imageSize from "image-size";
import { HTTPError } from "lambert-server";
@@ -30,16 +30,16 @@ const router = Router({ mergeParams: true });
const SANITIZED_CONTENT_TYPE = ["text/html", "text/mhtml", "multipart/related", "application/xhtml+xml"];
-router.post("/:channel_id", multer.single("file"), async (req: Request, res: Response) => {
- if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature");
+router.post("/:channel_id/:message_id", multer.single("file"), async (req: Request, res: Response) => {
+ if (req.headers.signature !== Config.get().security.requestSignature)
+ throw new HTTPError(`Invalid request signature, expected '${Config.get().security.requestSignature}', got ${req.headers.signature}`);
if (!req.file) throw new HTTPError("file missing");
const { buffer, mimetype, size, originalname } = req.file;
- const { channel_id } = req.params as { [key: string]: string };
+ const { channel_id, message_id } = req.params as { [key: string]: string };
const filename = originalname.replaceAll(" ", "_").replace(/[^a-zA-Z0-9._]+/g, "");
- const id = Snowflake.generate();
- const path = `attachments/${channel_id}/${id}/${filename}`;
+ const path = `attachments/${channel_id}/${message_id}/${filename}`;
const endpoint = Config.get()?.cdn.endpointPublic;
@@ -57,7 +57,9 @@ router.post("/:channel_id", multer.single("file"), async (req: Request, res: Res
const finalUrl = `${endpoint}/${path}`;
const file = {
- id,
+ id: Snowflake.generate(),
+ channel_id,
+ message_id,
content_type: mimetype,
filename: filename,
size,
@@ -70,11 +72,11 @@ router.post("/:channel_id", multer.single("file"), async (req: Request, res: Res
return res.json(file);
});
-router.get("/:channel_id/:id/:filename", cache, async (req: Request, res: Response) => {
- const { channel_id, id, filename } = req.params as { [key: string]: string };
+router.get("/:channel_id/:message_id/:filename", cache, async (req: Request, res: Response) => {
+ const { channel_id, message_id, filename } = req.params as { [key: string]: string };
// const { format } = req.query;
- const path = `attachments/${channel_id}/${id}/${filename}`;
+ const path = `attachments/${channel_id}/${message_id}/${filename}`;
const fullUrl = (req.headers["x-forwarded-proto"] ?? req.protocol) + "://" + (req.headers["x-forwarded-host"] ?? req.hostname) + req.originalUrl;
@@ -91,14 +93,24 @@ router.get("/:channel_id/:id/:filename", cache, async (req: Request, res: Respon
}),
UrlSignResult.fromUrl(fullUrl),
);
- console.warn("[CDN/Attachments] Client sent invalid attachment URL signature");
+ if (!hasValidAuth) console.warn("[CDN/Attachments] Client sent invalid attachment URL signature");
}
- if (!hasValidAuth) {
- return res.status(404).send("This content is no longer available.");
- }
+ if (!hasValidAuth) return res.status(404).send("This content is no longer available.");
- const file = await storage.get(path);
+ let file = await storage.get(path);
+ // handle re-keying paths to be correct
+ if (!file) {
+ const att = await Attachment.findOne({ where: { id: message_id, channel_id: channel_id } });
+ if (att) {
+ const oldPath = `attachments/${channel_id}/${att.id}/${filename}`;
+ if (await storage.exists(oldPath)) {
+ console.log(`[CDN/Attachments] Moving ${oldPath} -> ${path}!`);
+ await storage.move(oldPath, path);
+ 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";
@@ -112,11 +124,11 @@ router.get("/:channel_id/:id/:filename", cache, async (req: Request, res: Respon
return res.send(file);
});
-router.delete("/:channel_id/:id/:filename", async (req: Request, res: Response) => {
+router.delete("/:channel_id/:message_id/:filename", async (req: Request, res: Response) => {
if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature");
- const { channel_id, id, filename } = req.params as { [key: string]: string };
- const path = `attachments/${channel_id}/${id}/${filename}`;
+ const { channel_id, message_id, filename } = req.params as { [key: string]: string };
+ const path = `attachments/${channel_id}/${message_id}/${filename}`;
await storage.delete(path);
|