fix permits UI and content upload/delete API
This commit is contained in:
@ -1,12 +1,72 @@
|
|||||||
import path from 'path';
|
|
||||||
import { promises as fs } from 'fs';
|
|
||||||
import express from 'express';
|
|
||||||
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import nc from 'next-connect';
|
import { promises as fs } from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import multer from 'multer';
|
||||||
|
import sharp from 'sharp';
|
||||||
|
import { createRouter } from 'next-connect';
|
||||||
|
|
||||||
const handler = nc({
|
|
||||||
onError: (err, req, res, next) => {
|
// Generalized Multer configuration
|
||||||
|
const storage = multer.diskStorage({
|
||||||
|
destination: (req, file, cb) => {
|
||||||
|
const uploadPath = path.join(process.cwd(), 'public/content', req.query.subfolder as string);
|
||||||
|
fs.mkdir(uploadPath, { recursive: true }).then(() => cb(null, uploadPath)).catch(cb);
|
||||||
|
},
|
||||||
|
filename: (req, file, cb) => {
|
||||||
|
const prefix = req.body.prefix || path.parse(file.originalname).name;
|
||||||
|
cb(null, `${prefix}${path.extname(file.originalname)}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileFilter = (req, file, cb) => {
|
||||||
|
// Accept PDFs only
|
||||||
|
if (file.mimetype === 'application/pdf') {
|
||||||
|
cb(null, true);
|
||||||
|
} else {
|
||||||
|
cb(new Error('Only PDF files are allowed!'), false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const upload = multer({ storage, fileFilter });
|
||||||
|
|
||||||
|
const router = createRouter<NextApiRequest, NextApiResponse>();
|
||||||
|
|
||||||
|
router.use(upload.array('file'));
|
||||||
|
|
||||||
|
router.post((req, res) => {
|
||||||
|
console.log(req.files); // Log files to see if PDFs are included
|
||||||
|
if (req.files.length === 0) {
|
||||||
|
return res.status(400).json({ error: 'No files were uploaded.' });
|
||||||
|
}
|
||||||
|
// Process uploaded files, assume images are being resized and saved
|
||||||
|
res.json({ message: 'Files uploaded successfully', files: req.files });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get(async (req, res) => {
|
||||||
|
// Implement functionality to list files
|
||||||
|
const directory = path.join(process.cwd(), 'public/content', req.query.subfolder as string);
|
||||||
|
try {
|
||||||
|
const files = await fs.readdir(directory);
|
||||||
|
const imageUrls = files.map(file => `/content/${req.query.subfolder}/${file}`);
|
||||||
|
res.json({ imageUrls });
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({ error: 'Internal Server Error', details: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete(async (req, res) => {
|
||||||
|
// Implement functionality to delete a file
|
||||||
|
const filePath = path.join(process.cwd(), 'public/content', req.query.subfolder as string, req.query.file as string);
|
||||||
|
try {
|
||||||
|
await fs.unlink(filePath);
|
||||||
|
res.send('File deleted successfully.');
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).send('Failed to delete the file.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router.handler({
|
||||||
|
onError: (err, req, res) => {
|
||||||
console.error(err.stack);
|
console.error(err.stack);
|
||||||
res.status(500).end('Something broke!');
|
res.status(500).end('Something broke!');
|
||||||
},
|
},
|
||||||
@ -15,131 +75,8 @@ const handler = nc({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.use((req: NextApiRequest, res: NextApiResponse, next) => {
|
|
||||||
const subfolder = req.query.subfolder as string;
|
|
||||||
const upload = createUploadMiddleware(subfolder).array('image');
|
|
||||||
upload(req, res, (err) => {
|
|
||||||
if (err) {
|
|
||||||
return res.status(500).json({ error: 'Failed to upload files.', details: err.message });
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
handler.post((req: NextApiRequest, res: NextApiResponse) => {
|
|
||||||
// Process uploaded files
|
|
||||||
// Example response
|
|
||||||
res.json({ message: 'Files uploaded successfully', files: req.files });
|
|
||||||
});
|
|
||||||
|
|
||||||
handler.get((req: NextApiRequest, res: NextApiResponse) => {
|
|
||||||
// Handle listing files
|
|
||||||
//listFiles(req, res, req.subfolder);
|
|
||||||
listFiles(req, res, req.query.subfolder as string);
|
|
||||||
});
|
|
||||||
|
|
||||||
handler.delete((req: NextApiRequest, res: NextApiResponse) => {
|
|
||||||
// Handle deleting files
|
|
||||||
deleteFile(req, res, req.query.subfolder as string);
|
|
||||||
});
|
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
bodyParser: false,
|
bodyParser: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handler;
|
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
//handling file uploads
|
|
||||||
import multer from 'multer';
|
|
||||||
import sharp from 'sharp';
|
|
||||||
|
|
||||||
// Generalized Multer configuration
|
|
||||||
export const createUploadMiddleware = (folder: string) => {
|
|
||||||
const storage = multer.diskStorage({
|
|
||||||
destination: (req, file, cb) => {
|
|
||||||
const uploadPath = path.join(process.cwd(), 'public/content', folder);
|
|
||||||
if (!fs.existsSync(uploadPath)) {
|
|
||||||
fs.mkdirSync(uploadPath, { recursive: true });
|
|
||||||
}
|
|
||||||
cb(null, uploadPath);
|
|
||||||
},
|
|
||||||
filename: (req, file, cb) => {
|
|
||||||
const prefix = req.body.prefix || path.parse(file.originalname).name;
|
|
||||||
cb(null, `${prefix}${path.extname(file.originalname)}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return multer({ storage });
|
|
||||||
};
|
|
||||||
|
|
||||||
async function processFiles(req, res, folder) {
|
|
||||||
if (!req.files || req.files.length === 0) {
|
|
||||||
return res.status(400).json({ error: 'No files uploaded.' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const uploadDir = path.join(process.cwd(), 'public/content', folder);
|
|
||||||
const thumbDir = path.join(uploadDir, "thumb");
|
|
||||||
|
|
||||||
if (!fs.existsSync(thumbDir)) {
|
|
||||||
fs.mkdirSync(thumbDir, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const processedFiles = await Promise.all(req.files.map(async (file) => {
|
|
||||||
const originalPath = path.join(uploadDir, file.filename);
|
|
||||||
const thumbPath = path.join(thumbDir, file.filename);
|
|
||||||
|
|
||||||
await sharp(file.path)
|
|
||||||
.resize({ width: 1920, fit: sharp.fit.inside, withoutEnlargement: true })
|
|
||||||
.jpeg({ quality: 80 })
|
|
||||||
.toFile(originalPath);
|
|
||||||
|
|
||||||
await sharp(file.path)
|
|
||||||
.resize(320, 320, { fit: sharp.fit.inside, withoutEnlargement: true })
|
|
||||||
.toFile(thumbPath);
|
|
||||||
|
|
||||||
fs.unlinkSync(file.path); // Remove temp file
|
|
||||||
|
|
||||||
return {
|
|
||||||
originalUrl: `/content/${folder}/${file.filename}`,
|
|
||||||
thumbUrl: `/content/${folder}/thumb/${file.filename}`
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
|
|
||||||
res.json(processedFiles);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error processing files:', error);
|
|
||||||
res.status(500).json({ error: 'Error processing files.' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// List files in a directory
|
|
||||||
async function listFiles(req, res, folder) {
|
|
||||||
const directory = path.join(process.cwd(), 'public/content', folder);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const files = await fs.promises.readdir(directory);
|
|
||||||
const imageUrls = files.map(file => `${req.protocol}://${req.get('host')}/content/${folder}/${file}`);
|
|
||||||
res.json({ imageUrls });
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error reading uploads directory:', err);
|
|
||||||
res.status(500).json({ error: 'Internal Server Error' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete a file
|
|
||||||
async function deleteFile(req, res, folder) {
|
|
||||||
const filename = req.query.file;
|
|
||||||
if (!filename) {
|
|
||||||
return res.status(400).send('Filename is required.');
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const filePath = path.join(process.cwd(), 'public/content', folder, filename);
|
|
||||||
await fs.unlink(filePath);
|
|
||||||
res.status(200).send('File deleted successfully.');
|
|
||||||
} catch (error) {
|
|
||||||
res.status(500).send('Failed to delete the file.');
|
|
||||||
}
|
|
||||||
}
|
|
@ -44,17 +44,25 @@ const PDFViewerPage = ({ pdfFiles }) => {
|
|||||||
<Layout>
|
<Layout>
|
||||||
<h1 className="text-3xl font-bold p-4 pt-8">Разрешителни</h1>
|
<h1 className="text-3xl font-bold p-4 pt-8">Разрешителни</h1>
|
||||||
<ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage="">
|
<ProtectedRoute allowedRoles={[UserRole.ADMIN]} deniedMessage="">
|
||||||
<input type="file" onChange={handleFileUpload} className="mb-4" />
|
<div className="border border-blue-500 border-solid p-2">
|
||||||
{files.map((file, index) => (
|
<div className="mb-4">
|
||||||
<div key={file.name} className="py-2">
|
За да качите файл кликнете на бутона по-долу и изберете файл от вашия компютър.
|
||||||
<a href={file.url} className="text-blue-600 hover:text-blue-800 visited:text-purple-600 underline" target='_blank'>
|
|
||||||
{file.name}
|
|
||||||
</a>
|
|
||||||
<button onClick={() => handleFileDelete(file.name)} className="ml-4 bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 rounded">
|
|
||||||
изтрий
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
<input type="file" onChange={handleFileUpload} className="mb-4" />
|
||||||
|
<div className="mb-4">
|
||||||
|
Съществуващи файлове:
|
||||||
|
</div>
|
||||||
|
{files.map((file, index) => (
|
||||||
|
<div key={file.name} className="py-2">
|
||||||
|
<a href={file.url} className="text-blue-600 hover:text-blue-800 visited:text-purple-600 underline" target='_blank'>
|
||||||
|
{file.name}
|
||||||
|
</a>
|
||||||
|
<button onClick={() => handleFileDelete(file.name)} className="ml-4 bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 rounded">
|
||||||
|
изтрий
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</ProtectedRoute>
|
</ProtectedRoute>
|
||||||
|
|
||||||
<div style={{ width: '100%', height: 'calc(100vh - 100px)' }}> {/* Adjust the 100px based on your header/footer size */}
|
<div style={{ width: '100%', height: 'calc(100vh - 100px)' }}> {/* Adjust the 100px based on your header/footer size */}
|
||||||
|
Reference in New Issue
Block a user