// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial // SPDX-FileCopyrightText: 2025-2026 Christian Galo package cmd import ( "context" "database/sql" "fmt" "log/slog" "time" "git.coopcloud.tech/wiki-cafe/member-console/internal/db" "git.coopcloud.tech/wiki-cafe/member-console/internal/domains" "git.coopcloud.tech/wiki-cafe/member-console/internal/integration" "git.coopcloud.tech/wiki-cafe/member-console/internal/integrations" "git.coopcloud.tech/wiki-cafe/member-console/internal/systemtenant" ) // ensureBootDataState performs the data-state work `start` does after // migrations and before serving, and `seed-demo` does before seeding, so a // database that was reset, migrated, and reseeded (test/reset-app-db.sh, // the screen-capture utility's first step) holds exactly what a booted // deployment holds: // // - every installed integration's provider record, after its key is // checked against its manifest (integration-registration); // - the System tenant (system-tenant); // - expired pending domain claims swept (claim-expiry-and-carve-guards // D1/D3): core registry work with no integration in it, run BEFORE the // reconciliation below because a stranded pending external claim at a // configured farm domain is exactly what makes EnsureOperatorRoot fail; // logged, not fatal, since an unswept claim holds one name where an // unensurable root leaves every hosted name unallocatable; // - monthly RANGE partitions for core.webhook_events for the months ahead // (webhook-partition-maintenance); logged, not fatal; // - each integration's domains reconciliation (design D8): operator roots // ensured from deployment configuration and placements backfilled for // resources that predate the registry. Unconditional, not an // integration's Startup hook, which runs only when Temporal is // configured. Fatal: a registry that does not fit the deployment's own // namespace configuration is a misconfiguration. func ensureBootDataState(ctx context.Context, database *sql.DB, logger *slog.Logger, integs []integrations.Integration) error { providerSources := make([]integration.ProviderSource, 0, len(integs)) for _, integ := range integs { ps := integ.Provider() if err := integration.AssertKeyMatch(integ.Key(), ps.ProviderManifest()); err != nil { return fmt.Errorf("integration key mismatch: %w", err) } providerSources = append(providerSources, ps) } if err := integration.RegisterProviders(ctx, database, providerSources); err != nil { return fmt.Errorf("register providers: %w", err) } if _, err := systemtenant.Ensure(ctx, database); err != nil { return fmt.Errorf("ensure system tenant: %w", err) } if result, err := domains.NewRegistry(database).SweepExpiredClaims(ctx); err != nil { logger.Error("domain claim expiry sweep failed", slog.Int("candidates", result.Candidates), slog.Int("expired", result.Expired), slog.Int("failed", result.Failed), slog.Any("error", err)) } else if result.Candidates > 0 { logger.Info("domain claim expiry sweep completed", slog.Int("candidates", result.Candidates), slog.Int("expired", result.Expired)) } if err := db.EnsureWebhookEventPartitions(ctx, database, time.Now(), db.DefaultWebhookPartitionMonthsAhead); err != nil { logger.Warn("webhook_events partition ensure failed", slog.Any("error", err)) } for _, integ := range integs { dr, ok := integ.(domainsReconciler) if !ok { continue } if err := dr.ReconcileDomains(ctx, database, logger); err != nil { return fmt.Errorf("reconcile domains registry for %s: %w", integ.Key(), err) } } return nil }