package main import ( "context" "fmt" "log" "os" "github.com/jackc/pgx/v5/pgxpool" "golang.org/x/crypto/bcrypt" "github.com/geo-platform/tenant-api/internal/shared/config" "github.com/geo-platform/tenant-api/internal/tenant/prompts" ) func main() { configPath := "configs/config.yaml" if p := os.Getenv("CONFIG_PATH"); p != "" { configPath = p } cfg, err := config.Load(configPath) if err != nil { log.Fatalf("load config: %v", err) } ctx := context.Background() pool, err := pgxpool.New(ctx, cfg.Database.DSN()) if err != nil { log.Fatalf("connect db: %v", err) } defer pool.Close() hash, err := bcrypt.GenerateFromPassword([]byte("Admin@123"), bcrypt.DefaultCost) if err != nil { log.Fatalf("hash password: %v", err) } tx, err := pool.Begin(ctx) if err != nil { log.Fatalf("begin tx: %v", err) } defer func() { _ = tx.Rollback(ctx) }() // Tenant var tenantID int64 err = tx.QueryRow(ctx, ` WITH ensured AS ( INSERT INTO tenants (name, status) VALUES ('GEO Demo', 'active') ON CONFLICT DO NOTHING RETURNING id ) SELECT id FROM ensured UNION ALL SELECT id FROM tenants WHERE name = 'GEO Demo' LIMIT 1 `).Scan(&tenantID) if err != nil { log.Fatalf("insert tenant: %v", err) } // User var userID int64 err = tx.QueryRow(ctx, ` WITH ensured AS ( INSERT INTO users (email, password_hash, name, status) VALUES ('admin@geo.local', $1, 'Admin', 'active') ON CONFLICT DO NOTHING RETURNING id ) SELECT id FROM ensured UNION ALL SELECT id FROM users WHERE email = 'admin@geo.local' LIMIT 1 `, string(hash)).Scan(&userID) if err != nil { log.Fatalf("insert user: %v", err) } // Membership _, err = tx.Exec(ctx, ` INSERT INTO tenant_memberships (tenant_id, user_id, tenant_role) VALUES ($1, $2, 'tenant_admin') ON CONFLICT DO NOTHING `, tenantID, userID) if err != nil { log.Fatalf("insert membership: %v", err) } // Plan var planID int64 err = tx.QueryRow(ctx, ` WITH ensured AS ( INSERT INTO plans (plan_code, name, quota_policy_json, status) VALUES ('free', 'Free Plan', '{"article_generation": 100}', 'active') ON CONFLICT DO NOTHING RETURNING id ) SELECT id FROM ensured UNION ALL SELECT id FROM plans WHERE plan_code = 'free' LIMIT 1 `).Scan(&planID) if err != nil { log.Fatalf("insert plan: %v", err) } // Subscription _, err = tx.Exec(ctx, ` INSERT INTO tenant_plan_subscriptions (tenant_id, plan_id, start_at, end_at, status) SELECT $1, $2, NOW(), NOW() + INTERVAL '1 year', 'active' WHERE NOT EXISTS ( SELECT 1 FROM tenant_plan_subscriptions WHERE tenant_id = $1 AND plan_id = $2 AND status = 'active' ) `, tenantID, planID) if err != nil { log.Fatalf("insert subscription: %v", err) } // Quota ledger _, err = tx.Exec(ctx, ` INSERT INTO tenant_quota_ledgers (tenant_id, quota_type, delta, balance_after, reason) SELECT $1, 'article_generation', 100, 100, 'initial_allocation' WHERE NOT EXISTS ( SELECT 1 FROM tenant_quota_ledgers WHERE tenant_id = $1 AND quota_type = 'article_generation' AND reason = 'initial_allocation' ) `, tenantID) if err != nil { log.Fatalf("insert quota: %v", err) } // Platform templates templates := prompts.PlatformTemplateSeeds() for _, t := range templates { _, err = tx.Exec(ctx, ` INSERT INTO article_templates (scope, origin_type, template_key, template_name, prompt_template, prompt_visibility, card_config_json, status) VALUES ('platform', 'platform', $1, $2, $3, 'visible', $4::jsonb, 'active') ON CONFLICT DO NOTHING `, t.Key, t.Name, t.PromptTemplate, t.CardConfigJSON) if err != nil { log.Fatalf("insert template %s: %v", t.Key, err) } _, err = tx.Exec(ctx, ` UPDATE article_templates SET template_name = $2, prompt_template = $3, card_config_json = $4::jsonb, prompt_visibility = 'visible', status = 'active', updated_at = NOW() WHERE scope = 'platform' AND template_key = $1 AND deleted_at IS NULL `, t.Key, t.Name, t.PromptTemplate, t.CardConfigJSON) if err != nil { log.Fatalf("update template %s: %v", t.Key, err) } } if err := tx.Commit(ctx); err != nil { log.Fatalf("commit: %v", err) } fmt.Println("Seed data inserted successfully.") fmt.Printf(" Tenant ID: %d\n", tenantID) fmt.Printf(" User ID: %d (admin@geo.local / Admin@123)\n", userID) fmt.Printf(" Plan ID: %d (free)\n", planID) fmt.Println(" Templates: 4 platform presets") }