package bootstrap import ( "context" "embed" "fmt" "log" "os" "BingPaper/internal/config" "BingPaper/internal/cron" "BingPaper/internal/http" "BingPaper/internal/repo" "BingPaper/internal/service/fetcher" "BingPaper/internal/storage" "BingPaper/internal/storage/local" "BingPaper/internal/storage/s3" "BingPaper/internal/storage/webdav" "BingPaper/internal/util" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // Init 初始化应用各项服务 func Init(webFS embed.FS, configPath string) *gin.Engine { // 0. 确保数据目录存在 _ = os.MkdirAll("data/picture", 0755) // 1. 初始化配置 if err := config.Init(configPath); err != nil { log.Fatalf("Failed to initialize config: %v", err) } cfg := config.GetConfig() // 2. 初始化日志 util.InitLogger(cfg.Log) // 输出配置信息 util.Logger.Info("Application configuration loaded") util.Logger.Info("├─ Config file", zap.String("path", config.GetRawViper().ConfigFileUsed())) util.Logger.Info("├─ Database ", zap.String("type", cfg.DB.Type)) util.Logger.Info("├─ Storage ", zap.String("type", cfg.Storage.Type)) util.Logger.Info("└─ Server ", zap.Int("port", cfg.Server.Port)) // 根据存储类型输出更多信息 switch cfg.Storage.Type { case "s3": util.Logger.Info("S3 storage detail", zap.String("endpoint", cfg.Storage.S3.Endpoint), zap.String("bucket", cfg.Storage.S3.Bucket), ) case "webdav": util.Logger.Info("WebDAV storage detail", zap.String("url", cfg.Storage.WebDAV.URL), ) default: util.Logger.Info("Local storage detail", zap.String("root", cfg.Storage.Local.Root), ) } // 3. 初始化数据库 if err := repo.InitDB(); err != nil { util.Logger.Fatal("Failed to initialize database") } // 注册数据库配置变更回调,支持热迁移 config.OnDBConfigChange = func(newCfg *config.Config) { util.Logger.Info("Database configuration change detected, initiating migration...") if err := repo.MigrateDataToNewDB(repo.DB, newCfg); err != nil { util.Logger.Error("Automatic data migration failed", zap.Error(err)) } else { util.Logger.Info("Automatic data migration finished") } } // 4. 初始化存储 var s storage.Storage var err error switch cfg.Storage.Type { case "s3": s, err = s3.NewS3Storage( cfg.Storage.S3.Endpoint, cfg.Storage.S3.Region, cfg.Storage.S3.Bucket, cfg.Storage.S3.AccessKey, cfg.Storage.S3.SecretKey, cfg.Storage.S3.PublicURLPrefix, cfg.Storage.S3.ForcePathStyle, ) case "webdav": s, err = webdav.NewWebDAVStorage( cfg.Storage.WebDAV.URL, cfg.Storage.WebDAV.Username, cfg.Storage.WebDAV.Password, cfg.Storage.WebDAV.PublicURLPrefix, ) default: // local s, err = local.NewLocalStorage(cfg.Storage.Local.Root) } if err != nil { util.Logger.Fatal("Failed to initialize storage", zap.Error(err)) } storage.GlobalStorage = s // 5. 初始化定时任务 cron.InitCron() // 6. 启动时执行一次抓取 (可选,这里我们默认执行一次以确保有数据) go func() { f := fetcher.NewFetcher() f.Fetch(context.Background(), config.BingFetchN) }() // 7. 设置路由 return http.SetupRouter(webFS) } // LogWelcomeInfo 输出欢迎信息和快速跳转地址 func LogWelcomeInfo() { cfg := config.GetConfig() port := cfg.Server.Port baseURL := cfg.Server.BaseURL if baseURL == "" { baseURL = fmt.Sprintf("http://localhost:%d", port) } fmt.Println("\n---------------------------------------------------------") fmt.Println(" BingPaper 服务已启动!") fmt.Printf(" - 首页地址: %s/\n", baseURL) fmt.Printf(" - 管理后台: %s/admin\n", baseURL) fmt.Printf(" - API 文档: %s/swagger/index.html\n", baseURL) fmt.Printf(" - 今日图片: %s/api/v1/image/today\n", baseURL) fmt.Println("---------------------------------------------------------") }