📄 beautiful_file_server.js

const http = require('http');
const fs = require('fs');
const path = require('path');

const PORT = 8081;
const DIRECTORY = '/data/ai-output';

const server = http.createServer((req, res) => {
    let pathname = req.url.split('?')[0];
    
    // URL解码
    try {
        pathname = decodeURIComponent(pathname);
    } catch (e) {
        console.error('URL解码失败:', e.message);
    }
    
    const filePath = path.join(DIRECTORY, pathname);
    console.log(`[${new Date().toLocaleString('zh-CN')}] ${req.method} ${pathname}`);
    
    // 检查文件是否存在
    if (!fs.existsSync(filePath)) {
        res.writeHead(404, {'Content-Type': 'text/html; charset=utf-8'});
        res.end('<h1>404 - 文件未找到</h1><p><a href="/">返回首页</a></p>');
        return;
    }
    
    const stat = fs.statSync(filePath);
    
    // 如果是目录,显示文件列表
    if (stat.isDirectory()) {
        const files = fs.readdirSync(filePath).map(f => {
            const fstat = fs.statSync(path.join(filePath, f));
            const icon = fstat.isDirectory() ? '📁' : '📄';
            const size = fstat.isFile() ? `${(fstat.size / 1024).toFixed(1)} KB` : '';
            const encodedName = encodeURIComponent(f);
            const link = path.join(pathname, encodedName);
            return `
                <div style="padding: 15px; margin: 10px 0; background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); display: flex; justify-content: space-between; align-items: center;">
                    <div>
                        <span style="font-size: 24px;">${icon}</span>
                        <a href="${link}" style="color: #667eea; text-decoration: none; font-size: 16px; margin-left: 10px; font-weight: 500;">${f}</a>
                    </div>
                    <span style="color: #999; font-size: 14px;">${size}</span>
                </div>
            `;
        }).join('');
        
        const html = getHtmlTemplate('文件列表', `
            <div style="background: white; padding: 20px; border-radius: 12px; margin-top: 20px;">
                <h2 style="color: #2c3e50; margin-bottom: 20px;">📂 当前目录</h2>
                ${files}
            </div>
        `, pathname);
        
        res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
        res.end(html);
        return;
    }
    
    // 读取文件内容
    const content = fs.readFileSync(filePath, 'utf-8');
    const ext = path.extname(filePath).toLowerCase();
    
    // Markdown文件:渲染为HTML
    if (ext === '.md') {
        const html = getHtmlTemplate(path.basename(filePath), `
            <div style="background: white; padding: 40px; border-radius: 12px; margin-top: 20px;">
                <div class="markdown-body" id="content"></div>
            </div>
        `, pathname, content, path.basename(filePath));
        
        res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
        res.end(html);
    } else {
        // 其他文件:直接返回
        const mimeTypes = {
            '.txt': 'text/plain; charset=utf-8',
            '.html': 'text/html; charset=utf-8',
            '.css': 'text/css; charset=utf-8',
            '.js': 'application/javascript; charset=utf-8',
            '.json': 'application/json; charset=utf-8',
            '.png': 'image/png',
            '.jpg': 'image/jpeg',
            '.jpeg': 'image/jpeg',
            '.gif': 'image/gif',
            '.pdf': 'application/pdf'
        };
        
        const mimeType = mimeTypes[ext] || 'application/octet-stream';
        res.writeHead(200, {
            'Content-Type': mimeType,
            'Content-Length': stat.size
        });
        res.end(content);
    }
});

function getHtmlTemplate(title, bodyContent, currentPath, markdownContent = null, filename = null) {
    return `<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${title}</title>
    
    <!-- 引入 marked.js -->
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
    
    <!-- 引入 highlight.js -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.9.0/styles/github-dark.min.css">
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.9.0/lib/core.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.9.0/lib/languages/javascript.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.9.0/lib/languages/python.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.9.0/lib/languages/bash.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.9.0/lib/languages/json.min.js"></script>
    
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", "Microsoft YaHei", sans-serif;
            line-height: 1.8;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
        }
        
        .header {
            background: white;
            padding: 30px;
            border-radius: 12px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
            margin-bottom: 20px;
        }
        
        .header h1 {
            color: #2c3e50;
            font-size: 28px;
            margin-bottom: 10px;
        }
        
        .breadcrumb {
            color: #7f8c8d;
            font-size: 14px;
            margin-top: 10px;
        }
        
        .breadcrumb a {
            color: #667eea;
            text-decoration: none;
        }
        
        .breadcrumb a:hover {
            text-decoration: underline;
        }
        
        /* Markdown 样式 */
        .markdown-body h1 { 
            color: #2c3e50; 
            border-bottom: 3px solid #667eea; 
            padding-bottom: 15px; 
            margin: 30px 0 20px 0;
            font-size: 32px;
        }
        
        .markdown-body h2 { 
            color: #34495e; 
            border-bottom: 2px solid #95a5a6; 
            padding-bottom: 10px; 
            margin: 25px 0 15px 0;
            font-size: 26px;
        }
        
        .markdown-body h3 { 
            color: #7f8c8d; 
            margin: 20px 0 10px 0;
            font-size: 22px;
        }
        
        .markdown-body h4 {
            color: #95a5a6;
            margin: 15px 0 10px 0;
            font-size: 18px;
        }
        
        .markdown-body p { 
            margin: 15px 0; 
            line-height: 1.8;
        }
        
        .markdown-body table { 
            border-collapse: collapse; 
            width: 100%; 
            margin: 20px 0; 
            font-size: 14px;
        }
        
        .markdown-body th, .markdown-body td { 
            border: 1px solid #e0e0e0; 
            padding: 12px 15px; 
            text-align: left; 
        }
        
        .markdown-body th { 
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white; 
            font-weight: 600;
        }
        
        .markdown-body tr:nth-child(even) { background-color: #f9f9f9; }
        .markdown-body tr:hover { background-color: #f0f0ff; }
        
        .markdown-body code { 
            background: #f4f4f4; 
            padding: 3px 8px; 
            border-radius: 4px; 
            font-size: 0.9em;
            font-family: 'Courier New', Consolas, monospace;
            color: #e74c3c;
        }
        
        .markdown-body pre { 
            background: #2c3e50; 
            color: #ecf0f1; 
            padding: 20px; 
            border-radius: 8px; 
            overflow-x: auto;
            margin: 20px 0;
        }
        
        .markdown-body pre code { 
            background: none; 
            padding: 0; 
            color: #ecf0f1;
            font-size: 14px;
        }
        
        .markdown-body blockquote { 
            border-left: 5px solid #667eea; 
            padding: 15px 20px; 
            margin: 20px 0; 
            color: #7f8c8d;
            background: #f9f9f9;
            border-radius: 0 8px 8px 0;
        }
        
        .markdown-body a { 
            color: #667eea; 
            text-decoration: none; 
            border-bottom: 1px dashed #667eea;
        }
        
        .markdown-body a:hover { 
            color: #764ba2;
            border-bottom-style: solid;
        }
        
        .markdown-body hr {
            border: none;
            height: 2px;
            background: linear-gradient(to right, transparent, #667eea, transparent);
            margin: 30px 0;
        }
        
        .markdown-body ul, .markdown-body ol {
            padding-left: 30px;
            margin: 15px 0;
        }
        
        .markdown-body li {
            margin: 8px 0;
            line-height: 1.6;
        }
        
        .markdown-body img {
            max-width: 100%;
            border-radius: 8px;
            margin: 15px 0;
        }
        
        .footer {
            text-align: center;
            padding: 20px;
            color: white;
            font-size: 14px;
            margin-top: 20px;
        }
        
        @media (max-width: 768px) {
            .header h1 { font-size: 24px; }
            .markdown-body h1 { font-size: 24px; }
            .markdown-body h2 { font-size: 20px; }
            .markdown-body h3 { font-size: 18px; }
            body { padding: 10px; }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>📄 ${title}</h1>
            <div class="breadcrumb">
                <a href="/">🏠 首页</a> / ${currentPath.split('/').filter(p => p).map(p => `<a href="${p}">${p}</a>`).join(' / ')}
            </div>
        </div>
        
        ${bodyContent}
        
        <div class="footer">
            由增强版文件服务器生成 • ${new Date().toLocaleString('zh-CN')} • 支持 Markdown 渲染
        </div>
    </div>
    
    ${markdownContent ? `
    <script>
        // 配置 marked
        marked.setOptions({
            breaks: true,
            gfm: true,
            highlight: function(code, lang) {
                if (lang && hljs.getLanguage(lang)) {
                    return hljs.highlight(code, { language: lang }).value;
                }
                return code;
            }
        });
        
        // 渲染 Markdown
        const content = ${JSON.stringify(markdownContent)};
        document.getElementById('content').innerHTML = marked.parse(content);
    </script>
    ` : ''}
</body>
</html>`;
}

server.listen(PORT, '0.0.0.0', () => {
    console.log('✅ 增强版 Markdown 文件服务器运行中');
    console.log(`📁 服务目录: ${DIRECTORY}`);
    console.log(`🌐 访问地址: http://proxy.mossai.icu:${PORT}`);
    console.log('🎨 功能: Markdown渲染、代码高亮、UTF-8编码、美观界面');
});