diff --git a/cdn/package.json b/cdn/package.json
index 7e69b65d..1eb6b1a8 100644
--- a/cdn/package.json
+++ b/cdn/package.json
@@ -61,6 +61,7 @@
"jest": {
"setupFilesAfterEnv": [
"<rootDir>/jest/setup.js"
- ]
+ ],
+ "verbose": true
}
}
diff --git a/cdn/src/routes/external.ts b/cdn/src/routes/external.ts
index 625b6bbd..10bb0f7d 100644
--- a/cdn/src/routes/external.ts
+++ b/cdn/src/routes/external.ts
@@ -1,45 +1,38 @@
-// @ts-nocheck
-import bodyParser from "body-parser";
import { Router, Response, Request } from "express";
import fetch from "node-fetch";
-import crypto from "crypto";
import { HTTPError } from "lambert-server";
import { Snowflake } from "@fosscord/util";
import { storage } from "../util/Storage";
+import FileType from "file-type";
+import { Config } from "@fosscord/util";
-const router = Router();
-
-type crawled = {
- id: string;
- ogTitle: string;
- ogType: string;
- ogDescription: string;
- ogUrl: string;
- cachedImage: string;
-};
+// TODO: somehow handle the deletion of images posted to the /external route
+const router = Router();
const DEFAULT_FETCH_OPTIONS: any = {
redirect: "follow",
follow: 1,
headers: {
- "user-agent": "Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)",
+ "user-agent": "Mozilla/5.0 (compatible Fosscordbot/0.1; +https://fosscord.com)",
},
size: 1024 * 1024 * 8,
compress: true,
method: "GET",
};
-router.post("/", bodyParser.json(), async (req: Request, res: Response) => {
+router.post("/", async (req: Request, res: Response) => {
if (req.headers.signature !== Config.get().security.requestSignature)
throw new HTTPError("Invalid request signature");
+
if (!req.body) throw new HTTPError("Invalid Body");
+
const { url } = req.body;
if (!url || typeof url !== "string") throw new HTTPError("Invalid url");
const id = Snowflake.generate();
try {
- const response = await fetch(ogImage, DEFAULT_FETCH_OPTIONS);
+ const response = await fetch(url, DEFAULT_FETCH_OPTIONS);
const buffer = await response.buffer();
await storage.set(`/external/${id}`, buffer);
@@ -50,7 +43,7 @@ router.post("/", bodyParser.json(), async (req: Request, res: Response) => {
}
});
-router.get("/:id/", async (req: Request, res: Response) => {
+router.get("/:id", async (req: Request, res: Response) => {
const { id } = req.params;
const file = await storage.get(`/external/${id}`);
diff --git a/cdn/src/util/FileStorage.ts b/cdn/src/util/FileStorage.ts
index 6e74788f..fae6eb1a 100644
--- a/cdn/src/util/FileStorage.ts
+++ b/cdn/src/util/FileStorage.ts
@@ -6,6 +6,8 @@ import "missing-native-js-functions";
import { Readable } from "stream";
import ExifTransformer = require("exif-be-gone");
+// TODO: split stored files into separate folders named after cloned route
+
function getPath(path: string) {
// STORAGE_LOCATION has a default value in start.ts
const root = process.env.STORAGE_LOCATION || "../";
diff --git a/cdn/tests/cdn_endpoints.test.js b/cdn/tests/cdn_endpoints.test.js
new file mode 100644
index 00000000..a133d0dd
--- /dev/null
+++ b/cdn/tests/cdn_endpoints.test.js
@@ -0,0 +1,211 @@
+const dotenv = require("dotenv");
+const path = require("path");
+const fse = require("fs-extra");
+dotenv.config();
+
+// TODO: write unittest to check if FileStorage.ts is working
+// TODO: write unitest to check if env vars are defined
+
+if (!process.env.STORAGE_PROVIDER) process.env.STORAGE_PROVIDER = "file";
+// TODO:nodejs path.join trailing slash windows compatible
+if (process.env.STORAGE_PROVIDER === "file") {
+ if (process.env.STORAGE_LOCATION) {
+ if (!process.env.STORAGE_LOCATION.startsWith("/")) {
+ process.env.STORAGE_LOCATION = path.join(__dirname, "..", process.env.STORAGE_LOCATION, "/");
+ }
+ } else {
+ process.env.STORAGE_LOCATION = path.join(__dirname, "..", "files", "/");
+ }
+ fse.ensureDirSync(process.env.STORAGE_LOCATION);
+}
+const { CDNServer } = require("../dist/Server");
+const { Config } = require("@fosscord/util");
+const supertest = require("supertest");
+const request = supertest("http://localhost:3003");
+const server = new CDNServer({ port: Number(process.env.PORT) || 3003 });
+
+beforeAll(async () => {
+ await server.start();
+ return server;
+});
+
+afterAll(() => {
+ return server.stop();
+});
+
+describe("/ping", () => {
+ describe("GET", () => {
+ describe("without signature specified", () => {
+ test("route should respond with 200", async () => {
+ let response = await request.get("/ping");
+ expect(response.text).toBe("pong");
+ });
+ });
+ });
+});
+
+describe("/attachments", () => {
+ describe("POST", () => {
+ describe("without signature specified", () => {
+ test("route should respond with 400", async () => {
+ const response = await request.post("/attachments/123456789");
+ expect(response.statusCode).toBe(400);
+ });
+ });
+ describe("with signature specified, without file specified", () => {
+ test("route should respond with 400", async () => {
+ const response = await request
+ .post("/attachments/123456789")
+ .set({ signature: Config.get().security.requestSignature });
+ expect(response.statusCode).toBe(400);
+ });
+ });
+ describe("with signature specified, with file specified ", () => {
+ test("route should respond with Content-type: application/json, 200 and res.body.url", async () => {
+ const response = await request
+ .post("/attachments/123456789")
+ .set({ signature: Config.get().security.requestSignature })
+ .attach("file", __dirname + "/antman.jpg");
+ expect(response.statusCode).toBe(200);
+ expect(response.headers["content-type"]).toEqual(expect.stringContaining("json"));
+ expect(response.body.url).toBeDefined();
+ });
+ });
+ });
+ describe("GET", () => {
+ describe("getting uploaded image by url returned by POST /attachments", () => {
+ test("route should respond with 200", async () => {
+ let response = await request
+ .post("/attachments/123456789")
+ .set({ signature: Config.get().security.requestSignature })
+ .attach("file", __dirname + "/antman.jpg");
+ request.get(response.body.url.replace("http://localhost:3003", "")).then((x) => {
+ expect(x.statusCode).toBe(200);
+ });
+ });
+ });
+ });
+ describe("DELETE", () => {
+ describe("deleting uploaded image by url returned by POST /attachments", () => {
+ test("route should respond with res.body.success", async () => {
+ let response = await request
+ .post("/attachments/123456789")
+ .set({ signature: Config.get().security.requestSignature })
+ .attach("file", __dirname + "/antman.jpg");
+ request.delete(response.body.url.replace("http://localhost:3003", "")).then((x) => {
+ expect(x.body.success).toBeDefined();
+ });
+ });
+ });
+ });
+});
+
+describe("/avatars", () => {
+ describe("POST", () => {
+ describe("without signature specified", () => {
+ test("route should respond with 400", async () => {
+ const response = await request.post("/avatars/123456789");
+ expect(response.statusCode).toBe(400);
+ });
+ });
+ describe("with signature specified, without file specified", () => {
+ test("route should respond with 400", async () => {
+ const response = await request
+ .post("/avatars/123456789")
+ .set({ signature: Config.get().security.requestSignature });
+ expect(response.statusCode).toBe(400);
+ });
+ });
+ describe("with signature specified, with file specified ", () => {
+ test("route should respond with Content-type: application/json, 200 and res.body.url", async () => {
+ const response = await request
+ .post("/avatars/123456789")
+ .set({ signature: Config.get().security.requestSignature })
+ .attach("file", __dirname + "/antman.jpg");
+ expect(response.statusCode).toBe(200);
+ expect(response.headers["content-type"]).toEqual(expect.stringContaining("json"));
+ expect(response.body.url).toBeDefined();
+ });
+ });
+ });
+ describe("GET", () => {
+ describe("getting uploaded image by url returned by POST /avatars", () => {
+ test("route should respond with 200", async () => {
+ let response = await request
+ .post("/avatars/123456789")
+ .set({ signature: Config.get().security.requestSignature })
+ .attach("file", __dirname + "/antman.jpg");
+ request.get(response.body.url.replace("http://localhost:3003", "")).then((x) => {
+ expect(x.statusCode).toBe(200);
+ });
+ });
+ });
+ });
+ describe("DELETE", () => {
+ describe("deleting uploaded image by url returned by POST /avatars", () => {
+ test("route should respond with res.body.success", async () => {
+ let response = await request
+ .post("/avatars/123456789")
+ .set({ signature: Config.get().security.requestSignature })
+ .attach("file", __dirname + "/antman.jpg");
+ request.delete(response.body.url.replace("http://localhost:3003", "")).then((x) => {
+ expect(x.body.success).toBeDefined();
+ });
+ });
+ });
+ });
+});
+
+describe("/external", () => {
+ describe("POST", () => {
+ describe("without signature specified", () => {
+ test("route should respond with 400", async () => {
+ const response = await request.post("/external");
+ expect(response.statusCode).toBe(400);
+ });
+ });
+ describe("with signature specified, without file specified", () => {
+ test("route should respond with 400", async () => {
+ const response = await request
+ .post("/external")
+ .set({ signature: Config.get().security.requestSignature });
+ expect(response.statusCode).toBe(400);
+ });
+ });
+ describe("with signature specified, with file specified ", () => {
+ test("route should respond with Content-type: application/json, 200 and res.body.url", async () => {
+ const response = await request
+ .post("/external")
+ .set({ signature: Config.get().security.requestSignature })
+ .send({ url: "https://i.ytimg.com/vi_webp/TiXzhQr5AUc/mqdefault.webp" });
+ expect(response.statusCode).toBe(200);
+ expect(response.headers["content-type"]).toEqual(expect.stringContaining("json"));
+ expect(response.body.id).toBeDefined();
+ });
+ });
+ describe("with signature specified, with falsy url specified ", () => {
+ test("route should respond with 400", async () => {
+ const response = await request
+ .post("/external")
+ .set({ signature: Config.get().security.requestSignature })
+ .send({
+ url: "notavalidurl.123",
+ });
+ expect(response.statusCode).toBe(400);
+ });
+ });
+ });
+ describe("GET", () => {
+ describe("getting uploaded image by url returned by POST /avatars", () => {
+ test("route should respond with 200", async () => {
+ let response = await request
+ .post("/external")
+ .set({ signature: Config.get().security.requestSignature })
+ .send({ url: "https://i.ytimg.com/vi_webp/TiXzhQr5AUc/mqdefault.webp" });
+ request.get(`external/${response.body.id}`).then((x) => {
+ expect(x.statusCode).toBe(200);
+ });
+ });
+ });
+ });
+});
diff --git a/cdn/tests/filestorage.test.js b/cdn/tests/filestorage.test.js
new file mode 100644
index 00000000..78036602
--- /dev/null
+++ b/cdn/tests/filestorage.test.js
@@ -0,0 +1,27 @@
+const path = require("path");
+process.env.STORAGE_LOCATION = path.join(__dirname, "..", "files", "/");
+
+const { FileStorage } = require("../dist/util/FileStorage");
+const storage = new FileStorage();
+const fs = require("fs");
+
+const file = fs.readFileSync(path.join(__dirname, "antman.jpg"));
+
+describe("FileStorage", () => {
+ describe("saving a file", () => {
+ test("saving a buffer", async () => {
+ await storage.set("test_saving_file", file);
+ });
+ });
+ describe("getting a file", () => {
+ test("getting buffer with given name", async () => {
+ const buffer2 = await storage.get("test_saving_file");
+ expect(Buffer.compare(file, buffer2)).toBeTruthy();
+ });
+ });
+ describe("deleting a file", () => {
+ test("deleting buffer with given name", async () => {
+ await storage.delete("test_saving_file");
+ });
+ });
+});
diff --git a/cdn/tests/server.test.js b/cdn/tests/server.test.js
deleted file mode 100644
index 13046034..00000000
--- a/cdn/tests/server.test.js
+++ /dev/null
@@ -1,100 +0,0 @@
-const dotenv = require("dotenv");
-const path = require("path");
-const fse = require("fs-extra");
-dotenv.config();
-
-if (!process.env.STORAGE_PROVIDER) process.env.STORAGE_PROVIDER = "file";
-// TODO:nodejs path.join trailing slash windows compatible
-if (process.env.STORAGE_PROVIDER === "file") {
- if (process.env.STORAGE_LOCATION) {
- if (!process.env.STORAGE_LOCATION.startsWith("/")) {
- process.env.STORAGE_LOCATION = path.join(__dirname, "..", process.env.STORAGE_LOCATION, "/");
- }
- } else {
- process.env.STORAGE_LOCATION = path.join(__dirname, "..", "files", "/");
- }
- fse.ensureDirSync(process.env.STORAGE_LOCATION);
-}
-
-const { CDNServer } = require("../dist/Server");
-const { Config } = require("@fosscord/util");
-const supertest = require("supertest");
-const request = supertest("http://localhost:3003");
-const server = new CDNServer({ port: Number(process.env.PORT) || 3003 });
-
-beforeAll(async () => {
- await server.start();
- return server;
-});
-
-afterAll(() => {
- return server.stop();
-});
-
-describe("/ping", () => {
- describe("GET", () => {
- describe("without signature specified", () => {
- test("route should respond with 200", async () => {
- let response = await request.get("/ping");
- expect(response.text).toBe("pong");
- });
- });
- });
-});
-
-describe("/attachments", () => {
- describe("POST", () => {
- describe("without signature specified", () => {
- test("route should respond with 400", async () => {
- const response = await request.post("/attachments/123456789");
- expect(response.statusCode).toBe(400);
- });
- });
- describe("with signature specified, without file specified", () => {
- test("route should respond with 400", async () => {
- const response = await request
- .post("/attachments/123456789")
- .set({ signature: Config.get().security.requestSignature });
- expect(response.statusCode).toBe(400);
- });
- });
- describe("with signature specified, with file specified ", () => {
- test("route should respond with Content-type: application/json, 200 and res.body.url", async () => {
- const response = await request
- .post("/attachments/123456789")
- .set({ signature: Config.get().security.requestSignature })
- .attach("file", __dirname + "/antman.jpg");
- expect(response.statusCode).toBe(200);
- expect(response.headers["content-type"]).toEqual(expect.stringContaining("json"));
- expect(response.body.url).toBeDefined();
- attachment_url = response.body.url;
- });
- });
- });
- describe("GET", () => {
- describe("getting uploaded image by url returned by POST /attachments", () => {
- test("route should respond with 200", async () => {
- let response = await request
- .post("/attachments/123456789")
- .set({ signature: Config.get().security.requestSignature })
- .attach("file", __dirname + "/antman.jpg");
- request.get(response.body.url.replace("http://localhost:3003", "")).then((x) => {
- expect(x.statusCode).toBe(200);
- });
- });
- });
- });
- describe("DELETE", () => {
- describe("deleting uploaded image by url returned by POST /attachments", () => {
- test("route should respond with res.body.success", async () => {
- let response = await request
- .post("/attachments/123456789")
- .set({ signature: Config.get().security.requestSignature })
- .attach("file", __dirname + "/antman.jpg");
- request.delete(response.body.url.replace("http://localhost:3003", "")).then((x) => {
- expect(x.body.success).toBeDefined();
- });
- });
- });
- });
-});
|