Files
sap-erp/routes/projects.js
T
John 69b4e68baf
SAP-ERP Portal CI/CD / build (push) Failing after 5m20s
first commit
2026-09-23 17:31:02 +05:30

120 lines
5.7 KiB
JavaScript

'use strict';
const express = require('express');
const router = express.Router();
const path = require('path');
const fs = require('fs');
const multer = require('multer');
const { verifyToken, hasStepPerm } = require('../middleware/auth');
const store = () => require('../services/projectStore');
// ── file upload (multer) ──────────────────────────────────────────────────────
// uploads/ is resolved from the CWD (the app root — every start script cd's
// there first), NOT __dirname: after esbuild bundling __dirname becomes the
// bundle's own folder, which would break the ../uploads hop.
const UPLOADS_ROOT = path.resolve('uploads');
const storage = multer.diskStorage({
destination(req, file, cb) {
const dir = path.join(UPLOADS_ROOT, 'projects', String(req.params.id));
fs.mkdirSync(dir, { recursive: true });
cb(null, dir);
},
filename(req, file, cb) {
const safe = file.originalname.replace(/[^a-zA-Z0-9._-]/g, '_');
cb(null, Date.now() + '-' + safe);
},
});
const upload = multer({ storage, limits: { fileSize: 20 * 1024 * 1024 } });
// ── next code preview ─────────────────────────────────────────────────────────
router.get('/next-code', verifyToken, async (req, res) => {
try {
res.json({ success: true, code: await store().nextCode() });
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
});
// ── CRUD ──────────────────────────────────────────────────────────────────────
router.get('/', verifyToken, async (req, res) => {
try {
res.json({ success: true, data: await store().list(req.query) });
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
});
router.post('/', verifyToken, async (req, res) => {
try {
if (!req.body.project_name)
return res.status(400).json({ success: false, message: 'Project name is required' });
const result = await store().create({ ...req.body, submitted_by: req.user.username });
res.json({ success: true, ...result });
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
});
router.get('/:id', verifyToken, async (req, res) => {
try {
const p = await store().findById(req.params.id);
if (!p) return res.status(404).json({ success: false, message: 'Not found' });
res.json({ success: true, data: p });
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
});
router.patch('/:id', verifyToken, async (req, res) => {
try {
await store().update(req.params.id, req.body);
res.json({ success: true, message: 'Updated' });
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
});
router.post('/:id/approve', verifyToken, async (req, res) => {
try {
const step = req.body?.step;
if (!step || !hasStepPerm(req.user, `project:${step}`, 'approve'))
return res.status(403).json({ success: false, message: `You are not assigned "approve" on approval step: project:${step || '?'}` });
await store().stepApprove(req.params.id, req.body);
res.json({ success: true });
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
});
// ── file upload ───────────────────────────────────────────────────────────────
router.post('/:id/upload', verifyToken, upload.array('files', 10), async (req, res) => {
try {
const p = await store().findById(req.params.id);
if (!p) return res.status(404).json({ success: false, message: 'Project not found' });
const existing = Array.isArray(p.attachments) ? p.attachments : [];
const newFiles = (req.files || []).map(f => ({
name: f.filename,
originalName: f.originalname,
size: f.size,
url: `/uploads/projects/${req.params.id}/${f.filename}`,
}));
await store().update(req.params.id, { attachments: [...existing, ...newFiles] });
res.json({ success: true, files: newFiles });
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
});
router.delete('/:id/attachments/:filename', verifyToken, async (req, res) => {
try {
const p = await store().findById(req.params.id);
if (!p) return res.status(404).json({ success: false, message: 'Project not found' });
const existing = Array.isArray(p.attachments) ? p.attachments : [];
const updated = existing.filter(f => f.name !== req.params.filename);
await store().update(req.params.id, { attachments: updated });
// delete file from disk
const filePath = path.join(UPLOADS_ROOT, 'projects', String(req.params.id), req.params.filename);
fs.unlink(filePath, () => {});
res.json({ success: true });
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
});
router.delete('/:id', verifyToken, async (req, res) => {
try {
const p = await store().findById(req.params.id);
if (!p) return res.status(404).json({ success: false, message: 'Not found' });
if (p.status !== 'DRAFT' && req.user.role !== 'admin')
return res.status(403).json({ success: false, message: 'Only DRAFT projects can be deleted' });
await store().remove(req.params.id);
res.json({ success: true });
} catch (err) { res.status(500).json({ success: false, message: err.message }); }
});
module.exports = router;