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:
commit
1eecd409af
@ -61,6 +61,7 @@
|
|||||||
"jest": {
|
"jest": {
|
||||||
"setupFilesAfterEnv": [
|
"setupFilesAfterEnv": [
|
||||||
"<rootDir>/jest/setup.js"
|
"<rootDir>/jest/setup.js"
|
||||||
]
|
],
|
||||||
|
"verbose": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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}`);
|
||||||
|
@ -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 || "../";
|
||||||
|
211
cdn/tests/cdn_endpoints.test.js
Normal file
211
cdn/tests/cdn_endpoints.test.js
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
27
cdn/tests/filestorage.test.js
Normal file
27
cdn/tests/filestorage.test.js
Normal 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");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user