// Path: Backend/internal/bootstrap/service.go package bootstrap import ( "context" "errors" "strings" "github.com/jackc/pgx/v5" ) func (service *Service) SaveInstance(ctx context.Context, input SaveInstanceInput) (InstallationRecord, error) { row := service.db.Pool.QueryRow(ctx, ` INSERT INTO installations (singleton, name, mode, access, protocol, host) VALUES ( TRUE, COALESCE((SELECT name FROM installations WHERE singleton = TRUE LIMIT 1), ''), COALESCE((SELECT mode FROM installations WHERE singleton = TRUE LIMIT 1), 'personal'::instance_mode), $1::instance_access, $2::instance_protocol, $3 ) ON CONFLICT (singleton) DO UPDATE SET access = EXCLUDED.access, protocol = EXCLUDED.protocol, host = EXCLUDED.host, updated_at = NOW() RETURNING id::text, name, mode::text, access::text, protocol::text, host, is_bootstrapped, materialization_status::text, materialization_error; `, input.Access, input.Protocol, input.Host) return scanInstallationRecord(row) } func (service *Service) SaveMode(ctx context.Context, input SaveModeInput) (InstallationRecord, error) { row := service.db.Pool.QueryRow(ctx, ` INSERT INTO installations (singleton, name, mode, access, protocol, host) VALUES ( TRUE, $2, $1::instance_mode, COALESCE((SELECT access FROM installations WHERE singleton = TRUE LIMIT 1), 'local'::instance_access), COALESCE((SELECT protocol FROM installations WHERE singleton = TRUE LIMIT 1), 'http'::instance_protocol), COALESCE((SELECT host FROM installations WHERE singleton = TRUE LIMIT 1), $3) ) ON CONFLICT (singleton) DO UPDATE SET name = EXCLUDED.name, mode = EXCLUDED.mode, updated_at = NOW() RETURNING id::text, name, mode::text, access::text, protocol::text, host, is_bootstrapped, materialization_status::text, materialization_error; `, input.Mode, input.Name, defaultInstallationHost) return scanInstallationRecord(row) } func (service *Service) SaveAdmin(ctx context.Context, input SaveAdminInput) (AdminRecord, error) { tx, err := service.db.Pool.BeginTx(ctx, pgx.TxOptions{}) if err != nil { return AdminRecord{}, err } defer func() { _ = tx.Rollback(ctx) }() if _, err := tx.Exec(ctx, ` UPDATE users SET is_instance_admin = FALSE, updated_at = NOW() WHERE is_instance_admin = TRUE; `); err != nil { return AdminRecord{}, err } var record AdminRecord if err := tx.QueryRow(ctx, ` INSERT INTO users (email, display_name, password_hash, is_instance_admin) VALUES ($1, $2, crypt($3, gen_salt('bf')), TRUE) ON CONFLICT ((LOWER(email))) DO UPDATE SET email = EXCLUDED.email, display_name = EXCLUDED.display_name, password_hash = crypt($3, gen_salt('bf')), is_instance_admin = TRUE, updated_at = NOW() RETURNING id::text, email, display_name, is_instance_admin; `, input.Email, input.DisplayName, input.Password).Scan( &record.ID, &record.Email, &record.DisplayName, &record.IsInstanceAdmin, ); err != nil { return AdminRecord{}, err } record.HomeTitle = personalHomeTitle(record.DisplayName) if err := tx.QueryRow(ctx, ` INSERT INTO user_homes (user_id, title) VALUES ($1::uuid, $2) ON CONFLICT (user_id) DO UPDATE SET title = EXCLUDED.title, updated_at = NOW() RETURNING title; `, record.ID, record.HomeTitle).Scan(&record.HomeTitle); err != nil { return AdminRecord{}, err } if err := tx.Commit(ctx); err != nil { return AdminRecord{}, err } return record, nil } // SaveStructure persists the bootstrap domain records synchronously, then hands the // slow POSIX/projector materialization work to the background worker. // // This keeps the API request responsible for validation and durable relational writes, // while the worker owns retryable filesystem/projection side effects. func (service *Service) SaveStructure(ctx context.Context, input SaveStructureInput) (StructureRecord, error) { tx, err := service.db.Pool.BeginTx(ctx, pgx.TxOptions{}) if err != nil { return StructureRecord{}, err } defer func() { _ = tx.Rollback(ctx) }() prerequisites, err := service.loadBootstrapStructurePrerequisites(ctx, tx) if err != nil { return StructureRecord{}, err } organizationName := strings.TrimSpace(input.OrganizationName) if organizationName == "" { organizationName = defaultRootOrganizationName( prerequisites.installation.Name, prerequisites.installation.Mode, prerequisites.installation.Host, prerequisites.admin.DisplayName, ) } organization, err := upsertNamedRecord(ctx, tx, ` INSERT INTO organizations (name, slug, created_by_user_id) VALUES ($1, $2, $3::uuid) ON CONFLICT (slug) DO UPDATE SET name = EXCLUDED.name, created_by_user_id = EXCLUDED.created_by_user_id, updated_at = NOW() RETURNING id::text, name, slug; `, organizationName, primaryOrganizationSlug, prerequisites.admin.ID) if err != nil { return StructureRecord{}, err } if _, err := tx.Exec(ctx, ` INSERT INTO organization_memberships (organization_id, user_id, role) VALUES ($1::uuid, $2::uuid, 'owner'::membership_role) ON CONFLICT (organization_id, user_id) DO UPDATE SET role = EXCLUDED.role; `, organization.ID, prerequisites.admin.ID); err != nil { return StructureRecord{}, err } department, err := upsertNamedRecord(ctx, tx, ` INSERT INTO departments (organization_id, name, slug, created_by_user_id) VALUES ($1::uuid, $2, $3, $4::uuid) ON CONFLICT (organization_id, slug) DO UPDATE SET name = EXCLUDED.name, created_by_user_id = EXCLUDED.created_by_user_id, updated_at = NOW() RETURNING id::text, name, slug; `, organization.ID, input.DepartmentName, primaryDepartmentSlug, prerequisites.admin.ID) if err != nil { return StructureRecord{}, err } team, err := upsertNamedRecord(ctx, tx, ` INSERT INTO teams (organization_id, department_id, name, slug, created_by_user_id) VALUES ($1::uuid, $2::uuid, $3, $4, $5::uuid) ON CONFLICT (organization_id, slug) DO UPDATE SET department_id = EXCLUDED.department_id, name = EXCLUDED.name, created_by_user_id = EXCLUDED.created_by_user_id, updated_at = NOW() RETURNING id::text, name, slug; `, organization.ID, department.ID, input.TeamName, primaryTeamSlug, prerequisites.admin.ID) if err != nil { return StructureRecord{}, err } if _, err := tx.Exec(ctx, ` INSERT INTO team_memberships (team_id, user_id, role) VALUES ($1::uuid, $2::uuid, 'owner'::membership_role) ON CONFLICT (team_id, user_id) DO UPDATE SET role = EXCLUDED.role; `, team.ID, prerequisites.admin.ID); err != nil { return StructureRecord{}, err } project, err := upsertNamedRecord(ctx, tx, ` INSERT INTO projects (organization_id, department_id, team_id, name, slug, created_by_user_id) VALUES ($1::uuid, $2::uuid, $3::uuid, $4, $5, $6::uuid) ON CONFLICT (organization_id, slug) DO UPDATE SET department_id = EXCLUDED.department_id, team_id = EXCLUDED.team_id, name = EXCLUDED.name, created_by_user_id = EXCLUDED.created_by_user_id, updated_at = NOW() RETURNING id::text, name, slug; `, organization.ID, department.ID, team.ID, input.ProjectName, primaryProjectSlug, prerequisites.admin.ID) if err != nil { return StructureRecord{}, err } if _, err := tx.Exec(ctx, ` INSERT INTO project_memberships (project_id, user_id, role) VALUES ($1::uuid, $2::uuid, 'owner'::membership_role) ON CONFLICT (project_id, user_id) DO UPDATE SET role = EXCLUDED.role; `, project.ID, prerequisites.admin.ID); err != nil { return StructureRecord{}, err } if err := upsertWorkspace(ctx, tx, organization.ID, organization.Name, organizationWorkspaceSlug, bootstrapWorkspaceKindOrg, prerequisites.admin.ID, nil, nil, nil); err != nil { return StructureRecord{}, err } if err := upsertWorkspace(ctx, tx, organization.ID, department.Name, departmentWorkspaceSlug, bootstrapWorkspaceKindDept, prerequisites.admin.ID, &department.ID, nil, nil); err != nil { return StructureRecord{}, err } if err := upsertWorkspace(ctx, tx, organization.ID, team.Name, teamWorkspaceSlug, bootstrapWorkspaceKindTeam, prerequisites.admin.ID, &department.ID, &team.ID, nil); err != nil { return StructureRecord{}, err } if err := upsertWorkspace(ctx, tx, organization.ID, project.Name, projectWorkspaceSlug, bootstrapWorkspaceKindProject, prerequisites.admin.ID, &department.ID, &team.ID, &project.ID); err != nil { return StructureRecord{}, err } installation, err := updateBootstrappedInstallation(ctx, tx) if err != nil { return StructureRecord{}, err } if err := tx.Commit(ctx); err != nil { return StructureRecord{}, err } if err := service.enqueueBootstrapStructureMaterialization(ctx, &installation); err != nil { return StructureRecord{}, err } return StructureRecord{ Installation: installation, Organization: organization, Department: department, Team: team, Project: project, Admin: prerequisites.admin, }, nil } func (service *Service) ResetDevelopmentState(ctx context.Context) error { tx, err := service.db.Pool.BeginTx(ctx, pgx.TxOptions{}) if err != nil { return err } defer func() { _ = tx.Rollback(ctx) }() if _, err := tx.Exec(ctx, ` TRUNCATE TABLE project_memberships, team_memberships, organization_memberships, workspaces, projects, teams, departments, user_homes, users, organizations, background_jobs, installations RESTART IDENTITY; `); err != nil { return err } return tx.Commit(ctx) } func (service *Service) GetAdmin(ctx context.Context) (*AdminRecord, error) { var record AdminRecord err := service.db.Pool.QueryRow(ctx, ` SELECT u.id::text, u.email, u.display_name, u.is_instance_admin, COALESCE(uh.title, '') FROM users u LEFT JOIN user_homes uh ON uh.user_id = u.id WHERE u.is_instance_admin = TRUE ORDER BY u.created_at ASC LIMIT 1; `).Scan( &record.ID, &record.Email, &record.DisplayName, &record.IsInstanceAdmin, &record.HomeTitle, ) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, nil } return nil, err } return &record, nil } func (service *Service) GetStructure(ctx context.Context) (BootstrapStructureState, error) { workspaces, err := service.listWorkspaces(ctx) if err != nil { return BootstrapStructureState{}, err } organization, err := service.loadPrimaryOrganization(ctx) if err != nil { return BootstrapStructureState{}, err } department, err := service.loadPrimaryDepartment(ctx) if err != nil { return BootstrapStructureState{}, err } team, err := service.loadPrimaryTeam(ctx) if err != nil { return BootstrapStructureState{}, err } project, err := service.loadPrimaryProject(ctx) if err != nil { return BootstrapStructureState{}, err } return BootstrapStructureState{ Organization: organization, Department: department, Team: team, Project: project, Workspaces: workspaces, }, nil }