// Path: Backend/internal/bootstrap/service.go package bootstrap import ( "context" "errors" "fmt" "strings" "github.com/jackc/pgx/v5" "moku-backend/internal/database" ) const ( primaryOrganizationSlug = "primary-organization" primaryDepartmentSlug = "primary-department" primaryTeamSlug = "primary-team" primaryProjectSlug = "primary-project" organizationWorkspaceSlug = "organization-home" departmentWorkspaceSlug = "department-home" teamWorkspaceSlug = "team-home" projectWorkspaceSlug = "project-home" defaultInstallationHost = "localhost" defaultInstallationMode = "personal" defaultInstallationAccess = "local" defaultInstallationProtocol = "http" defaultOrganizationName = "Moku" defaultPersonalServerSuffix = "Personal" defaultPersonalDisplayName = "Personal" bootstrapWorkspaceKindOrg = "organization" bootstrapWorkspaceKindDept = "department" bootstrapWorkspaceKindTeam = "team" bootstrapWorkspaceKindProject = "project" ) var ( ErrInstallationNotConfigured = errors.New("bootstrap installation step has not been completed") ErrAdminNotConfigured = errors.New("bootstrap admin step has not been completed") ) type Service struct { db *database.DB } type SaveInstanceInput struct { Protocol string Access string Host string } type SaveModeInput struct { Mode string } type SaveAdminInput struct { DisplayName string Email string Password string } type SaveStructureInput struct { OrganizationName string DepartmentName string TeamName string ProjectName string } type InstallationRecord struct { ID string `json:"id"` Mode string `json:"mode"` Access string `json:"access"` Protocol string `json:"protocol"` Host string `json:"host"` IsBootstrapped bool `json:"isBootstrapped"` } type AdminRecord struct { ID string `json:"id"` Email string `json:"email"` DisplayName string `json:"displayName"` IsInstanceAdmin bool `json:"isInstanceAdmin"` HomeTitle string `json:"homeTitle"` } type OrganizationRecord struct { ID string `json:"id"` Name string `json:"name"` Slug string `json:"slug"` } type DepartmentRecord struct { ID string `json:"id"` OrganizationID string `json:"organizationId"` Name string `json:"name"` Slug string `json:"slug"` } type TeamRecord struct { ID string `json:"id"` OrganizationID string `json:"organizationId"` DepartmentID *string `json:"departmentId,omitempty"` Name string `json:"name"` Slug string `json:"slug"` } type ProjectRecord struct { ID string `json:"id"` OrganizationID string `json:"organizationId"` DepartmentID *string `json:"departmentId,omitempty"` TeamID *string `json:"teamId,omitempty"` Name string `json:"name"` Slug string `json:"slug"` } type WorkspaceRecord struct { ID string `json:"id"` OrganizationID string `json:"organizationId"` Name string `json:"name"` Slug string `json:"slug"` Kind string `json:"kind"` DepartmentID *string `json:"departmentId,omitempty"` TeamID *string `json:"teamId,omitempty"` ProjectID *string `json:"projectId,omitempty"` } type StructureRecord struct { Installation InstallationRecord `json:"installation"` Organization namedRecord `json:"organization"` Department namedRecord `json:"department"` Team namedRecord `json:"team"` Project namedRecord `json:"project"` Admin AdminSummary `json:"admin"` } type AdminSummary struct { ID string `json:"id"` Email string `json:"email"` DisplayName string `json:"displayName"` } type BootstrapStructureState struct { Organization *OrganizationRecord `json:"organization,omitempty"` Department *DepartmentRecord `json:"department,omitempty"` Team *TeamRecord `json:"team,omitempty"` Project *ProjectRecord `json:"project,omitempty"` Workspaces []WorkspaceRecord `json:"workspaces"` } type BootstrapState struct { Installation *InstallationRecord `json:"installation,omitempty"` Admin *AdminRecord `json:"admin,omitempty"` Structure BootstrapStructureState `json:"structure"` } type AppShellState struct { Installation *InstallationRecord `json:"installation,omitempty"` Admin *AdminRecord `json:"admin,omitempty"` Organizations []OrganizationRecord `json:"organizations"` Departments []DepartmentRecord `json:"departments"` Teams []TeamRecord `json:"teams"` Projects []ProjectRecord `json:"projects"` Workspaces []WorkspaceRecord `json:"workspaces"` } type namedRecord struct { ID string `json:"id"` Name string `json:"name"` Slug string `json:"slug"` } func NewService(db *database.DB) *Service { return &Service{db: db} } func (service *Service) SaveInstance(ctx context.Context, input SaveInstanceInput) (InstallationRecord, error) { row := service.db.Pool.QueryRow(ctx, ` INSERT INTO installations (singleton, mode, access, protocol, host) VALUES ( TRUE, 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, mode::text, access::text, protocol::text, host, is_bootstrapped; `, 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, mode, access, protocol, host) VALUES ( TRUE, $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), $2) ) ON CONFLICT (singleton) DO UPDATE SET mode = EXCLUDED.mode, updated_at = NOW() RETURNING id::text, mode::text, access::text, protocol::text, host, is_bootstrapped; `, input.Mode, 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 } 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) }() installation, err := loadInstallation(ctx, tx) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return StructureRecord{}, ErrInstallationNotConfigured } return StructureRecord{}, err } admin, err := loadPrimaryAdmin(ctx, tx) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return StructureRecord{}, ErrAdminNotConfigured } return StructureRecord{}, err } organizationName := strings.TrimSpace(input.OrganizationName) if organizationName == "" { organizationName = defaultRootOrganizationName(installation.Mode, installation.Host, 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, 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, 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, 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, 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, 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, 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, admin.ID); err != nil { return StructureRecord{}, err } if err := upsertWorkspace(ctx, tx, organization.ID, organization.Name, organizationWorkspaceSlug, bootstrapWorkspaceKindOrg, admin.ID, nil, nil, nil); err != nil { return StructureRecord{}, err } if err := upsertWorkspace(ctx, tx, organization.ID, department.Name, departmentWorkspaceSlug, bootstrapWorkspaceKindDept, admin.ID, &department.ID, nil, nil); err != nil { return StructureRecord{}, err } if err := upsertWorkspace(ctx, tx, organization.ID, team.Name, teamWorkspaceSlug, bootstrapWorkspaceKindTeam, admin.ID, &department.ID, &team.ID, nil); err != nil { return StructureRecord{}, err } if err := upsertWorkspace(ctx, tx, organization.ID, project.Name, projectWorkspaceSlug, bootstrapWorkspaceKindProject, 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 } return StructureRecord{ Installation: installation, Organization: organization, Department: department, Team: team, Project: project, Admin: 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, installations RESTART IDENTITY; `); err != nil { return err } return tx.Commit(ctx) } func (service *Service) GetInstallation(ctx context.Context) (*InstallationRecord, error) { record, err := scanInstallationRecord(service.db.Pool.QueryRow(ctx, ` SELECT id::text, mode::text, access::text, protocol::text, host, is_bootstrapped FROM installations WHERE singleton = TRUE LIMIT 1; `)) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, nil } return nil, err } return &record, nil } 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 } func (service *Service) GetState(ctx context.Context) (BootstrapState, error) { installation, err := service.GetInstallation(ctx) if err != nil { return BootstrapState{}, err } admin, err := service.GetAdmin(ctx) if err != nil { return BootstrapState{}, err } structure, err := service.GetStructure(ctx) if err != nil { return BootstrapState{}, err } return BootstrapState{ Installation: installation, Admin: admin, Structure: structure, }, nil } func (service *Service) GetAppShellState(ctx context.Context) (AppShellState, error) { installation, err := service.GetInstallation(ctx) if err != nil { return AppShellState{}, err } admin, err := service.GetAdmin(ctx) if err != nil { return AppShellState{}, err } organizations, err := service.listOrganizations(ctx) if err != nil { return AppShellState{}, err } departments, err := service.listDepartments(ctx) if err != nil { return AppShellState{}, err } teams, err := service.listTeams(ctx) if err != nil { return AppShellState{}, err } projects, err := service.listProjects(ctx) if err != nil { return AppShellState{}, err } workspaces, err := service.listWorkspaces(ctx) if err != nil { return AppShellState{}, err } return AppShellState{ Installation: installation, Admin: admin, Organizations: organizations, Departments: departments, Teams: teams, Projects: projects, Workspaces: workspaces, }, nil } func scanInstallationRecord(row pgx.Row) (InstallationRecord, error) { var record InstallationRecord if err := row.Scan(&record.ID, &record.Mode, &record.Access, &record.Protocol, &record.Host, &record.IsBootstrapped); err != nil { return InstallationRecord{}, err } return record, nil } func (service *Service) loadPrimaryOrganization(ctx context.Context) (*OrganizationRecord, error) { var record OrganizationRecord err := service.db.Pool.QueryRow(ctx, ` SELECT id::text, name, slug FROM organizations ORDER BY CASE WHEN slug = $1 THEN 0 ELSE 1 END, created_at ASC LIMIT 1; `, primaryOrganizationSlug).Scan(&record.ID, &record.Name, &record.Slug) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, nil } return nil, err } return &record, nil } func (service *Service) loadPrimaryDepartment(ctx context.Context) (*DepartmentRecord, error) { var record DepartmentRecord err := service.db.Pool.QueryRow(ctx, ` SELECT id::text, organization_id::text, name, slug FROM departments ORDER BY CASE WHEN slug = $1 THEN 0 ELSE 1 END, created_at ASC LIMIT 1; `, primaryDepartmentSlug).Scan(&record.ID, &record.OrganizationID, &record.Name, &record.Slug) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, nil } return nil, err } return &record, nil } func (service *Service) loadPrimaryTeam(ctx context.Context) (*TeamRecord, error) { var record TeamRecord err := service.db.Pool.QueryRow(ctx, ` SELECT id::text, organization_id::text, department_id::text, name, slug FROM teams ORDER BY CASE WHEN slug = $1 THEN 0 ELSE 1 END, created_at ASC LIMIT 1; `, primaryTeamSlug).Scan(&record.ID, &record.OrganizationID, &record.DepartmentID, &record.Name, &record.Slug) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, nil } return nil, err } return &record, nil } func (service *Service) loadPrimaryProject(ctx context.Context) (*ProjectRecord, error) { var record ProjectRecord err := service.db.Pool.QueryRow(ctx, ` SELECT id::text, organization_id::text, department_id::text, team_id::text, name, slug FROM projects ORDER BY CASE WHEN slug = $1 THEN 0 ELSE 1 END, created_at ASC LIMIT 1; `, primaryProjectSlug).Scan(&record.ID, &record.OrganizationID, &record.DepartmentID, &record.TeamID, &record.Name, &record.Slug) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, nil } return nil, err } return &record, nil } func (service *Service) listOrganizations(ctx context.Context) ([]OrganizationRecord, error) { rows, err := service.db.Pool.Query(ctx, ` SELECT id::text, name, slug FROM organizations ORDER BY created_at ASC; `) if err != nil { return nil, err } defer rows.Close() var records []OrganizationRecord for rows.Next() { var record OrganizationRecord if err := rows.Scan(&record.ID, &record.Name, &record.Slug); err != nil { return nil, err } records = append(records, record) } return records, rows.Err() } func (service *Service) listDepartments(ctx context.Context) ([]DepartmentRecord, error) { rows, err := service.db.Pool.Query(ctx, ` SELECT id::text, organization_id::text, name, slug FROM departments ORDER BY created_at ASC; `) if err != nil { return nil, err } defer rows.Close() var records []DepartmentRecord for rows.Next() { var record DepartmentRecord if err := rows.Scan(&record.ID, &record.OrganizationID, &record.Name, &record.Slug); err != nil { return nil, err } records = append(records, record) } return records, rows.Err() } func (service *Service) listTeams(ctx context.Context) ([]TeamRecord, error) { rows, err := service.db.Pool.Query(ctx, ` SELECT id::text, organization_id::text, department_id::text, name, slug FROM teams ORDER BY created_at ASC; `) if err != nil { return nil, err } defer rows.Close() var records []TeamRecord for rows.Next() { var record TeamRecord if err := rows.Scan(&record.ID, &record.OrganizationID, &record.DepartmentID, &record.Name, &record.Slug); err != nil { return nil, err } records = append(records, record) } return records, rows.Err() } func (service *Service) listProjects(ctx context.Context) ([]ProjectRecord, error) { rows, err := service.db.Pool.Query(ctx, ` SELECT id::text, organization_id::text, department_id::text, team_id::text, name, slug FROM projects ORDER BY created_at ASC; `) if err != nil { return nil, err } defer rows.Close() var records []ProjectRecord for rows.Next() { var record ProjectRecord if err := rows.Scan(&record.ID, &record.OrganizationID, &record.DepartmentID, &record.TeamID, &record.Name, &record.Slug); err != nil { return nil, err } records = append(records, record) } return records, rows.Err() } func (service *Service) listWorkspaces(ctx context.Context) ([]WorkspaceRecord, error) { rows, err := service.db.Pool.Query(ctx, ` SELECT id::text, organization_id::text, name, slug, kind::text, department_id::text, team_id::text, project_id::text FROM workspaces ORDER BY created_at ASC; `) if err != nil { return nil, err } defer rows.Close() var records []WorkspaceRecord for rows.Next() { var record WorkspaceRecord if err := rows.Scan(&record.ID, &record.OrganizationID, &record.Name, &record.Slug, &record.Kind, &record.DepartmentID, &record.TeamID, &record.ProjectID); err != nil { return nil, err } records = append(records, record) } return records, rows.Err() } func loadInstallation(ctx context.Context, tx pgx.Tx) (InstallationRecord, error) { return scanInstallationRecord(tx.QueryRow(ctx, ` SELECT id::text, mode::text, access::text, protocol::text, host, is_bootstrapped FROM installations WHERE singleton = TRUE LIMIT 1; `)) } func loadPrimaryAdmin(ctx context.Context, tx pgx.Tx) (AdminSummary, error) { var admin AdminSummary if err := tx.QueryRow(ctx, ` SELECT id::text, email, display_name FROM users WHERE is_instance_admin = TRUE ORDER BY created_at ASC LIMIT 1; `).Scan(&admin.ID, &admin.Email, &admin.DisplayName); err != nil { return AdminSummary{}, err } return admin, nil } func updateBootstrappedInstallation(ctx context.Context, tx pgx.Tx) (InstallationRecord, error) { return scanInstallationRecord(tx.QueryRow(ctx, ` UPDATE installations SET is_bootstrapped = TRUE, bootstrapped_at = COALESCE(bootstrapped_at, NOW()), updated_at = NOW() WHERE singleton = TRUE RETURNING id::text, mode::text, access::text, protocol::text, host, is_bootstrapped; `)) } func upsertNamedRecord(ctx context.Context, tx pgx.Tx, query string, args ...any) (namedRecord, error) { var record namedRecord if err := tx.QueryRow(ctx, query, args...).Scan(&record.ID, &record.Name, &record.Slug); err != nil { return namedRecord{}, err } return record, nil } func upsertWorkspace(ctx context.Context, tx pgx.Tx, organizationID, name, slug, kind, createdByUserID string, departmentID, teamID, projectID *string) error { _, err := tx.Exec(ctx, ` INSERT INTO workspaces (organization_id, name, slug, kind, created_by_user_id, department_id, team_id, project_id) VALUES ($1::uuid, $2, $3, $4::workspace_kind, $5::uuid, $6::uuid, $7::uuid, $8::uuid) ON CONFLICT (organization_id, slug) DO UPDATE SET name = EXCLUDED.name, kind = EXCLUDED.kind, created_by_user_id = EXCLUDED.created_by_user_id, department_id = EXCLUDED.department_id, team_id = EXCLUDED.team_id, project_id = EXCLUDED.project_id, updated_at = NOW(); `, organizationID, name, slug, kind, createdByUserID, departmentID, teamID, projectID) return err } func defaultRootOrganizationName(mode, host, adminDisplayName string) string { trimmedHost := strings.TrimSpace(host) trimmedAdminDisplayName := strings.TrimSpace(adminDisplayName) if strings.EqualFold(mode, defaultInstallationMode) { if trimmedAdminDisplayName != "" { return fmt.Sprintf("%s %s", trimmedAdminDisplayName, defaultPersonalServerSuffix) } return defaultPersonalDisplayName } if trimmedHost != "" { return trimmedHost } return defaultOrganizationName } func personalHomeTitle(displayName string) string { trimmedDisplayName := strings.TrimSpace(displayName) if trimmedDisplayName == "" { return "Home" } if strings.HasSuffix(strings.ToLower(trimmedDisplayName), "s") { return fmt.Sprintf("%s' Home", trimmedDisplayName) } return fmt.Sprintf("%s's Home", trimmedDisplayName) }