1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-11-07 11:22:35 +01:00

Merge branch 'typeorm' of https://github.com/fosscord/fosscord-api into typeorm

This commit is contained in:
Flam3rboy 2021-08-30 12:15:07 +02:00
commit 1eecd409af
6 changed files with 252 additions and 118 deletions

View File

@ -61,6 +61,7 @@
"jest": { "jest": {
"setupFilesAfterEnv": [ "setupFilesAfterEnv": [
"<rootDir>/jest/setup.js" "<rootDir>/jest/setup.js"
] ],
"verbose": true
} }
} }

View File

@ -1,45 +1,38 @@
// @ts-nocheck
import bodyParser from "body-parser";
import { Router, Response, Request } from "express"; import { Router, Response, Request } from "express";
import fetch from "node-fetch"; import fetch from "node-fetch";
import crypto from "crypto";
import { HTTPError } from "lambert-server"; import { HTTPError } from "lambert-server";
import { Snowflake } from "@fosscord/util"; import { Snowflake } from "@fosscord/util";
import { storage } from "../util/Storage"; import { storage } from "../util/Storage";
import FileType from "file-type";
import { Config } from "@fosscord/util";
// TODO: somehow handle the deletion of images posted to the /external route
const router = Router(); const router = Router();
type crawled = {
id: string;
ogTitle: string;
ogType: string;
ogDescription: string;
ogUrl: string;
cachedImage: string;
};
const DEFAULT_FETCH_OPTIONS: any = { const DEFAULT_FETCH_OPTIONS: any = {
redirect: "follow", redirect: "follow",
follow: 1, follow: 1,
headers: { 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, size: 1024 * 1024 * 8,
compress: true, compress: true,
method: "GET", 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) if (req.headers.signature !== Config.get().security.requestSignature)
throw new HTTPError("Invalid request signature"); throw new HTTPError("Invalid request signature");
if (!req.body) throw new HTTPError("Invalid Body"); if (!req.body) throw new HTTPError("Invalid Body");
const { url } = req.body; const { url } = req.body;
if (!url || typeof url !== "string") throw new HTTPError("Invalid url"); if (!url || typeof url !== "string") throw new HTTPError("Invalid url");
const id = Snowflake.generate(); const id = Snowflake.generate();
try { try {
const response = await fetch(ogImage, DEFAULT_FETCH_OPTIONS); const response = await fetch(url, DEFAULT_FETCH_OPTIONS);
const buffer = await response.buffer(); const buffer = await response.buffer();
await storage.set(`/external/${id}`, 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 { id } = req.params;
const file = await storage.get(`/external/${id}`); const file = await storage.get(`/external/${id}`);

View File

@ -6,6 +6,8 @@ import "missing-native-js-functions";
import { Readable } from "stream"; import { Readable } from "stream";
import ExifTransformer = require("exif-be-gone"); import ExifTransformer = require("exif-be-gone");
// TODO: split stored files into separate folders named after cloned route
function getPath(path: string) { function getPath(path: string) {
// STORAGE_LOCATION has a default value in start.ts // STORAGE_LOCATION has a default value in start.ts
const root = process.env.STORAGE_LOCATION || "../"; const root = process.env.STORAGE_LOCATION || "../";

View File

@ -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);
});
});
});
});
});

View File

@ -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");
});
});
});

View File

@ -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();
});
});
});
});
});