web_ui.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package main
  2. import (
  3. "compress/gzip"
  4. "context"
  5. "embed"
  6. "io/fs"
  7. "net/http"
  8. "strings"
  9. "time"
  10. )
  11. //go:embed web/*
  12. var webRootFS embed.FS
  13. type WebUI struct {
  14. fs fs.FS
  15. mux *http.ServeMux
  16. server *http.Server
  17. }
  18. type gzipResponseWriter struct {
  19. http.ResponseWriter
  20. writer *gzip.Writer
  21. }
  22. func (w gzipResponseWriter) Write(b []byte) (int, error) {
  23. return w.writer.Write(b)
  24. }
  25. func gzipHandler(h http.Handler) http.Handler {
  26. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  27. if r.Method == http.MethodHead ||
  28. !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
  29. h.ServeHTTP(w, r)
  30. return
  31. }
  32. path := strings.ToLower(r.URL.Path)
  33. if !(strings.HasSuffix(path, ".html") || strings.HasSuffix(path, ".js") ||
  34. strings.HasSuffix(path, ".css")) {
  35. h.ServeHTTP(w, r)
  36. return
  37. }
  38. gz, err := gzip.NewWriterLevel(w, gzip.DefaultCompression)
  39. if err != nil {
  40. h.ServeHTTP(w, r)
  41. return
  42. }
  43. defer gz.Close()
  44. w.Header().Set("Content-Encoding", "gzip")
  45. w.Header().Set("Vary", "Accept-Encoding")
  46. w.Header().Del("Content-Length")
  47. h.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, writer: gz}, r)
  48. })
  49. }
  50. func staticHandler(h http.Handler) http.Handler {
  51. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  52. path := r.URL.Path
  53. if strings.HasPrefix(path, "/static/") {
  54. w.Header().Set("Cache-Control", "public, max-age=86400")
  55. }
  56. h.ServeHTTP(w, r)
  57. })
  58. }
  59. func NewWebUI(addr string) (*WebUI, error) {
  60. sub, err := fs.Sub(webRootFS, "web")
  61. if err != nil {
  62. return nil, err
  63. }
  64. mux := http.NewServeMux()
  65. static := http.FileServer(http.FS(sub))
  66. mux.Handle(
  67. "/static/",
  68. staticHandler(static),
  69. )
  70. registerRoutes(mux)
  71. return &WebUI{
  72. fs: sub,
  73. mux: mux,
  74. server: &http.Server{
  75. Addr: addr,
  76. Handler: gzipHandler(mux),
  77. },
  78. }, nil
  79. }
  80. func (ui *WebUI) Start() error {
  81. startSessionCleaner() // session过期自动回收
  82. ui.mux.HandleFunc("/", ui.rootHandler)
  83. return ui.server.ListenAndServe()
  84. }
  85. func (ui *WebUI) Stop() error {
  86. if ui.server == nil {
  87. return nil
  88. }
  89. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  90. defer cancel()
  91. err := ui.server.Shutdown(ctx)
  92. if err != nil {
  93. _ = ui.server.Close()
  94. }
  95. return err
  96. }