Merge branch 'typeorm' of https://github.com/fosscord/fosscord-api into typeorm
This commit is contained in:
		
						commit
						7847f55ef6
					
				| @ -61,6 +61,7 @@ | ||||
| 	"jest": { | ||||
| 		"setupFilesAfterEnv": [ | ||||
| 			"<rootDir>/jest/setup.js" | ||||
| 		] | ||||
| 		], | ||||
| 		"verbose": true | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -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"; | ||||
| 
 | ||||
| // TODO: somehow handle the deletion of images posted to the /external route
 | ||||
| 
 | ||||
| const router = Router(); | ||||
| 
 | ||||
| type crawled = { | ||||
| 	id: string; | ||||
| 	ogTitle: string; | ||||
| 	ogType: string; | ||||
| 	ogDescription: string; | ||||
| 	ogUrl: string; | ||||
| 	cachedImage: string; | ||||
| }; | ||||
| 
 | ||||
| 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}`); | ||||
|  | ||||
| @ -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 || "../"; | ||||
|  | ||||
							
								
								
									
										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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Flam3rboy
						Flam3rboy