Dafa
Upload 7 files
5878f15 verified
// pages/api/jekyll/static/[...path].js
// Serve static Jekyll files
import path from 'path';
import fs from 'fs-extra';
import { lookup } from 'mime-types';
export default async function handler(req, res) {
try {
const { path: pathArray } = req.query;
if (!pathArray || !Array.isArray(pathArray) || pathArray.length === 0) {
return res.status(400).json({ error: 'Invalid path' });
}
const [siteName, ...filePath] = pathArray;
const requestedFile = filePath.join('/') || 'index.html';
// Security: prevent path traversal
if (siteName.includes('..') || requestedFile.includes('..')) {
return res.status(403).json({ error: 'Access denied' });
}
const sitePath = path.join('/app/projects', siteName, '_site');
const fullPath = path.join(sitePath, requestedFile);
// Check if site exists
if (!(await fs.pathExists(sitePath))) {
return res.status(404).json({ error: 'Site not found' });
}
// Try to find the file
let targetFile = fullPath;
// If requesting a directory, try index.html
if ((await fs.pathExists(fullPath)) && (await fs.stat(fullPath)).isDirectory()) {
targetFile = path.join(fullPath, 'index.html');
}
// If file doesn't exist, try adding .html extension
if (!(await fs.pathExists(targetFile))) {
targetFile = fullPath + '.html';
}
// Final check
if (!(await fs.pathExists(targetFile))) {
return res.status(404).json({ error: 'File not found' });
}
// Security: ensure file is within site directory
const resolvedPath = path.resolve(targetFile);
const resolvedSitePath = path.resolve(sitePath);
if (!resolvedPath.startsWith(resolvedSitePath)) {
return res.status(403).json({ error: 'Access denied' });
}
// Get file info
const stats = await fs.stat(targetFile);
const mimeType = lookup(targetFile) || 'application/octet-stream';
// Set headers
res.setHeader('Content-Type', mimeType);
res.setHeader('Content-Length', stats.size);
res.setHeader('Last-Modified', stats.mtime.toUTCString());
res.setHeader('Cache-Control', 'public, max-age=3600');
// Handle conditional requests
const ifModifiedSince = req.headers['if-modified-since'];
if (ifModifiedSince && new Date(ifModifiedSince) >= stats.mtime) {
return res.status(304).end();
}
// Stream the file
const fileStream = fs.createReadStream(targetFile);
fileStream.pipe(res);
fileStream.on('error', (error) => {
console.error('Error streaming file:', error);
if (!res.headersSent) {
res.status(500).json({ error: 'Error reading file' });
}
});
} catch (error) {
console.error('Error serving static file:', error);
if (!res.headersSent) {
res.status(500).json({
error: 'Internal server error',
details: error.message
});
}
}
}
export const config = {
api: {
responseLimit: '10mb', // Allow larger files
},
};