summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Server.ts68
-rw-r--r--src/index.ts1
-rw-r--r--src/routes/attachments.ts73
-rw-r--r--src/routes/avatars.ts72
-rw-r--r--src/routes/external.ts65
-rw-r--r--src/routes/ping.ts9
-rw-r--r--src/start.ts27
-rw-r--r--src/util/FileStorage.ts40
-rw-r--r--src/util/Storage.ts15
-rw-r--r--src/util/multer.ts10
10 files changed, 0 insertions, 380 deletions
diff --git a/src/Server.ts b/src/Server.ts
deleted file mode 100644

index 1b79b037..00000000 --- a/src/Server.ts +++ /dev/null
@@ -1,68 +0,0 @@ -import { Server, ServerOptions } from "lambert-server"; -import { Config, db } from "@fosscord/server-util"; -import path from "path"; -import avatarsRoute from "./routes/avatars"; - -export interface CDNServerOptions extends ServerOptions {} - -export class CDNServer extends Server { - public options: CDNServerOptions; - - constructor(options?: Partial<CDNServerOptions>) { - super(options); - } - - async start() { - console.log("[Database] connecting ..."); - // @ts-ignore - await (db as Promise<Connection>); - await Config.init(); - console.log("[Database] connected"); - this.app.use((req, res, next) => { - res.set("Access-Control-Allow-Origin", "*"); - // TODO: use better CSP policy - res.set( - "Content-security-policy", - "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';" - ); - res.set("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers") || "*"); - res.set("Access-Control-Allow-Methods", req.header("Access-Control-Request-Methods") || "*"); - next(); - }); - - await this.registerRoutes(path.join(__dirname, "routes/")); - - this.app.use("/icons/", avatarsRoute); - this.log("info", "[Server] Route /icons registered"); - - this.app.use("/emojis/", avatarsRoute); - this.log("info", "[Server] Route /emojis registered"); - - this.app.use("/stickers/", avatarsRoute); - this.log("info", "[Server] Route /stickers registered"); - - this.app.use("/banners/", avatarsRoute); - this.log("info", "[Server] Route /banners registered"); - - this.app.use("/splashes/", avatarsRoute); - this.log("info", "[Server] Route /splashes registered"); - - this.app.use("/app-icons/", avatarsRoute); - this.log("info", "[Server] Route /app-icons registered"); - - this.app.use("/app-assets/", avatarsRoute); - this.log("info", "[Server] Route /app-assets registered"); - - this.app.use("/discover-splashes/", avatarsRoute); - this.log("info", "[Server] Route /discover-splashes registered"); - - this.app.use("/team-icons/", avatarsRoute); - this.log("info", "[Server] Route /team-icons registered"); - - return super.start(); - } - - async stop() { - return super.stop(); - } -} diff --git a/src/index.ts b/src/index.ts deleted file mode 100644
index 7513bd2f..00000000 --- a/src/index.ts +++ /dev/null
@@ -1 +0,0 @@ -export * from "./Server"; diff --git a/src/routes/attachments.ts b/src/routes/attachments.ts deleted file mode 100644
index c387aa37..00000000 --- a/src/routes/attachments.ts +++ /dev/null
@@ -1,73 +0,0 @@ -import { Router, Response, Request } from "express"; -import { Config, Snowflake } from "@fosscord/server-util"; -import { storage } from "../util/Storage"; -import FileType from "file-type"; -import { HTTPError } from "lambert-server"; -import { multer } from "../util/multer"; -import imageSize from "image-size"; - -const router = Router(); - -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"); - if (!req.file) return; - - const { buffer, mimetype, size, originalname, fieldname } = req.file; - const { channel_id } = req.params; - const filename = originalname.replaceAll(" ", "_").replace(/[^a-zA-Z0-9._]+/g, ""); - const id = Snowflake.generate(); - const path = `attachments/${channel_id}/${id}/${filename}`; - - const endpoint = Config.get()?.cdn.endpoint || "http://localhost:3003"; - - await storage.set(path, buffer); - var width; - var height; - if (mimetype.includes("image")) { - const dimensions = imageSize(buffer); - if (dimensions) { - width = dimensions.width; - height = dimensions.height; - } - } - - const file = { - id, - content_type: mimetype, - filename: filename, - size, - url: `${endpoint}/${path}`, - width, - height, - }; - - return res.json(file); -}); - -router.get("/:channel_id/:id/:filename", async (req: Request, res: Response) => { - const { channel_id, id, filename } = req.params; - - const file = await storage.get(`attachments/${channel_id}/${id}/${filename}`); - if (!file) throw new HTTPError("File not found"); - const type = await FileType.fromBuffer(file); - - res.set("Content-Type", type?.mime); - res.set("Cache-Control", "public, max-age=31536000"); - - return res.send(file); -}); - -router.delete("/:channel_id/: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; - const path = `attachments/${channel_id}/${id}/${filename}`; - - await storage.delete(path); - - return res.send({ success: true }); -}); - -export default router; diff --git a/src/routes/avatars.ts b/src/routes/avatars.ts deleted file mode 100644
index 60befe2c..00000000 --- a/src/routes/avatars.ts +++ /dev/null
@@ -1,72 +0,0 @@ -import { Router, Response, Request } from "express"; -import { Config, Snowflake } from "@fosscord/server-util"; -import { storage } from "../util/Storage"; -import FileType from "file-type"; -import { HTTPError } from "lambert-server"; -import { multer } from "../util/multer"; -import crypto from "crypto"; - -// TODO: check premium and animated pfp are allowed in the config -// TODO: generate different sizes of icon -// TODO: generate different image types of icon -// TODO: delete old icons - -const ANIMATED_MIME_TYPES = ["image/apng", "image/gif", "image/gifv"]; -const STATIC_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/svg+xml", "image/svg"]; -const ALLOWED_MIME_TYPES = [...ANIMATED_MIME_TYPES, ...STATIC_MIME_TYPES]; - -const router = Router(); - -router.post("/:user_id", multer.single("file"), async (req: Request, res: Response) => { - if (req.headers.signature !== Config.get().security.requestSignature) - throw new HTTPError("Invalid request signature"); - if (!req.file) throw new HTTPError("Missing file"); - const { buffer, mimetype, size, originalname, fieldname } = req.file; - const { user_id } = req.params; - - var hash = crypto.createHash("md5").update(Snowflake.generate()).digest("hex"); - - const type = await FileType.fromBuffer(buffer); - if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) throw new HTTPError("Invalid file type"); - if (ANIMATED_MIME_TYPES.includes(type.mime)) hash = `a_${hash}`; // animated icons have a_ infront of the hash - - const path = `avatars/${user_id}/${hash}`; - const endpoint = Config.get().cdn.endpoint || "http://localhost:3003"; - - await storage.set(path, buffer); - - return res.json({ - id: hash, - content_type: type.mime, - size, - url: `${endpoint}${req.baseUrl}/${user_id}/${hash}`, - }); -}); - -router.get("/:user_id/:hash", async (req: Request, res: Response) => { - var { user_id, hash } = req.params; - hash = hash.split(".")[0]; // remove .file extension - const path = `avatars/${user_id}/${hash}`; - - const file = await storage.get(path); - if (!file) throw new HTTPError("not found", 404); - const type = await FileType.fromBuffer(file); - - res.set("Content-Type", type?.mime); - res.set("Cache-Control", "public, max-age=31536000"); - - return res.send(file); -}); - -router.delete("/:user_id/:id", async (req: Request, res: Response) => { - if (req.headers.signature !== Config.get().security.requestSignature) - throw new HTTPError("Invalid request signature"); - const { user_id, id } = req.params; - const path = `avatars/${user_id}/${id}`; - - await storage.delete(path); - - return res.send({ success: true }); -}); - -export default router; diff --git a/src/routes/external.ts b/src/routes/external.ts deleted file mode 100644
index 3abe9c22..00000000 --- a/src/routes/external.ts +++ /dev/null
@@ -1,65 +0,0 @@ -// @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/server-util"; -import { storage } from "../util/Storage"; - -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)", - }, - size: 1024 * 1024 * 8, - compress: true, - method: "GET", -}; - -router.post("/", bodyParser.json(), 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 buffer = await response.buffer(); - - await storage.set(`/external/${id}`, buffer); - - res.send({ id }); - } catch (error) { - throw new HTTPError("Couldn't fetch website"); - } -}); - -router.get("/:id/", async (req: Request, res: Response) => { - const { id } = req.params; - - const file = await storage.get(`/external/${id}`); - if (!file) throw new HTTPError("File not found"); - const result = await FileType.fromBuffer(file); - - res.set("Content-Type", result?.mime); - - return res.send(file); -}); - -export default router; diff --git a/src/routes/ping.ts b/src/routes/ping.ts deleted file mode 100644
index 38daf81e..00000000 --- a/src/routes/ping.ts +++ /dev/null
@@ -1,9 +0,0 @@ -import { Router, Response, Request } from "express"; - -const router = Router(); - -router.get("/", (req: Request, res: Response) => { - res.send("pong"); -}); - -export default router; diff --git a/src/start.ts b/src/start.ts deleted file mode 100644
index 57c9f704..00000000 --- a/src/start.ts +++ /dev/null
@@ -1,27 +0,0 @@ -import path from "path"; -import dotenv from "dotenv"; -import fse from "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); -} - -import { CDNServer } from "./Server"; - -const server = new CDNServer({ port: Number(process.env.PORT) || 3003 }); -server - .start() - .then(() => { - console.log("[Server] started on :" + server.options.port); - }) - .catch((e) => console.error("[Server] Error starting: ", e)); diff --git a/src/util/FileStorage.ts b/src/util/FileStorage.ts deleted file mode 100644
index 6e74788f..00000000 --- a/src/util/FileStorage.ts +++ /dev/null
@@ -1,40 +0,0 @@ -import { Storage } from "./Storage"; -import fs from "fs"; -import fse from "fs-extra"; -import { join, relative, dirname } from "path"; -import "missing-native-js-functions"; -import { Readable } from "stream"; -import ExifTransformer = require("exif-be-gone"); - -function getPath(path: string) { - // STORAGE_LOCATION has a default value in start.ts - const root = process.env.STORAGE_LOCATION || "../"; - var filename = join(root, path); - - if (path.indexOf("\0") !== -1 || !filename.startsWith(root)) throw new Error("invalid path"); - return filename; -} - -export class FileStorage implements Storage { - async get(path: string): Promise<Buffer | null> { - try { - return fs.readFileSync(getPath(path)); - } catch (error) { - return null; - } - } - - async set(path: string, value: any) { - path = getPath(path); - fse.ensureDirSync(dirname(path)); - - value = Readable.from(value); - const cleaned_file = fs.createWriteStream(path); - - return value.pipe(new ExifTransformer()).pipe(cleaned_file); - } - - async delete(path: string) { - fs.unlinkSync(getPath(path)); - } -} diff --git a/src/util/Storage.ts b/src/util/Storage.ts deleted file mode 100644
index f8b09e71..00000000 --- a/src/util/Storage.ts +++ /dev/null
@@ -1,15 +0,0 @@ -import { FileStorage } from "./FileStorage"; - -export interface Storage { - set(path: string, data: Buffer): Promise<void>; - get(path: string): Promise<Buffer | null>; - delete(path: string): Promise<void>; -} - -var storage: Storage; - -if (process.env.STORAGE_PROVIDER === "file") { - storage = new FileStorage(); -} - -export { storage }; diff --git a/src/util/multer.ts b/src/util/multer.ts deleted file mode 100644
index bfdf6aff..00000000 --- a/src/util/multer.ts +++ /dev/null
@@ -1,10 +0,0 @@ -import multerConfig from "multer"; - -export const multer = multerConfig({ - storage: multerConfig.memoryStorage(), - limits: { - fields: 10, - files: 10, - fileSize: 1024 * 1024 * 100, // 100 mb - }, -});