serve.py 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
  1. #!/usr/bin/env python3
  2. """极简静态服务器(带 SPA 路由回退)—— 用户空间,CentOS 自带 Python 3.6 即可。
  3. 托管前端构建产物(deploy 分支的 dist),供内网访问。客户端路由(如
  4. /behavior/funnel/group)刷新时回退到 index.html,避免 404。
  5. 兼容 Python 3.6(不用 3.7+ 的 `directory=` 参数,改 os.chdir)。
  6. 用法:
  7. python3 serve.py <静态目录> [端口]
  8. 例:
  9. nohup python3 ~/serve-hs.py ~/hs-data 6666 > ~/hs-web.log 2>&1 &
  10. """
  11. import os
  12. import sys
  13. from http.server import SimpleHTTPRequestHandler
  14. from socketserver import ThreadingTCPServer
  15. ROOT = os.path.abspath(sys.argv[1] if len(sys.argv) > 1 else ".")
  16. PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 6666
  17. os.chdir(ROOT)
  18. class SpaHandler(SimpleHTTPRequestHandler):
  19. # 覆盖 send_head(GET 与 HEAD 都走它),统一做 SPA 回退。
  20. def send_head(self):
  21. rel = self.path.split("?", 1)[0].split("#", 1)[0].lstrip("/")
  22. if rel and not os.path.isfile(os.path.join(ROOT, rel)):
  23. last = rel.rsplit("/", 1)[-1]
  24. # 仅"看起来像路由"的路径(末段无扩展名)回退到 index.html;
  25. # 缺失的资源文件(如 /assets/x.js)让其正常 404,避免返回 HTML 触发 MIME 错误。
  26. if "." not in last:
  27. self.path = "/index.html"
  28. return SimpleHTTPRequestHandler.send_head(self)
  29. def end_headers(self):
  30. # index.html 不缓存(构建产物自带 hash,可长缓存)。
  31. if self.path.split("?", 1)[0] in ("/", "/index.html"):
  32. self.send_header("Cache-Control", "no-cache")
  33. SimpleHTTPRequestHandler.end_headers(self)
  34. class Server(ThreadingTCPServer):
  35. allow_reuse_address = True
  36. daemon_threads = True
  37. if __name__ == "__main__":
  38. print("serving %s on 0.0.0.0:%d (SPA fallback)" % (ROOT, PORT))
  39. Server(("0.0.0.0", PORT), SpaHandler).serve_forever()