package app import ( "context" "errors" "fmt" "time" "github.com/h44z/wg-portal/internal/config" "github.com/h44z/wg-portal/internal/domain" "github.com/sirupsen/logrus" evbus "github.com/vardius/message-bus" ) type App struct { Config *config.Config bus evbus.MessageBus Authenticator UserManager WireGuardManager StatisticsCollector ConfigFileManager MailManager } func New(cfg *config.Config, bus evbus.MessageBus, authenticator Authenticator, users UserManager, wireGuard WireGuardManager, stats StatisticsCollector, cfgFiles ConfigFileManager, mailer MailManager) (*App, error) { a := &App{ Config: cfg, bus: bus, Authenticator: authenticator, UserManager: users, WireGuardManager: wireGuard, StatisticsCollector: stats, ConfigFileManager: cfgFiles, MailManager: mailer, } startupContext, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // Switch to admin user context startupContext = domain.SetUserInfo(startupContext, domain.SystemAdminContextUserInfo()) if err := a.createDefaultUser(startupContext); err != nil { return nil, fmt.Errorf("failed to create default user: %w", err) } if err := a.importNewInterfaces(startupContext); err != nil { return nil, fmt.Errorf("failed to import new interfaces: %w", err) } if err := a.restoreInterfaceState(startupContext); err != nil { return nil, fmt.Errorf("failed to restore interface state: %w", err) } return a, nil } func (a *App) Startup(ctx context.Context) error { a.UserManager.StartBackgroundJobs(ctx) a.StatisticsCollector.StartBackgroundJobs(ctx) a.WireGuardManager.StartBackgroundJobs(ctx) return nil } func (a *App) importNewInterfaces(ctx context.Context) error { if !a.Config.Core.ImportExisting { logrus.Trace("skipping interface import - feature disabled") return nil // feature disabled } importedCount, err := a.ImportNewInterfaces(ctx) if err != nil { return err } if importedCount > 0 { logrus.Infof("%d new interfaces imported", importedCount) } return nil } func (a *App) restoreInterfaceState(ctx context.Context) error { if !a.Config.Core.RestoreState { logrus.Trace("skipping interface state restore - feature disabled") return nil // feature disabled } err := a.RestoreInterfaceState(ctx, true) if err != nil { return err } logrus.Info("interface state restored") return nil } func (a *App) createDefaultUser(ctx context.Context) error { adminUserId := domain.UserIdentifier(a.Config.Core.AdminUser) if adminUserId == "" { logrus.Trace("skipping default user creation - admin user is blank") return nil // empty admin user - do not create } _, err := a.GetUser(ctx, adminUserId) if err != nil && !errors.Is(err, domain.ErrNotFound) { return err } if err == nil { logrus.Trace("skipping default user creation - admin user already exists") return nil // admin user already exists } now := time.Now() admin, err := a.CreateUser(ctx, &domain.User{ BaseModel: domain.BaseModel{ CreatedBy: "system", UpdatedBy: "system", CreatedAt: now, UpdatedAt: now, }, Identifier: adminUserId, Email: "admin@wgportal.local", Source: domain.UserSourceDatabase, ProviderName: "", IsAdmin: true, Firstname: "WireGuard Portal", Lastname: "Admin", Phone: "", Department: "", Notes: "default administrator user", Password: domain.PrivateString(a.Config.Core.AdminPassword), Disabled: nil, DisabledReason: "", Locked: nil, LockedReason: "", LinkedPeerCount: 0, }) if err != nil { return err } logrus.Infof("admin user %s created", admin.Identifier) return nil }