// Path: Backend/internal/bootstrap/project_queries.go package bootstrap import ( "context" "errors" "github.com/jackc/pgx/v5" ) 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) loadProjectByID(ctx context.Context, projectID string) (*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 WHERE id = $1::uuid LIMIT 1; `, projectID).Scan(&record.ID, &record.OrganizationID, &record.DepartmentID, &record.TeamID, &record.Name, &record.Slug) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, ErrProjectNotFound } return nil, err } return &record, nil } func (service *Service) GetProjectHierarchyFolders(ctx context.Context, projectID string) ([]ProjectHierarchyFolderRecord, error) { return service.getProjectHierarchyFoldersByRootPath(ctx, projectID, projectHierarchyRootPath) } func (service *Service) GetProjectTreeFolders(ctx context.Context, projectID string) ([]ProjectHierarchyFolderRecord, error) { return service.getProjectHierarchyFoldersByRootPath(ctx, projectID, projectTreeRootPath) } func (service *Service) GetProjectTreeNodes(ctx context.Context, projectID string) ([]ProjectTreeNodeRecord, error) { return service.getProjectTreeNodesByRootPath(ctx, projectID, projectTreeRootPath) } func (service *Service) getProjectHierarchyFoldersByRootPath( ctx context.Context, projectID string, rootPath func(projectSlug string) string, ) ([]ProjectHierarchyFolderRecord, error) { project, err := service.loadProjectByID(ctx, projectID) if err != nil { return nil, err } rootParentPath := rootPath(project.Slug) rows, err := service.db.Pool.Query(ctx, ` SELECT COALESCE(folder_meta.resource_id, ''), directories.path, COALESCE(directories.parent_path, ''), COALESCE(folder_meta.resource_name, directories.resource_name, '') FROM posix_nodes AS directories LEFT JOIN posix_nodes AS folder_meta ON folder_meta.path = directories.path || '/folder.json' AND folder_meta.node_kind = 'file'::posix_node_kind WHERE directories.node_kind = 'directory'::posix_node_kind AND directories.logical_type = 'hierarchy_folder' AND directories.project_slug = $1 AND directories.path LIKE $2 ORDER BY directories.depth ASC, directories.path ASC; `, project.Slug, rootParentPath+"/%") if err != nil { return nil, err } defer rows.Close() var folderRows []projectHierarchyFolderRow for rows.Next() { var row projectHierarchyFolderRow if err := rows.Scan(&row.ID, &row.Path, &row.ParentPath, &row.Label); err != nil { return nil, err } folderRows = append(folderRows, row) } if err := rows.Err(); err != nil { return nil, err } folders := buildProjectHierarchyFolderTree(folderRows, rootParentPath) folderOrder := service.readProjectFolderOrder(project.Slug, rootParentPath) return applyProjectHierarchyFolderOrdering(folders, folderOrder), nil } func (service *Service) getProjectTreeNodesByRootPath( ctx context.Context, projectID string, rootPath func(projectSlug string) string, ) ([]ProjectTreeNodeRecord, error) { project, err := service.loadProjectByID(ctx, projectID) if err != nil { return nil, err } rootParentPath := rootPath(project.Slug) rows, err := service.db.Pool.Query(ctx, ` SELECT COALESCE(node_meta.resource_id, ''), directories.path, COALESCE(directories.parent_path, ''), COALESCE(node_meta.resource_name, directories.resource_name, ''), directories.logical_type, COALESCE(node_meta.content_json->>'type', directories.content_json->>'type', '') FROM posix_nodes AS directories LEFT JOIN posix_nodes AS node_meta ON node_meta.path = directories.path || CASE WHEN directories.logical_type = 'hierarchy_folder' THEN '/folder.json' WHEN directories.logical_type = 'item' THEN '/item.json' ELSE '' END AND node_meta.node_kind = 'file'::posix_node_kind WHERE directories.node_kind = 'directory'::posix_node_kind AND directories.logical_type IN ('hierarchy_folder', 'item') AND directories.project_slug = $1 AND directories.path LIKE $2 ORDER BY directories.depth ASC, directories.path ASC; `, project.Slug, rootParentPath+"/%") if err != nil { return nil, err } defer rows.Close() var nodeRows []projectTreeNodeRow for rows.Next() { var row projectTreeNodeRow if err := rows.Scan(&row.ID, &row.Path, &row.ParentPath, &row.Label, &row.Kind, &row.ItemType); err != nil { return nil, err } nodeRows = append(nodeRows, row) } if err := rows.Err(); err != nil { return nil, err } nodes := buildProjectTreeNodeTree(nodeRows, rootParentPath) folderOrder := service.readProjectFolderOrder(project.Slug, rootParentPath) return applyProjectTreeNodeOrdering(nodes, folderOrder), nil } 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() }