package application import ( "context" "strings" "git.nianxx.cn/wangxuming/NianAIGC/backend/internal/administration" "git.nianxx.cn/wangxuming/NianAIGC/backend/internal/postgres" ) // BootstrapAdminConfig describes the config-driven first super administrator. // When every field is present and the process runs against PostgreSQL, // application startup creates this account exactly once and skips the // bootstrap when a super administrator already exists. type BootstrapAdminConfig struct { Phone string Password string DisplayName string } // Configured reports whether every bootstrap field is present. func (c BootstrapAdminConfig) Configured() bool { return c.Phone != "" && c.Password != "" && c.DisplayName != "" } // ParseBootstrapAdminConfig reads ZHINIAN_BOOTSTRAP_ADMIN_PHONE, // ZHINIAN_BOOTSTRAP_ADMIN_PASSWORD, and ZHINIAN_BOOTSTRAP_ADMIN_NAME. The // variable names mirror the previous scripts/bootstrap-admin.mjs contract; // the display name defaults to 平台超级管理员. func ParseBootstrapAdminConfig(getenv func(string) string) BootstrapAdminConfig { if getenv == nil { return BootstrapAdminConfig{} } return BootstrapAdminConfig{ Phone: strings.TrimSpace(getenv("ZHINIAN_BOOTSTRAP_ADMIN_PHONE")), Password: getenv("ZHINIAN_BOOTSTRAP_ADMIN_PASSWORD"), DisplayName: firstNonEmpty(strings.TrimSpace(getenv("ZHINIAN_BOOTSTRAP_ADMIN_NAME")), "平台超级管理员"), } } // BootstrapSuperAdmin creates the configured first super administrator once. // It is a no-op when the backend is not PostgreSQL, when the configuration is // incomplete, or when a super administrator already exists (including // disabled ones, so a retired administrator cannot trigger a duplicate). // Failures are returned to the caller and fail application startup, so a // misconfigured bootstrap is visible instead of silently missing. func BootstrapSuperAdmin(ctx context.Context, backend postgres.Backend, service *administration.Service, config BootstrapAdminConfig) (bool, error) { if backend != postgres.BackendPostgres || !config.Configured() { return false, nil } existing, err := service.ListAccounts(ctx, administration.Actor{Role: administration.RoleSuperAdmin}, administration.AccountFilters{Role: administration.RoleSuperAdmin, IncludeDisabled: true}) if err != nil { return false, err } if len(existing) > 0 { return false, nil } if _, err := service.CreateAccount(ctx, administration.Actor{Role: administration.RoleSuperAdmin}, administration.CreateAccountInput{ Phone: config.Phone, DisplayName: config.DisplayName, Password: config.Password, Role: administration.RoleSuperAdmin, }); err != nil { return false, err } return true, nil }