135 lines
3.3 KiB
TypeScript
135 lines
3.3 KiB
TypeScript
import express from "express";
|
|
import multer, { FileFilterCallback } from "multer";
|
|
import sizeOf from "image-size";
|
|
import * as fsp from "fs/promises";
|
|
import * as path from "path";
|
|
const app = express();
|
|
const publicDirname = path.join(__dirname, "../public");
|
|
const uploadDirname = path.join(__dirname, "../uploads");
|
|
const MAX_FILE_SIZE_IN_BYTES = 15 * 1_000_000; // 15 mb
|
|
const DEVELOPMENT_PORT = 8000;
|
|
const IMAGE_SQUARE_VALIDATION_TOLERENCE = 0.2;
|
|
|
|
import allowedUsers from "./allowedUsers.json";
|
|
|
|
import * as fs from "fs";
|
|
import * as dotenv from "dotenv";
|
|
import { makePullRequest } from "./giteaClient";
|
|
import { validateForm } from "./handleForm";
|
|
|
|
dotenv.config();
|
|
|
|
const devQuestId = "cdalek";
|
|
|
|
const validateAuthorization = (req, res, next) => {
|
|
// TODO: Replace with actual quest id from request
|
|
if (!allowedUsers.includes(devQuestId)) {
|
|
res.status(401);
|
|
res.sendFile(path.join(publicDirname, "unauthorized.html"));
|
|
} else {
|
|
return next();
|
|
}
|
|
};
|
|
|
|
// Validate user's quest id
|
|
app.use(validateAuthorization);
|
|
|
|
// Configure multer for file uploads
|
|
const storage = multer.diskStorage({
|
|
destination: function (req, file, callback) {
|
|
callback(null, uploadDirname);
|
|
},
|
|
filename: function (req, file, callback) {
|
|
callback(null, Date.now() + "--" + file.originalname);
|
|
},
|
|
});
|
|
|
|
const fileFilter = async (
|
|
_,
|
|
file: Express.Multer.File,
|
|
callback: FileFilterCallback
|
|
) => {
|
|
let mimeType = file.mimetype.toLowerCase();
|
|
if (
|
|
!mimeType.includes("jpeg") &&
|
|
!mimeType.includes("png") &&
|
|
!mimeType.includes("jpg")
|
|
) {
|
|
callback(
|
|
Error("Invalid file type! Only JPEG and PNG files are supported!")
|
|
);
|
|
} else {
|
|
callback(null, true);
|
|
}
|
|
};
|
|
|
|
const validateImageIsSquare = async(
|
|
file: Express.Multer.File
|
|
) => {
|
|
const dimensions = sizeOf(file.path);
|
|
const ratio = dimensions.height / dimensions.width;
|
|
|
|
return ratio < 1 + IMAGE_SQUARE_VALIDATION_TOLERENCE && ratio > 1 - IMAGE_SQUARE_VALIDATION_TOLERENCE;
|
|
}
|
|
|
|
let upload = multer({
|
|
storage: storage,
|
|
fileFilter: fileFilter,
|
|
limits: { fileSize: MAX_FILE_SIZE_IN_BYTES },
|
|
});
|
|
|
|
// Routes
|
|
app.get("/", async (req, res) => {
|
|
res.sendFile(path.join(publicDirname, "index.html"));
|
|
|
|
// TODO: Implement this for www
|
|
// makePullRequest();
|
|
});
|
|
|
|
app.post("/event", upload.single("poster"), async (req, res) => {
|
|
const { name, short, long, start, end, register, location, online } =
|
|
req.body;
|
|
|
|
const validationStatus = validateForm(
|
|
name,
|
|
short,
|
|
long,
|
|
start,
|
|
end,
|
|
register,
|
|
location,
|
|
online
|
|
);
|
|
|
|
if(!(await validateImageIsSquare(req.file))) {
|
|
// Delete the file
|
|
await fsp.unlink(req.file.path);
|
|
return res.status(400).send("The poster photo must be approximately square.")
|
|
}
|
|
|
|
|
|
if (validationStatus === true) {
|
|
res.send(
|
|
"The event has been validated (change it to added after finished) successfully."
|
|
);
|
|
// Perform API request
|
|
} else {
|
|
res.status(400).send(validationStatus);
|
|
}
|
|
});
|
|
|
|
function initUploadDir() {
|
|
// Create upload directory if it doesn't exist already
|
|
if (!fs.existsSync(uploadDirname)) {
|
|
fs.mkdirSync(uploadDirname);
|
|
}
|
|
}
|
|
|
|
// Allows serving static files
|
|
app.use(express.static(publicDirname));
|
|
initUploadDir();
|
|
|
|
app.listen(DEVELOPMENT_PORT, () => {
|
|
console.log(`Listening on http://localhost:${DEVELOPMENT_PORT}`);
|
|
});
|