mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
feat(migrate): add import CLI command and importer for migration data
Introduce a new `import` subcommand to the server binary that reads exported JSON files and imports masters, providers, keys, bindings, and namespaces into the database. Key features: - Support for dry-run mode to validate without writing - Conflict policies: skip existing or overwrite - Optional binding import via --include-bindings flag - Auto-generation of master keys with secure hashing - Namespace auto-creation for referenced namespaces - Detailed import summary with warnings and created credentials
This commit is contained in:
@@ -2,7 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"expvar"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -16,6 +19,7 @@ import (
|
||||
"github.com/ez-api/ez-api/internal/config"
|
||||
"github.com/ez-api/ez-api/internal/cron"
|
||||
"github.com/ez-api/ez-api/internal/middleware"
|
||||
"github.com/ez-api/ez-api/internal/migrate"
|
||||
"github.com/ez-api/ez-api/internal/model"
|
||||
"github.com/ez-api/ez-api/internal/service"
|
||||
"github.com/ez-api/foundation/logging"
|
||||
@@ -72,6 +76,10 @@ func isOriginAllowed(allowed []string, origin string) bool {
|
||||
|
||||
func main() {
|
||||
logger, _ := logging.New(logging.Options{Service: "ez-api"})
|
||||
if len(os.Args) > 1 && os.Args[1] == "import" {
|
||||
code := runImport(logger, os.Args[2:])
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// 1. Load Configuration
|
||||
cfg, err := config.Load()
|
||||
@@ -357,3 +365,71 @@ func main() {
|
||||
|
||||
logger.Info("server exited properly")
|
||||
}
|
||||
|
||||
func runImport(logger *slog.Logger, args []string) int {
|
||||
fs := flag.NewFlagSet("import", flag.ContinueOnError)
|
||||
fs.SetOutput(os.Stderr)
|
||||
|
||||
var filePath string
|
||||
var dryRun bool
|
||||
var conflictPolicy string
|
||||
var includeBindings bool
|
||||
|
||||
fs.StringVar(&filePath, "file", "", "Path to export JSON")
|
||||
fs.BoolVar(&dryRun, "dry-run", false, "Validate only, do not write to database")
|
||||
fs.StringVar(&conflictPolicy, "conflict", migrate.ConflictSkip, "Conflict policy: skip or overwrite")
|
||||
fs.BoolVar(&includeBindings, "include-bindings", false, "Import bindings from payload")
|
||||
|
||||
if err := fs.Parse(args); err != nil {
|
||||
logger.Error("failed to parse flags", "err", err)
|
||||
return 2
|
||||
}
|
||||
if strings.TrimSpace(filePath) == "" {
|
||||
fmt.Fprintln(os.Stderr, "missing --file")
|
||||
return 2
|
||||
}
|
||||
conflictPolicy = strings.ToLower(strings.TrimSpace(conflictPolicy))
|
||||
if conflictPolicy != migrate.ConflictSkip && conflictPolicy != migrate.ConflictOverwrite {
|
||||
fmt.Fprintf(os.Stderr, "invalid --conflict value: %s\n", conflictPolicy)
|
||||
return 2
|
||||
}
|
||||
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
logger.Error("failed to load config", "err", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
db, err := gorm.Open(postgres.Open(cfg.Postgres.DSN), &gorm.Config{})
|
||||
if err != nil {
|
||||
logger.Error("failed to connect to postgresql", "err", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
if err := db.AutoMigrate(&model.Master{}, &model.Key{}, &model.Provider{}, &model.Model{}, &model.Binding{}, &model.Namespace{}); err != nil {
|
||||
logger.Error("failed to auto migrate", "err", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
importer := migrate.NewImporter(db, migrate.ImportOptions{
|
||||
DryRun: dryRun,
|
||||
ConflictPolicy: conflictPolicy,
|
||||
IncludeBindings: includeBindings,
|
||||
})
|
||||
summary, err := importer.ImportFile(filePath)
|
||||
if err != nil {
|
||||
logger.Error("import failed", "err", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
payload, err := json.MarshalIndent(summary, "", " ")
|
||||
if err != nil {
|
||||
logger.Error("failed to render import summary", "err", err)
|
||||
return 1
|
||||
}
|
||||
fmt.Fprintln(os.Stdout, string(payload))
|
||||
if dryRun {
|
||||
fmt.Fprintln(os.Stdout, "dry-run only: no data written")
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user