diff --git a/cmd/migrate.go b/cmd/migrate.go deleted file mode 100644 index dad0cad..0000000 --- a/cmd/migrate.go +++ /dev/null @@ -1,69 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" - - "github.com/peterqiu0516/sub-store/internal/config" - "github.com/peterqiu0516/sub-store/internal/database" -) - -//go:generate echo "embed handled in migrate.go" - -var migrateCmd = &cobra.Command{ - Use: "migrate", - Short: "Run database migrations", - RunE: func(cmd *cobra.Command, args []string) error { - cfg, err := config.Load(configFile) - if err != nil { - return err - } - db, err := database.InitDB(cfg.Database.Path) - if err != nil { - return err - } - defer db.Close() - return database.RunMigrations(db) - }, -} - -var configFile string - -func init() { - rootCmd.AddCommand(migrateCmd) - migrateCmd.Flags().StringVarP(&configFile, "config", "c", "", "config file path") - - serveCmd.Flags().StringVarP(&configFile, "config", "c", "", "config file path") - rootCmd.AddCommand(serveCmd) -} - -var serveCmd = &cobra.Command{ - Use: "serve", - Short: "Start the Sub-Store HTTP server", - RunE: func(cmd *cobra.Command, args []string) error { - cfg, err := config.Load(configFile) - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - return RunServer(cfg) - }, -} - -func RunServer(cfg *config.Config) error { - db, err := database.InitDB(cfg.Database.Path) - if err != nil { - return err - } - defer db.Close() - - if err := database.RunMigrations(db); err != nil { - return err - } - - return startFiber(cfg, db) -} - -// startFiber is implemented in server.go (same package). diff --git a/cmd/server.go b/cmd/server.go index 0f4ae12..da00c31 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -11,6 +11,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/sirupsen/logrus" "github.com/jmoiron/sqlx" + "github.com/spf13/cobra" "github.com/peterqiu0516/sub-store/internal/config" "github.com/peterqiu0516/sub-store/internal/database" @@ -18,6 +19,43 @@ import ( "github.com/peterqiu0516/sub-store/internal/middleware" ) +var configFile string + +var serveCmd = &cobra.Command{ + Use: "serve", + Short: "Start the Sub-Store HTTP server", + RunE: func(cmd *cobra.Command, args []string) error { + cfg, err := config.Load(configFile) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + return RunServer(cfg) + }, +} + +func init() { + rootCmd.AddCommand(serveCmd) + serveCmd.Flags().StringVarP(&configFile, "config", "c", "", "config file path") +} + +// RunServer opens the database, applies migrations (idempotent — goose tracks +// applied versions in goose_db_version), then starts the HTTP server. +func RunServer(cfg *config.Config) error { + db, err := database.InitDB(cfg.Database.Path) + if err != nil { + return err + } + defer db.Close() + + // Auto-migrate on every startup; goose is idempotent so this is safe to call repeatedly. + if err := database.RunMigrations(db); err != nil { + return err + } + + return startFiber(cfg, db) +} + func startFiber(cfg *config.Config, db *sqlx.DB) error { logrus.SetFormatter(&logrus.TextFormatter{ FullTimestamp: true, diff --git a/docs/backend-dev-plan.md b/docs/backend-dev-plan.md index d9e2d64..96a1631 100644 --- a/docs/backend-dev-plan.md +++ b/docs/backend-dev-plan.md @@ -62,8 +62,7 @@ sub-store/ ├── main.go # 入口,调用 cmd ├── cmd/ │ ├── root.go # cobra root command -│ ├── serve.go # sub-store serve (启动 HTTP 服务) -│ ├── migrate.go # sub-store migrate (执行数据库迁移) +│ ├── serve.go # sub-store serve (启动 HTTP 服务,启动时自动 migrate) │ └── version.go # sub-store version ├── internal/ │ ├── config/ @@ -322,11 +321,11 @@ ON source_cache(cached_at + ttl); | 任务 | 产出 | 依赖库 | |------|------|--------| | go mod init + 依赖引入 | `go.mod` | 全部 | -| cobra root/serve/migrate/version 命令 | `cmd/*.go` | cobra | +| cobra root/serve/version 命令 | `cmd/*.go` | cobra | | viper 配置加载 | `internal/config/config.go` | viper | | logrus 日志初始化 | 日志格式化、级别、输出 | logrus | | SQLite 连接初始化 | `internal/database/db.go` | modernc/sqlite, sqlx | -| goose 迁移执行 | `cmd/migrate.go` | goose | +| goose 迁移执行(启动时自动) | `internal/database/migrations.go` | goose | | 3 个 migration SQL 文件 | `internal/database/migrations/` | | | Fiber app 骨架 | 路由注册、健康检查 | fiber/v3 | | 优雅关闭 | signal handling | | @@ -830,11 +829,8 @@ output: YAML with proxies: [{name:"test", type:"ss", ...}] # 构建 go build -o sub-store ./main.go -# 运行 +# 运行(启动时自动执行迁移,幂等) ./sub-store serve --config config.yaml - -# 迁移 -./sub-store migrate --config config.yaml ``` ### 9.2 配置 diff --git a/docs/review-resolutions.md b/docs/review-resolutions.md index 9f47c2a..c60d4f8 100644 --- a/docs/review-resolutions.md +++ b/docs/review-resolutions.md @@ -42,7 +42,7 @@ | 30 | importStorage 导入顺序 | settings→sources→templates→collections | `handler/storage.go` | | 31 | exportStorage 排除内置模板 | 过滤 BuiltinTemplateIDs | `handler/storage.go` | | 32 | source_cache TTL 清理 | 后台 goroutine 定时清理 | `database/cache_repo.go` | -| 33 | goose 迁移嵌入 | //go:embed + SetBaseFS | `cmd/migrate.go` | +| 33 | goose 迁移嵌入 | //go:embed + SetBaseFS | `database/migrations.go` | | 34 | goroutine 关闭安全 | sync.WaitGroup + context | `service/subscription.go` | | 35 | 缓存操作错误吞咽 | recover + 日志,不传播 | `database/cache_repo.go` | | 36 | 并发 wait 延迟 | 保留 waitMs 参数 | `service/concurrency.go` | @@ -491,7 +491,7 @@ func RunMigrations(db *sqlx.DB) error { } ``` -**落地**:`cmd/migrate.go` + `database/migrations/*.sql`。 +**落地**:`database/migrations.go` + `database/migrations/*.sql`。启动时自动执行,幂等(goose 跟踪 `goose_db_version`)。 ---