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
  },
};