Files
fscan/web/server.go
ZacharyZcR 8312808769 refactor(logging): 统一日志前缀,删除废弃的 LogBase
- 删除 LogBase 函数,所有调用迁移到 LogInfo/LogError
- 新增 PrefixDebug ([.]) 前缀,所有日志级别现在都有前缀
- 修复日志输出缩进不一致的问题
- 删除未使用的 PrefixDefault 常量
2026-01-21 18:29:47 +08:00

126 lines
2.8 KiB
Go
Raw Permalink 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.
//go:build web
package web
import (
"context"
"embed"
"fmt"
"io/fs"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/web/api"
"github.com/shadow1ng/fscan/web/ws"
)
//go:embed dist/*
var distFS embed.FS
// StartServer 启动Web服务器
func StartServer(port int) error {
// 初始化WebSocket Hub
hub := ws.NewHub()
go hub.Run()
// 创建路由
mux := http.NewServeMux()
// API路由
api.RegisterRoutes(mux, hub)
// WebSocket路由
mux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
ws.ServeWs(hub, w, r)
})
// 静态文件服务
distContent, err := fs.Sub(distFS, "dist")
if err != nil {
return fmt.Errorf("failed to get dist fs: %w", err)
}
fileServer := http.FileServer(http.FS(distContent))
// SPA fallback: 对于非API/WS请求尝试静态文件否则返回index.html
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// 检查文件是否存在
path := r.URL.Path
if path == "/" {
path = "/index.html"
}
// 尝试打开文件
f, err := distContent.Open(path[1:]) // 移除开头的/
if err != nil {
// 文件不存在返回index.htmlSPA路由
r.URL.Path = "/"
fileServer.ServeHTTP(w, r)
return
}
f.Close()
// 文件存在,正常服务
fileServer.ServeHTTP(w, r)
})
// 创建服务器
addr := fmt.Sprintf(":%d", port)
server := &http.Server{
Addr: addr,
Handler: corsMiddleware(mux),
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second,
}
// 优雅关闭
done := make(chan bool)
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-quit
common.LogInfo(i18n.GetText("web_shutting_down"))
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
common.LogError(fmt.Sprintf("Server shutdown error: %v", err))
}
close(done)
}()
// 启动服务器
common.LogSuccess(i18n.Tr("web_server_started", port))
fmt.Printf(" http://localhost:%d\n", port)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return fmt.Errorf("server error: %w", err)
}
<-done
return nil
}
// corsMiddleware 添加CORS头开发时需要
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}