100 lines
2.6 KiB
Go
100 lines
2.6 KiB
Go
package main
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"log"
|
||
"net/http"
|
||
"os"
|
||
"os/signal"
|
||
"path/filepath"
|
||
"syscall"
|
||
|
||
"scout/internal/config"
|
||
"scout/internal/httpapi"
|
||
"scout/internal/notify"
|
||
"scout/internal/registry"
|
||
"scout/internal/users"
|
||
)
|
||
|
||
func main() {
|
||
logger := log.Default()
|
||
cfg, err := config.Load()
|
||
if err != nil {
|
||
logger.Fatal(err)
|
||
}
|
||
|
||
store, err := registry.New(cfg.DataDir)
|
||
if err != nil {
|
||
logger.Fatal(err)
|
||
}
|
||
projectStore, err := registry.NewProjectRegistry(cfg.DataDir)
|
||
if err != nil {
|
||
logger.Fatal(err)
|
||
}
|
||
userStore, err := users.New(filepath.Join(cfg.DataDir, "users.json"))
|
||
if err != nil {
|
||
logger.Fatal(err)
|
||
}
|
||
if cfg.AdminEmail != "" {
|
||
if _, err := userStore.EnsureAdmin(cfg.AdminEmail, cfg.AdminName, cfg.AdminPassword); err != nil {
|
||
logger.Fatal(err)
|
||
}
|
||
}
|
||
|
||
api := &httpapi.Server{
|
||
Registry: store,
|
||
Projects: projectStore,
|
||
DeveloperToken: cfg.DeveloperToken,
|
||
AllowLegacyToken: cfg.AllowLegacyToken,
|
||
MaxUploadBytes: cfg.MaxUploadBytes,
|
||
Users: userStore,
|
||
Mailer: notify.NewFromEnv(logger),
|
||
SessionDuration: cfg.SessionDuration,
|
||
AllowedOrigins: cfg.AllowedOrigins,
|
||
AllowedHosts: cfg.AllowedHosts,
|
||
ForceHTTPS: cfg.ForceHTTPS,
|
||
TrustProxy: cfg.TrustProxy,
|
||
RateLimitPerMinute: cfg.RateLimitPerMinute,
|
||
Logger: logger,
|
||
}
|
||
server := &http.Server{
|
||
Addr: cfg.Address,
|
||
Handler: api.Handler(),
|
||
ReadHeaderTimeout: cfg.ReadHeaderTimeout,
|
||
ReadTimeout: cfg.ReadTimeout,
|
||
WriteTimeout: cfg.WriteTimeout,
|
||
IdleTimeout: cfg.IdleTimeout,
|
||
}
|
||
|
||
logger.Printf("Scout 包服务端正在监听 %s,数据目录为 %s,运行环境为 %s", cfg.Address, cfg.DataDir, cfg.Environment)
|
||
if cfg.DeveloperToken == "" || !cfg.AllowLegacyToken {
|
||
logger.Printf("兼容开发者令牌未启用,上传需要开发者账户或管理员账户")
|
||
}
|
||
if os.Getenv("SCOUT_SMTP_HOST") == "" {
|
||
logger.Printf("未配置 SMTP,邮箱通知将只记录日志")
|
||
}
|
||
|
||
serverErrors := make(chan error, 1)
|
||
go func() {
|
||
serverErrors <- server.ListenAndServe()
|
||
}()
|
||
|
||
shutdownSignal := make(chan os.Signal, 1)
|
||
signal.Notify(shutdownSignal, os.Interrupt, syscall.SIGTERM)
|
||
select {
|
||
case err := <-serverErrors:
|
||
if !errors.Is(err, http.ErrServerClosed) {
|
||
logger.Fatal(err)
|
||
}
|
||
case signal := <-shutdownSignal:
|
||
logger.Printf("收到 %s,开始优雅停止服务", signal)
|
||
ctx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout)
|
||
defer cancel()
|
||
if err := server.Shutdown(ctx); err != nil {
|
||
logger.Printf("优雅停止失败,强制关闭服务: %v", err)
|
||
_ = server.Close()
|
||
}
|
||
}
|
||
}
|