wip permits file managment for admins
This commit is contained in:
@ -1,15 +1,123 @@
|
||||
import path from 'path';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
export default async function handler(req, res) {
|
||||
//Find the absolute path of the json directory and the requested file contents
|
||||
const jsonDirectory = path.join(process.cwd(), 'content');
|
||||
const requestedFile = req.query.nextcrud[0];
|
||||
const fileContents = await fs.readFile(path.join(jsonDirectory, requestedFile), 'utf8');
|
||||
// try to determine the content type from the file extension
|
||||
const contentType = requestedFile.endsWith('.json') ? 'application/json' : 'text/plain';
|
||||
// return the file contents with the appropriate content type
|
||||
res.status(200).setHeader('Content-Type', contentType).end(fileContents);
|
||||
import express from 'express';
|
||||
import { createUploadMiddleware, processFiles, listFiles, deleteFile } from './fileHandlers';
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Dynamic routing to handle different content types based on the subfolder
|
||||
router.use('/:subfolder', (req, res, next) => {
|
||||
const { subfolder } = req.params;
|
||||
req.subfolder = subfolder; // Pass the subfolder as part of the request for later use
|
||||
next();
|
||||
});
|
||||
|
||||
// POST: Upload files to a specific subfolder
|
||||
router.post('/:subfolder', createUploadMiddleware(req.subfolder).array('image'), (req, res) => {
|
||||
processFiles(req, res, req.subfolder);
|
||||
});
|
||||
|
||||
// GET: List files from a specific subfolder
|
||||
router.get('/:subfolder', (req, res) => {
|
||||
listFiles(req, res, req.subfolder);
|
||||
});
|
||||
|
||||
// DELETE: Delete a specific file from a subfolder
|
||||
router.delete('/:subfolder', (req, res) => {
|
||||
deleteFile(req, res, req.subfolder);
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
//handling file uploads
|
||||
import multer from 'multer';
|
||||
import sharp from 'sharp';
|
||||
|
||||
// Generalized Multer configuration
|
||||
export function createUploadMiddleware(folder) {
|
||||
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: 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.');
|
||||
}
|
||||
}
|
@ -3,35 +3,57 @@ import Layout from "../components/layout";
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { url } from 'inspector';
|
||||
import ProtectedRoute, { serverSideAuth } from "/components/protectedRoute";
|
||||
|
||||
const PDFViewerPage = ({ pdfFiles }) => {
|
||||
const [files, setFiles] = useState(pdfFiles);
|
||||
|
||||
const handleFileDelete = async (fileName) => {
|
||||
try {
|
||||
await axios.delete(`/api/delete-file?name=${fileName}`);
|
||||
setFiles(files.filter(file => file.name !== fileName));
|
||||
} catch (error) {
|
||||
console.error('Error deleting file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileUpload = async (event) => {
|
||||
const file = event.target.files[0];
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
try {
|
||||
const response = await axios.post('/api/upload-file', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
setFiles([...files, response.data]);
|
||||
} catch (error) {
|
||||
console.error('Error uploading file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<h1 className="text-3xl font-bold p-4 pt-8">Разрешителни</h1>
|
||||
<div style={{ width: '100%', height: 'calc(100vh - 100px)' }}> {/* Adjust the 100px based on your header/footer size */}
|
||||
{/* <p className="p-1">
|
||||
{pdfFiles.map((file, index) => (
|
||||
<p className="p-2">
|
||||
<a href={file.url} className="text-blue-600 hover:text-blue-800 visited:text-purple-600 underline" target='_blank'>
|
||||
Свали: {file.name}
|
||||
</a>
|
||||
</p>
|
||||
))}
|
||||
</p> */}
|
||||
{pdfFiles.map((file, index) => (
|
||||
<ProtectedRoute>
|
||||
|
||||
// <React.Fragment key={file.name}>
|
||||
// {index > 0 && <div className="bg-gray-400 w-px h-6"></div>} {/* Vertical line separator */}
|
||||
// <a
|
||||
// href={file.url}
|
||||
// target="_blank"
|
||||
// className={`text-lg py-2 px-4 bg-gray-200 text-gray-800 hover:bg-blue-500 hover:text-white ${index === 0 ? 'rounded-l-full' : index === pdfFiles.length - 1 ? 'rounded-r-full' : ''}`}
|
||||
// >
|
||||
// {file.name}
|
||||
// </a>
|
||||
// </React.Fragment>
|
||||
<input type="file" onChange={handleFileUpload} className="mb-4" />
|
||||
{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>
|
||||
))}
|
||||
</ProtectedRoute>
|
||||
|
||||
<div style={{ width: '100%', height: 'calc(100vh - 100px)' }}> {/* Adjust the 100px based on your header/footer size */}
|
||||
{pdfFiles.map((file, index) => (
|
||||
<> <p className="pt-2">
|
||||
<a href={file.url} className="text-blue-600 hover:text-blue-800 visited:text-purple-600 underline" target='_blank'>
|
||||
Свали: {file.name}
|
||||
|
BIN
public/content/permits/4 - Разрешително за Април - 24г..pdf
Normal file
BIN
public/content/permits/4 - Разрешително за Април - 24г..pdf
Normal file
Binary file not shown.
Reference in New Issue
Block a user