1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
import { Router, Response, Request } from "express";
import { Config, Snowflake } from "@fosscord/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";
import ffmpeg from "fluent-ffmpeg";
import Path from "path";
import { Duplex, Readable, Transform, Writable } from "stream";
const router = Router();
const SANITIZED_CONTENT_TYPE = [
"text/html",
"text/mhtml",
"multipart/related",
"application/xhtml+xml",
];
const probe = (file: string): Promise<ffmpeg.FfprobeData> => new Promise((resolve, reject) => {
ffmpeg.setFfprobePath(process.env.FFPROBE_PATH as string);
ffmpeg.ffprobe(file, (err, data) => {
if (err) return reject(err);
return resolve(data);
});
});
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) throw new HTTPError("file missing");
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.endpointPublic || "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;
}
}
else if (mimetype.includes("video") && process.env.FFPROBE_PATH) {
const root = process.env.STORAGE_LOCATION || "../"; // hmm, stolen from FileStorage
const out = await probe(Path.join(root, path));
const stream = out.streams[0]; // hmm
width = stream.width;
height = stream.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 { format } = req.query;
const path = `attachments/${channel_id}/${id}/${filename}`;
let file = await storage.get(path);
if (!file) throw new HTTPError("File not found");
const type = await FileType.fromBuffer(file);
let content_type = type?.mime || "application/octet-stream";
if (SANITIZED_CONTENT_TYPE.includes(content_type)) {
content_type = "application/octet-stream";
}
// lol, super gross
if (content_type.includes("video") && format == "jpeg" && process.env.FFMPEG_PATH) {
const promise = (): Promise<Buffer> => new Promise((resolve, reject) => {
ffmpeg.setFfmpegPath(process.env.FFMPEG_PATH as string);
const out: any[] = [];
const cmd = ffmpeg(Readable.from(file as Buffer))
.format("mjpeg")
.frames(1)
.on("end", () => resolve(Buffer.concat(out)))
.on("error", (err) => reject(err))
const stream = cmd.pipe();
stream.on("data", (data) => {
out.push(data)
});
});
const res = await promise();
file = res;
content_type = "jpeg";
}
res.set("Content-Type", content_type);
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;
|