mirror of
https://github.com/detleph/server.git
synced 2026-09-04 08:36:06 +02:00
Add controller and routes to upload images
+ Checks for valid image formats + Limits file size + Requires authentication + Disabled for now
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^3.3.0",
|
"@prisma/client": "^3.3.0",
|
||||||
"@types/handlebars": "^4.1.0",
|
"@types/handlebars": "^4.1.0",
|
||||||
|
"@types/express-fileupload": "^1.2.1",
|
||||||
"@types/jsonwebtoken": "^8.5.5",
|
"@types/jsonwebtoken": "^8.5.5",
|
||||||
"@types/mjml": "^4.7.0",
|
"@types/mjml": "^4.7.0",
|
||||||
"@types/node": "^16.10.3",
|
"@types/node": "^16.10.3",
|
||||||
@@ -31,6 +32,9 @@
|
|||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"express-async-errors": "^3.1.1",
|
"express-async-errors": "^3.1.1",
|
||||||
|
"express-fileupload": "^1.2.1",
|
||||||
|
"file-type": "^16.5.3",
|
||||||
|
"is-svg": "^4.3.2",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"mjml": "^4.11.0",
|
"mjml": "^4.11.0",
|
||||||
"nanoid": "^3.3.3",
|
"nanoid": "^3.3.3",
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import { Request, Response } from "express";
|
||||||
|
import fs from "fs";
|
||||||
|
import isSvg from "is-svg";
|
||||||
|
import { fromBuffer as fileTypeFromBuffer } from "file-type";
|
||||||
|
|
||||||
|
export const uploadImage = async (req: Request, res: Response) => {
|
||||||
|
if (!req.auth?.isAuthenticated) {
|
||||||
|
return res.status(500).json({
|
||||||
|
type: "failure",
|
||||||
|
payload: {
|
||||||
|
message: "The server was not able to validate your credentials; Please try again later",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(req.auth.permission_level === "ELEVATED")) {
|
||||||
|
return res.status(403).json({
|
||||||
|
type: "error",
|
||||||
|
payload: {
|
||||||
|
message: "You do not have sufficient permissions to use this feature",
|
||||||
|
required_level: "ELEVATED",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.files || !("file" in req.files)) {
|
||||||
|
return res.json({
|
||||||
|
type: "error",
|
||||||
|
payload: {
|
||||||
|
message: "The format of the data was not valid",
|
||||||
|
schema: {
|
||||||
|
multipart: {
|
||||||
|
file: "binary file",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = req.files.file;
|
||||||
|
|
||||||
|
if (Array.isArray(file)) {
|
||||||
|
return res.status(500).json({
|
||||||
|
type: "failure",
|
||||||
|
payload: {
|
||||||
|
message: "Invalid application state",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.size > 1024 * 1024) {
|
||||||
|
// Maybe resize (express-fileupload seems to support this)
|
||||||
|
return res.status(413).json({
|
||||||
|
type: "error",
|
||||||
|
payload: {
|
||||||
|
message: "The uploaded image is too large",
|
||||||
|
max_size: "1 MB",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileIsSvg = isSvg(file.data);
|
||||||
|
const fileType = await fileTypeFromBuffer(file.data); // Could be optimized
|
||||||
|
|
||||||
|
if (!(fileType && ["image/jpeg", "image/png"].includes(fileType.mime))) {
|
||||||
|
if (!fileIsSvg)
|
||||||
|
return res.status(415).json({
|
||||||
|
type: "error",
|
||||||
|
payload: {
|
||||||
|
message:
|
||||||
|
"The uploaded image does not satisfy the MIME type constraints (Only image/jpeg, image/png and SVG files are accepted)",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileName = file.md5 + (fileIsSvg ? ".svg" : "." + fileType?.ext);
|
||||||
|
|
||||||
|
if (fs.existsSync("media/" + fileName)) {
|
||||||
|
return res.status(409).json({
|
||||||
|
type: "error",
|
||||||
|
payload: {
|
||||||
|
message: "The uploaded file already exists",
|
||||||
|
},
|
||||||
|
_links: [
|
||||||
|
{
|
||||||
|
rel: "self",
|
||||||
|
type: "GET",
|
||||||
|
href: "/api/media/" + fileName,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
file.mv("media/" + fileName, console.error);
|
||||||
|
|
||||||
|
return res.status(201).json({
|
||||||
|
type: "success",
|
||||||
|
payload: {
|
||||||
|
message: "The file was uploaded and created on the server",
|
||||||
|
},
|
||||||
|
_links: [
|
||||||
|
{
|
||||||
|
rel: "self",
|
||||||
|
type: "GET",
|
||||||
|
href: "/api/media/" + fileName,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
|
import fileUpload from "express-fileupload";
|
||||||
|
import { requireAuthentication } from "../Middleware/auth/auth";
|
||||||
|
import { uploadImage } from "../Controllers/media.controller";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.post("/", requireAuthentication, fileUpload(), uploadImage);
|
||||||
|
|
||||||
// Media storage with express static (Should only handle GET and HEAD request methods)
|
// Media storage with express static (Should only handle GET and HEAD request methods)
|
||||||
router.use("/", express.static("media", { redirect: false }));
|
router.use("/", express.static("media", { redirect: false }));
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -52,7 +52,6 @@ async function main() {
|
|||||||
|
|
||||||
app.use("/api/admins", adminRouter);
|
app.use("/api/admins", adminRouter);
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
app.use("/api/organisations", organisationRouter);
|
app.use("/api/organisations", organisationRouter);
|
||||||
|
|
||||||
app.use("/api/groups", groupRouter);
|
app.use("/api/groups", groupRouter);
|
||||||
@@ -63,9 +62,11 @@ async function main() {
|
|||||||
|
|
||||||
// Error handling
|
// Error handling
|
||||||
app.use(defaultErrorHandler); // Not working
|
app.use(defaultErrorHandler); // Not working
|
||||||
=======
|
|
||||||
app.use("/api/media", mediaRouter);
|
app.use("/api/media", mediaRouter);
|
||||||
>>>>>>> Add express.static() to serve semi-static files
|
|
||||||
|
// Disable the media router for now
|
||||||
|
// app.use("/api/media", mediaRouter);
|
||||||
|
|
||||||
app.listen(process.env.PORT, () => {
|
app.listen(process.env.PORT, () => {
|
||||||
logger.info(`Listening on port ${process.env.PORT}`);
|
logger.info(`Listening on port ${process.env.PORT}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user