Files

90 lines
2.0 KiB
Go

package web
import (
"fmt"
"html/template"
"net"
"os"
"strings"
"speedtest/config"
"speedtest/internal/store"
"speedtest/internal/web/handlers"
"github.com/gin-gonic/gin"
)
// WebServer wraps the Gin engine and its dependencies.
type WebServer struct {
engine *gin.Engine
stores *store.Stores
cfg config.Config
}
// NewWebServer creates a new WebServer, initializes the Gin engine and registers routes.
func NewWebServer(cfg config.Config, stores *store.Stores) (*WebServer, error) {
gin.SetMode(gin.ReleaseMode)
engine := gin.New()
engine.Use(gin.Logger())
engine.Use(gin.Recovery())
// 不信任默认代理(真实 IP 通过 X-Real-IP 头由 Caddy 注入,自行解析)
_ = engine.SetTrustedProxies(nil)
// 加载 HTML 模板
tmpl := template.Must(template.New("").ParseGlob("internal/web/templates/*.html"))
engine.SetHTMLTemplate(tmpl)
handler, err := handlers.NewHandler(stores, cfg.Speedtest)
if err != nil {
return nil, err
}
ws := &WebServer{
engine: engine,
stores: stores,
cfg: cfg,
}
// 页面
engine.GET("/", func(c *gin.Context) {
c.HTML(200, "index.html", gin.H{})
})
// 测速 API
api := engine.Group("/api")
{
api.GET("/ping", handler.Ping)
api.GET("/download", handler.Download)
api.POST("/upload", handler.Upload)
api.POST("/result", handler.Result)
api.GET("/rankings", handler.Rankings)
}
return ws, nil
}
// Start launches the HTTP server on the configured address.
// Supports both TCP (e.g. ":8080") and Unix socket (e.g. "/opt/speedtest/web.sock").
func (ws *WebServer) Start() error {
addr := ws.cfg.Web.Addr
// Unix socket:地址以 / 开头
if strings.HasPrefix(addr, "/") {
// 清理旧的 socket 文件
os.Remove(addr)
listener, err := net.Listen("unix", addr)
if err != nil {
return fmt.Errorf("监听 Unix socket 失败 %s: %w", addr, err)
}
// 允许 caddy 等外部进程连接
os.Chmod(addr, 0666)
return ws.engine.RunListener(listener)
}
// TCP 端口
return ws.engine.Run(addr)
}