chore: sub-store Go 重写项目初始化
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
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).
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "sub-store",
|
||||
Short: "Sub-Store — subscription aggregation and proxy management",
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
"github.com/peterqiu0516/sub-store/internal/config"
|
||||
"github.com/peterqiu0516/sub-store/internal/database"
|
||||
"github.com/peterqiu0516/sub-store/internal/handler"
|
||||
"github.com/peterqiu0516/sub-store/internal/middleware"
|
||||
)
|
||||
|
||||
func startFiber(cfg *config.Config, db *sqlx.DB) error {
|
||||
logrus.SetFormatter(&logrus.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
})
|
||||
logrus.SetLevel(logrus.InfoLevel)
|
||||
|
||||
// Start cache cleaner — review-resolution #32
|
||||
cacheCtx, cacheCancel := context.WithCancel(context.Background())
|
||||
defer cacheCancel()
|
||||
cacheInterval := cfg.Fetcher.CacheTTL
|
||||
if cacheInterval <= 0 {
|
||||
cacheInterval = 300 * time.Second
|
||||
}
|
||||
database.StartCacheCleaner(cacheCtx, db, cacheInterval)
|
||||
|
||||
app := fiber.New(fiber.Config{
|
||||
ReadTimeout: cfg.Server.ReadTimeout,
|
||||
WriteTimeout: cfg.Server.WriteTimeout,
|
||||
BodyLimit: cfg.Server.BodyLimit,
|
||||
ErrorHandler: func(c fiber.Ctx, err error) error {
|
||||
logrus.WithError(err).WithFields(logrus.Fields{
|
||||
"method": c.Method(),
|
||||
"path": c.Path(),
|
||||
}).Error("request error")
|
||||
return c.Status(500).JSON(fiber.Map{
|
||||
"status": "failed",
|
||||
"error": fiber.Map{"code": 500, "message": "Internal server error"},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// Global middleware
|
||||
app.Use(middleware.SecurityHeaders())
|
||||
app.Use(middleware.HandleOptions())
|
||||
app.Use(middleware.CORS(""))
|
||||
app.Use(middleware.DownloadHostIsolation(cfg.Auth.DownloadHosts))
|
||||
|
||||
// Register routes
|
||||
handler.RegisterRoutes(app, cfg, db)
|
||||
|
||||
// Health check
|
||||
app.Get("/health", func(c fiber.Ctx) error {
|
||||
return c.JSON(fiber.Map{"status": "ok"})
|
||||
})
|
||||
|
||||
// Start server
|
||||
addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port)
|
||||
logrus.Infof("Starting %s on %s", cfg.App.Name, addr)
|
||||
|
||||
go func() {
|
||||
if err := app.Listen(addr); err != nil {
|
||||
logrus.WithError(err).Error("server failed")
|
||||
}
|
||||
}()
|
||||
|
||||
// Graceful shutdown — review-resolution #34
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
|
||||
logrus.Info("Shutting down server...")
|
||||
cacheCancel()
|
||||
|
||||
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer shutdownCancel()
|
||||
go func() {
|
||||
<-shutdownCtx.Done()
|
||||
}()
|
||||
|
||||
if err := app.Shutdown(); err != nil {
|
||||
logrus.WithError(err).Error("shutdown error")
|
||||
}
|
||||
logrus.Info("Server stopped")
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the Sub-Store version",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("sub-store v1.0.0")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
Reference in New Issue
Block a user