mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 05:56:38 +08:00
530a9dd3ee
BREAKING: pkg/migrator/ deleted entirely. Migration SQL files are now owned by each plugin in its own migrations/ directory. Architecture: - Delete pkg/migrator/ (26 global SQL files + ClickHouse migration) - Move global SQL to per-plugin migrations/ with go:embed + Register() - Rewrite cmd/app.go gooseEngine: uses Inject[DBService] for DB, iterates all plugin-registered MigrationEntry, runs goose.Up per entry - core.MigrationEngine.Migrate signature changed: *Context instead of context.Context, so engine can resolve services via IoC Per-plugin migration ownership: auth/ → w_access_tokens, w_auth_sources, w_external_accounts user/ → w_users (seed system user) admin/ → w_system_configs, w_templates (seeds) upload/ → w_uploads, w_upload_stats message_gateway/ → w_push_*, w_message_* risk_control/ → w_user_access_logs (PG + ClickHouse) driver_asynq_cron/ → w_schedules driver_asynq_worker/ → w_task_executions Dependencies: - cmd/banner.go: removed migration report display (migrations are automatic) - cmd/reset_passwd.go: removed PreRun migrator.Migrate() call - go.mod: clickhouse-go kept (used by plugins/infra/database/clickhouse.go)
135 lines
3.8 KiB
Go
135 lines
3.8 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/Rain-kl/Wavelet/core"
|
|
"github.com/Rain-kl/Wavelet/core/contracts"
|
|
"github.com/Rain-kl/Wavelet/pkg/config"
|
|
"github.com/Rain-kl/Wavelet/plugins/domain/admin"
|
|
"github.com/Rain-kl/Wavelet/plugins/domain/auth"
|
|
"github.com/Rain-kl/Wavelet/plugins/domain/cap"
|
|
"github.com/Rain-kl/Wavelet/plugins/domain/message_gateway"
|
|
"github.com/Rain-kl/Wavelet/plugins/domain/risk_control"
|
|
"github.com/Rain-kl/Wavelet/plugins/domain/system"
|
|
"github.com/Rain-kl/Wavelet/plugins/domain/upload"
|
|
"github.com/Rain-kl/Wavelet/plugins/domain/user"
|
|
"github.com/Rain-kl/Wavelet/plugins/drivers/driver_asynq_cron"
|
|
"github.com/Rain-kl/Wavelet/plugins/drivers/driver_asynq_worker"
|
|
"github.com/Rain-kl/Wavelet/plugins/drivers/driver_http"
|
|
"github.com/Rain-kl/Wavelet/plugins/infra/cache"
|
|
"github.com/Rain-kl/Wavelet/plugins/infra/database"
|
|
"github.com/Rain-kl/Wavelet/plugins/infra/logger"
|
|
"github.com/Rain-kl/Wavelet/plugins/infra/storage"
|
|
"github.com/pressly/goose/v3"
|
|
)
|
|
|
|
// newWaveletApp creates a core.App wired with Wavelet platform infrastructure, domain plugins, and profile drivers.
|
|
//
|
|
//nolint:contextcheck
|
|
func newWaveletApp(profile core.Profile) *core.App {
|
|
app := core.NewApp(
|
|
core.WithProfile(profile),
|
|
core.WithShutdownTimeout(time.Duration(config.Config.App.GracefulShutdownTimeout)*time.Second),
|
|
)
|
|
|
|
// 1. Register standard infrastructure plugins
|
|
app.Use(
|
|
database.New(),
|
|
cache.New(),
|
|
logger.New(),
|
|
storage.New(),
|
|
)
|
|
|
|
// 2. Register all 8 domain business plugins
|
|
app.Use(
|
|
auth.New(),
|
|
user.New(),
|
|
message_gateway.New(),
|
|
risk_control.New(),
|
|
admin.New(),
|
|
upload.New(),
|
|
cap.New(),
|
|
system.New(),
|
|
)
|
|
|
|
// 3. Bind Goose migration engine
|
|
app.SetMigrationEngine(&gooseEngine{})
|
|
|
|
// 4. Mount runtime drivers for each aspect
|
|
app.Use(
|
|
driver_http.New(driver_http.WithAddr(config.Config.App.Addr)),
|
|
driver_asynq_worker.New(),
|
|
driver_asynq_cron.New(),
|
|
)
|
|
|
|
return app
|
|
}
|
|
|
|
// gooseEngine implements core.MigrationEngine by iterating all plugin-registered
|
|
// migration entries and applying each plugin's migrations against the shared DB.
|
|
//
|
|
// Each plugin owns its own `migrations/*.sql` directory, embedded via go:embed
|
|
// and registered via ctx.Migrations().Register(pluginID, embedFS). The engine
|
|
// resolves DBService from the IoC container and runs every entry in order.
|
|
type gooseEngine struct{}
|
|
|
|
func (e *gooseEngine) Migrate(ctx *core.Context, entries []core.MigrationEntry) error {
|
|
if len(entries) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Resolve DBService from the IoC container.
|
|
var dbSvc contracts.DBService
|
|
if err := core.Using[contracts.DBService](ctx, func(svc contracts.DBService) {
|
|
dbSvc = svc
|
|
}); err != nil {
|
|
return fmt.Errorf("migration: resolve DBService: %w", err)
|
|
}
|
|
|
|
gormDB := dbSvc.GORM()
|
|
if gormDB == nil {
|
|
return fmt.Errorf("migration: DBService.GORM() returned nil")
|
|
}
|
|
|
|
sqlDB, err := gormDB.DB()
|
|
if err != nil {
|
|
return fmt.Errorf("migration: get underlying %s from GORM: %w", dialectName(), err)
|
|
}
|
|
|
|
dialect := dialectName()
|
|
for _, entry := range entries {
|
|
log.Printf("[migrate] applying %s migrations (%s)", entry.PluginID, entry.Dir)
|
|
|
|
goose.SetBaseFS(entry.FS)
|
|
if err := goose.SetDialect(dialect); err != nil {
|
|
return fmt.Errorf("migration %s: set dialect %q: %w", entry.PluginID, dialect, err)
|
|
}
|
|
|
|
dir := entry.Dir
|
|
if dir == "" {
|
|
dir = "migrations"
|
|
}
|
|
|
|
if err := goose.Up(sqlDB, dir); err != nil {
|
|
return fmt.Errorf("migration %s: apply %w", entry.PluginID, err)
|
|
}
|
|
|
|
log.Printf("[migrate] %s migrations applied", entry.PluginID)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// dialectName returns the goose dialect based on the configured database engine.
|
|
func dialectName() string {
|
|
if !config.Config.Database.Enabled {
|
|
return "sqlite3"
|
|
}
|
|
return "postgres"
|
|
} |