AwsLinker/scripts/serve-static.py
2025-09-16 17:19:58 +08:00

172 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
自定义静态文件服务器支持自定义404页面和SPA路由
用于部署Next.js静态导出的文件
"""
import http.server
import socketserver
import os
import sys
import urllib.parse
from pathlib import Path
class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
# 设置静态文件目录
super().__init__(*args, directory="out", **kwargs)
def end_headers(self):
# 添加安全头和缓存控制
self.send_header('X-Content-Type-Options', 'nosniff')
self.send_header('X-Frame-Options', 'DENY')
self.send_header('X-XSS-Protection', '1; mode=block')
# 为HTML文件禁用缓存为静态资源启用缓存
if self.path.endswith('.html'):
self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
self.send_header('Pragma', 'no-cache')
self.send_header('Expires', '0')
elif any(self.path.endswith(ext) for ext in ['.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.ico', '.svg', '.woff', '.woff2']):
self.send_header('Cache-Control', 'public, max-age=31536000')
super().end_headers()
def do_GET(self):
# URL解码
parsed_path = urllib.parse.unquote(self.path)
# 移除查询参数
if '?' in parsed_path:
parsed_path = parsed_path.split('?')[0]
# 移除fragment
if '#' in parsed_path:
parsed_path = parsed_path.split('#')[0]
# 构建文件路径
if parsed_path == '/':
# 根路径重定向到index.html
file_path = Path("out/index.html")
else:
# 移除开头的斜杠
clean_path = parsed_path.lstrip('/')
file_path = Path("out") / clean_path
# 如果是目录尝试查找index.html
if file_path.is_dir():
file_path = file_path / "index.html"
# 如果没有扩展名,尝试添加.html
elif not file_path.suffix and not file_path.exists():
file_path = file_path.with_suffix('.html')
print(f"请求路径: {self.path}")
print(f"解析路径: {parsed_path}")
print(f"文件路径: {file_path}")
print(f"文件存在: {file_path.exists()}")
# 检查文件是否存在
if file_path.exists() and file_path.is_file():
# 文件存在,正常处理
self.path = '/' + str(file_path.relative_to("out"))
super().do_GET()
else:
# 文件不存在返回404页面
self.send_custom_404()
def send_custom_404(self):
"""发送自定义404页面"""
try:
# 检查自定义404页面是否存在
error_page_path = Path("out/404.html")
if not error_page_path.exists():
# 如果404.html不存在使用public/404.html
error_page_path = Path("public/404.html")
if error_page_path.exists():
with open(error_page_path, 'rb') as f:
content = f.read()
self.send_response(404)
self.send_header('Content-type', 'text/html; charset=utf-8')
self.send_header('Content-Length', str(len(content)))
self.end_headers()
self.wfile.write(content)
print(f"返回自定义404页面: {error_page_path}")
else:
# 如果自定义404页面也不存在返回简单的404
self.send_simple_404()
except Exception as e:
print(f"发送自定义404页面时出错: {e}")
self.send_simple_404()
def send_simple_404(self):
"""发送简单的404响应"""
message = """
<!DOCTYPE html>
<html>
<head>
<title>404 - 页面未找到</title>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
h1 { color: #333; }
p { color: #666; }
a { color: #0066cc; text-decoration: none; }
</style>
</head>
<body>
<h1>404 - 页面未找到</h1>
<p>抱歉,您访问的页面不存在。</p>
<p><a href="/">返回首页</a></p>
</body>
</html>
"""
content = message.encode('utf-8')
self.send_response(404)
self.send_header('Content-type', 'text/html; charset=utf-8')
self.send_header('Content-Length', str(len(content)))
self.end_headers()
self.wfile.write(content)
def log_message(self, format, *args):
"""自定义日志格式"""
print(f"[{self.log_date_time_string()}] {format % args}")
def main():
# 检查out目录是否存在
if not os.path.exists("out"):
print("错误: 'out' 目录不存在。请先运行构建命令生成静态文件。")
sys.exit(1)
# 设置端口
PORT = 8080
if len(sys.argv) > 1:
try:
PORT = int(sys.argv[1])
except ValueError:
print("警告: 无效的端口号,使用默认端口 8080")
# 启动服务器
try:
with socketserver.TCPServer(("", PORT), CustomHTTPRequestHandler) as httpd:
print(f"静态文件服务器已启动")
print(f"访问地址: http://localhost:{PORT}")
print(f"静态文件目录: {os.path.abspath('out')}")
print("按 Ctrl+C 停止服务器")
print("-" * 50)
httpd.serve_forever()
except KeyboardInterrupt:
print("\n服务器已停止")
except OSError as e:
if e.errno == 48: # Address already in use
print(f"端口 {PORT} 已被占用,请尝试其他端口")
print(f"使用方法: python {sys.argv[0]} [端口号]")
else:
print(f"启动服务器时出错: {e}")
sys.exit(1)
if __name__ == "__main__":
main()