updateweb
This commit is contained in:
207
backend/routes/upload.js
Normal file
207
backend/routes/upload.js
Normal file
@@ -0,0 +1,207 @@
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const multer = require("multer");
|
||||
const path = require("path");
|
||||
const fs = require("fs").promises;
|
||||
const { requireAuth } = require("../middleware/auth");
|
||||
const { pool } = require("../config/database");
|
||||
|
||||
// Configure multer for file uploads
|
||||
const storage = multer.diskStorage({
|
||||
destination: async function (req, file, cb) {
|
||||
const uploadDir = path.join(__dirname, "..", "..", "website", "uploads");
|
||||
try {
|
||||
await fs.mkdir(uploadDir, { recursive: true });
|
||||
cb(null, uploadDir);
|
||||
} catch (error) {
|
||||
cb(error);
|
||||
}
|
||||
},
|
||||
filename: function (req, file, cb) {
|
||||
// Generate unique filename
|
||||
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1e9);
|
||||
const ext = path.extname(file.originalname);
|
||||
const name = path
|
||||
.basename(file.originalname, ext)
|
||||
.replace(/[^a-z0-9]/gi, "-")
|
||||
.toLowerCase();
|
||||
cb(null, name + "-" + uniqueSuffix + ext);
|
||||
},
|
||||
});
|
||||
|
||||
const upload = multer({
|
||||
storage: storage,
|
||||
limits: {
|
||||
fileSize: 5 * 1024 * 1024, // 5MB limit
|
||||
},
|
||||
fileFilter: function (req, file, cb) {
|
||||
// Accept images only
|
||||
if (!file.mimetype.startsWith("image/")) {
|
||||
return cb(new Error("Only image files are allowed!"), false);
|
||||
}
|
||||
cb(null, true);
|
||||
},
|
||||
});
|
||||
|
||||
// Upload multiple files
|
||||
router.post(
|
||||
"/upload",
|
||||
requireAuth,
|
||||
upload.array("files", 10),
|
||||
async (req, res) => {
|
||||
try {
|
||||
const uploadedBy = req.session.user?.id || null;
|
||||
const files = [];
|
||||
|
||||
// Insert each file into database
|
||||
for (const file of req.files) {
|
||||
const result = await pool.query(
|
||||
`INSERT INTO uploads
|
||||
(filename, original_name, file_path, file_size, mime_type, uploaded_by, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW())
|
||||
RETURNING id, filename, original_name, file_path, file_size, mime_type, created_at`,
|
||||
[
|
||||
file.filename,
|
||||
file.originalname,
|
||||
`/uploads/${file.filename}`,
|
||||
file.size,
|
||||
file.mimetype,
|
||||
uploadedBy,
|
||||
]
|
||||
);
|
||||
|
||||
files.push({
|
||||
id: result.rows[0].id,
|
||||
filename: result.rows[0].filename,
|
||||
originalName: result.rows[0].original_name,
|
||||
size: result.rows[0].file_size,
|
||||
mimetype: result.rows[0].mime_type,
|
||||
path: result.rows[0].file_path,
|
||||
uploadDate: result.rows[0].created_at,
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `${files.length} file(s) uploaded successfully`,
|
||||
files: files,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Upload error:", error);
|
||||
|
||||
// If database insert fails, clean up uploaded files
|
||||
if (req.files) {
|
||||
for (const file of req.files) {
|
||||
try {
|
||||
await fs.unlink(file.path);
|
||||
} catch (unlinkError) {
|
||||
console.error("Error cleaning up file:", unlinkError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Get all uploaded files
|
||||
router.get("/uploads", requireAuth, async (req, res) => {
|
||||
try {
|
||||
// Query files from database
|
||||
const result = await pool.query(
|
||||
`SELECT
|
||||
id,
|
||||
filename,
|
||||
original_name,
|
||||
file_path,
|
||||
file_size,
|
||||
mime_type,
|
||||
uploaded_by,
|
||||
created_at,
|
||||
updated_at,
|
||||
used_in_type,
|
||||
used_in_id
|
||||
FROM uploads
|
||||
ORDER BY created_at DESC`
|
||||
);
|
||||
|
||||
const files = result.rows.map((row) => ({
|
||||
id: row.id,
|
||||
filename: row.filename,
|
||||
originalName: row.original_name,
|
||||
size: row.file_size,
|
||||
mimetype: row.mime_type,
|
||||
path: row.file_path,
|
||||
uploadDate: row.created_at,
|
||||
uploadedBy: row.uploaded_by,
|
||||
usedInType: row.used_in_type,
|
||||
usedInId: row.used_in_id,
|
||||
}));
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
files: files,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error listing files:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Delete a file
|
||||
router.delete("/uploads/:filename", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const filename = req.params.filename;
|
||||
const uploadDir = path.join(__dirname, "..", "..", "website", "uploads");
|
||||
const filePath = path.join(uploadDir, filename);
|
||||
|
||||
// Security check: ensure file is within uploads directory
|
||||
if (!filePath.startsWith(uploadDir)) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
error: "Invalid file path",
|
||||
});
|
||||
}
|
||||
|
||||
// Start transaction: delete from database first
|
||||
const result = await pool.query(
|
||||
"DELETE FROM uploads WHERE filename = $1 RETURNING id",
|
||||
[filename]
|
||||
);
|
||||
|
||||
if (result.rowCount === 0) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: "File not found in database",
|
||||
});
|
||||
}
|
||||
|
||||
// Then delete physical file
|
||||
try {
|
||||
await fs.unlink(filePath);
|
||||
} catch (fileError) {
|
||||
console.warn("File already deleted from disk:", filename);
|
||||
// Continue anyway since database record is deleted
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: "File deleted successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error deleting file:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user