// Path: Backend/internal/posixproj/projector.go package posixproj import ( "context" "fmt" "strings" "moku-backend/internal/database" ) func NewProjector(db *database.DB, root string) *Projector { return &Projector{db: db, root: strings.TrimSpace(root)} } func (projector *Projector) Rebuild(ctx context.Context) error { _, err := projector.RebuildWithSummary(ctx) return err } func (projector *Projector) RebuildWithSummary(ctx context.Context) (RebuildSummary, error) { if projector == nil || projector.db == nil || projector.db.Pool == nil { return RebuildSummary{}, nil } nodes, err := ScanRoot(projector.root) if err != nil { return RebuildSummary{}, err } summary := summarizeNodes(nodes) tx, err := projector.db.Pool.Begin(ctx) if err != nil { return RebuildSummary{}, err } defer func() { _ = tx.Rollback(ctx) }() if _, err := tx.Exec(ctx, `DELETE FROM posix_nodes;`); err != nil { return RebuildSummary{}, fmt.Errorf("clear posix_nodes: %w", err) } for _, node := range nodes { if _, err := tx.Exec(ctx, ` INSERT INTO posix_nodes ( path, parent_path, name, depth, node_kind, logical_type, file_role, resource_id, resource_name, resource_slug, installation_id, organization_id, organization_slug, department_slug, team_slug, project_slug, personal_slug, content_json, size_bytes, checksum ) VALUES ( $1, $2, $3, $4, $5::posix_node_kind, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18::jsonb, $19, $20 ); `, node.Path, node.ParentPath, node.Name, node.Depth, string(node.NodeKind), node.LogicalType, node.FileRole, node.ResourceID, node.ResourceName, node.ResourceSlug, node.InstallationID, node.OrganizationID, node.OrganizationSlug, node.DepartmentSlug, node.TeamSlug, node.ProjectSlug, node.PersonalSlug, node.ContentJSON, node.SizeBytes, node.Checksum, ); err != nil { return RebuildSummary{}, fmt.Errorf("insert posix node %s: %w", node.Path, err) } } if err := tx.Commit(ctx); err != nil { return RebuildSummary{}, err } return summary, nil }