From 841a05572ec48ac56a928fbdec08fa8784396a5c Mon Sep 17 00:00:00 2001 From: GreenEclipse Date: Thu, 13 Aug 2026 23:43:53 +0200 Subject: [PATCH] fix --- apps/api/cmd/seed-platform-admin/main.go | 33 ++++++++++++++++++++++-- scripts/seed-platform-admin.mjs | 7 ++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/apps/api/cmd/seed-platform-admin/main.go b/apps/api/cmd/seed-platform-admin/main.go index 4db11dd..71c145c 100644 --- a/apps/api/cmd/seed-platform-admin/main.go +++ b/apps/api/cmd/seed-platform-admin/main.go @@ -90,7 +90,8 @@ func main() { log.Fatal(err) } billingSvc := &billing.Service{Pool: pg} - if err := billingSvc.ProvisionFreePlan(ctx, companyID); err != nil { + planNote, err := ensureFreePlan(ctx, pg, billingSvc, companyID) + if err != nil { log.Fatalf("provision Free plan: %v", err) } companyAction := "existing" @@ -98,7 +99,7 @@ func main() { companyAction = "created" } fmt.Printf(" company: %s (%s) [%s]\n", companyName, companyID, companyAction) - fmt.Println(" plan: Free (provisioned)") + fmt.Printf(" plan: %s\n", planNote) fmt.Println() fmt.Println("Sign in on the web app (company is selected on login). Platform panel: /admin.") } @@ -311,3 +312,31 @@ func ensureAdminCompany(ctx context.Context, pg *pgxpool.Pool, userID uuid.UUID, } return companyID, companyName, true, nil } + +// ensureFreePlan assigns Free when missing. Syncs company_plans id sequence first — +// cutover/import DBs often leave BIGSERIAL behind MAX(id), which surfaces as +// duplicate key on company_plans_pkey during INSERT. +func ensureFreePlan(ctx context.Context, pg *pgxpool.Pool, billingSvc *billing.Service, companyID uuid.UUID) (string, error) { + if err := billingSvc.EnsureDefaultPlans(ctx); err != nil { + return "", err + } + if _, err := pg.Exec(ctx, ` + SELECT setval( + pg_get_serial_sequence('company_plans', 'id'), + GREATEST(1, (SELECT COALESCE(MAX(id), 1) FROM company_plans)) + )`); err != nil { + return "", fmt.Errorf("sync company_plans sequence: %w", err) + } + assigned, err := billingSvc.AssignPlanByNameIfMissing(ctx, companyID, "Free") + if err != nil { + // Race / still-stuck sequence: if an active plan exists, treat as OK. + if has, hasErr := billingSvc.HasActivePlan(ctx, companyID); hasErr == nil && has { + return "already active (assign skipped after error)", nil + } + return "", err + } + if assigned { + return "Free (assigned)", nil + } + return "already active (unchanged)", nil +} diff --git a/scripts/seed-platform-admin.mjs b/scripts/seed-platform-admin.mjs index 27eabd6..55b95d4 100644 --- a/scripts/seed-platform-admin.mjs +++ b/scripts/seed-platform-admin.mjs @@ -62,7 +62,12 @@ function parseArgs(argv) { continue; } if (a === "--company" || a === "-company") { - out.company = String(argv[++i] || "").trim(); + // Join remaining tokens until next --flag so unquoted "Admin Company" works. + const parts = []; + while (i + 1 < argv.length && !String(argv[i + 1]).startsWith("-")) { + parts.push(String(argv[++i])); + } + out.company = parts.join(" ").trim(); continue; } if (a === "--help" || a === "-h") usage();