Spaces:
Sleeping
Sleeping
File size: 3,025 Bytes
5878f15 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
// 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
},
}; |