package main import ( "context" "errors" "io/fs" "log" "net/http" "os" "os/signal" "syscall" "time" "trade/web/internal/auth" "trade/web/internal/config" "trade/web/internal/handlers" "trade/web/internal/router" "trade/web/internal/store" ) func main() { cfg, err := config.Load() if err != nil { log.Fatalf("config: %v", err) } futures, err := store.OpenFutures(cfg.DatabaseURL) if err != nil { log.Fatalf("open futures: %v", err) } defer futures.Close() authDB, err := store.OpenAuth(cfg.AuthDBPath) if err != nil { log.Fatalf("open auth: %v", err) } defer authDB.Close() if err := auth.Bootstrap(authDB); err != nil { log.Fatalf("bootstrap: %v", err) } deps := &handlers.Deps{Auth: authDB, Futures: futures, TushareURL: cfg.TushareAPIURL} dist, err := fs.Sub(distFS, "dist") if err != nil { log.Fatalf("embed dist: %v", err) } srv := &http.Server{ Addr: cfg.ListenAddr, Handler: router.New(deps, dist), ReadHeaderTimeout: 10 * time.Second, } idle := make(chan struct{}) go func() { stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) <-stop log.Println("shutting down ...") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _ = srv.Shutdown(ctx) close(idle) }() log.Printf("web 监听 %s", cfg.ListenAddr) if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Fatalf("listen: %v", err) } <-idle }