- Replace Express 4 backend with Express 5 + Mongoose 8 + TypeScript 5.8 - Add MongoDB 8.0 container via docker-compose - Add JWT auth (jose), argon2 password hashing, zod validation, multer uploads - Models: BoardMember, Event, GalleryItem, Sponsor, Minute, Member, User - Full CRUD REST API for all content types - Seed script to populate MongoDB from hardcoded data - Wire all frontend components to fetch from API (no JSX changes) - Add /manage dashboard: login + tabs for Board, Events, Gallery, Sponsors, Minutes, Members - Uploads volume for persistent file storage across deploys
33 lines
1.5 KiB
TypeScript
33 lines
1.5 KiB
TypeScript
import multer from 'multer';
|
|
import path from 'path';
|
|
import fs from 'fs';
|
|
|
|
const uploadsBase = path.join(__dirname, '../../../uploads');
|
|
|
|
function storageFor(subdir: string) {
|
|
return multer.diskStorage({
|
|
destination: (_req, _file, cb) => {
|
|
const dir = path.join(uploadsBase, subdir);
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
cb(null, dir);
|
|
},
|
|
filename: (_req, file, cb) => {
|
|
const unique = `${Date.now()}-${Math.round(Math.random() * 1e6)}`;
|
|
cb(null, unique + path.extname(file.originalname));
|
|
},
|
|
});
|
|
}
|
|
|
|
const imageFilter: multer.Options['fileFilter'] = (_req, file, cb) => {
|
|
const allowed = /jpeg|jpg|png|gif|webp/;
|
|
cb(null, allowed.test(path.extname(file.originalname).toLowerCase()) && allowed.test(file.mimetype));
|
|
};
|
|
|
|
const pdfFilter: multer.Options['fileFilter'] = (_req, file, cb) => {
|
|
cb(null, file.mimetype === 'application/pdf' || path.extname(file.originalname).toLowerCase() === '.pdf');
|
|
};
|
|
|
|
export const uploadHeadshot = multer({ storage: storageFor('headshots'), fileFilter: imageFilter, limits: { fileSize: 5 * 1024 * 1024 } });
|
|
export const uploadPhoto = multer({ storage: storageFor('photos'), fileFilter: imageFilter, limits: { fileSize: 10 * 1024 * 1024 } });
|
|
export const uploadLogo = multer({ storage: storageFor('sponsors'), fileFilter: imageFilter, limits: { fileSize: 5 * 1024 * 1024 } });
|
|
export const uploadMinutes = multer({ storage: storageFor('minutes'), fileFilter: pdfFilter, limits: { fileSize: 20 * 1024 * 1024 } });
|