Refactor: improve backend modularity
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
// Path: Backend/internal/posixproj/projector_scan.go
|
||||
|
||||
package posixproj
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ScanRoot(root string) ([]Node, error) {
|
||||
rootPath := strings.TrimSpace(root)
|
||||
if rootPath == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
info, err := os.Stat(rootPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("stat POSIX root: %w", err)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return nil, fmt.Errorf("POSIX root is not a directory: %s", rootPath)
|
||||
}
|
||||
|
||||
rootScope, err := loadRootScope(rootPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodes := []Node{{
|
||||
Path: rootProjectionPath,
|
||||
ParentPath: nil,
|
||||
Name: filepath.Base(rootPath),
|
||||
Depth: 0,
|
||||
NodeKind: NodeKindDirectory,
|
||||
LogicalType: "tenant_root",
|
||||
InstallationID: rootScope.InstallationID,
|
||||
OrganizationID: rootScope.OrganizationID,
|
||||
OrganizationSlug: rootScope.OrganizationSlug,
|
||||
}}
|
||||
|
||||
err = filepath.WalkDir(rootPath, func(path string, entry fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
if path == rootPath {
|
||||
return nil
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(rootPath, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
relPath = filepath.ToSlash(relPath)
|
||||
if relPath == "." {
|
||||
return nil
|
||||
}
|
||||
|
||||
node, err := buildNode(rootPath, relPath, entry, rootScope)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nodes = append(nodes, node)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("scan POSIX root: %w", err)
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func loadRootScope(rootPath string) (Scope, error) {
|
||||
settingsPath := filepath.Join(rootPath, "settings.json")
|
||||
content, err := os.ReadFile(settingsPath)
|
||||
if err != nil {
|
||||
if errorsIsNotExist(err) {
|
||||
return Scope{}, nil
|
||||
}
|
||||
return Scope{}, fmt.Errorf("read root settings.json: %w", err)
|
||||
}
|
||||
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal(content, &payload); err != nil {
|
||||
return Scope{}, fmt.Errorf("decode root settings.json: %w", err)
|
||||
}
|
||||
|
||||
installation, _ := payload["installation"].(map[string]any)
|
||||
organization, _ := payload["organization"].(map[string]any)
|
||||
|
||||
return Scope{
|
||||
InstallationID: stringValue(installation["id"]),
|
||||
OrganizationID: stringValue(organization["id"]),
|
||||
OrganizationSlug: stringValue(organization["slug"]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildNode(rootPath, relPath string, entry fs.DirEntry, rootScope Scope) (Node, error) {
|
||||
scope := deriveScope(relPath, rootScope)
|
||||
parentPath := projectionParentPath(relPath)
|
||||
logicalType, fileRole := classifyPath(relPath, entry.IsDir())
|
||||
|
||||
node := Node{
|
||||
Path: relPath,
|
||||
ParentPath: parentPath,
|
||||
Name: entry.Name(),
|
||||
Depth: strings.Count(relPath, "/") + 1,
|
||||
NodeKind: NodeKindDirectory,
|
||||
LogicalType: logicalType,
|
||||
FileRole: fileRole,
|
||||
InstallationID: scope.InstallationID,
|
||||
OrganizationID: scope.OrganizationID,
|
||||
OrganizationSlug: scope.OrganizationSlug,
|
||||
DepartmentSlug: scope.DepartmentSlug,
|
||||
TeamSlug: scope.TeamSlug,
|
||||
ProjectSlug: scope.ProjectSlug,
|
||||
PersonalSlug: scope.PersonalSlug,
|
||||
}
|
||||
|
||||
if entry.IsDir() {
|
||||
return node, nil
|
||||
}
|
||||
|
||||
absPath := filepath.Join(rootPath, filepath.FromSlash(relPath))
|
||||
content, err := os.ReadFile(absPath)
|
||||
if err != nil {
|
||||
return Node{}, fmt.Errorf("read POSIX file %s: %w", relPath, err)
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(content)
|
||||
node.NodeKind = NodeKindFile
|
||||
node.SizeBytes = int64(len(content))
|
||||
node.Checksum = hex.EncodeToString(hash[:])
|
||||
|
||||
if strings.EqualFold(filepath.Ext(entry.Name()), ".json") {
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal(content, &payload); err == nil {
|
||||
jsonContent, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return Node{}, fmt.Errorf("remarshal POSIX file %s: %w", relPath, err)
|
||||
}
|
||||
node.ContentJSON = jsonContent
|
||||
node.ResourceID = stringValue(payload["id"])
|
||||
node.ResourceName = stringValue(payload["name"])
|
||||
node.ResourceSlug = stringValue(payload["slug"])
|
||||
if node.ResourceID == "" && fileRole == "settings" && logicalType == "tenant" {
|
||||
installation, _ := payload["installation"].(map[string]any)
|
||||
organization, _ := payload["organization"].(map[string]any)
|
||||
node.ResourceID = stringValue(installation["id"])
|
||||
node.ResourceName = stringValue(installation["name"])
|
||||
node.InstallationID = stringValue(installation["id"])
|
||||
node.OrganizationID = stringValue(organization["id"])
|
||||
node.OrganizationSlug = firstNonEmpty(node.OrganizationSlug, stringValue(organization["slug"]))
|
||||
}
|
||||
if node.ResourceID == "" && fileRole == "users" {
|
||||
node.ResourceName = firstNonEmpty(node.ResourceName, parentEntityName(logicalType, scope))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if node.ResourceSlug == "" {
|
||||
node.ResourceSlug = inferredResourceSlug(logicalType, scope)
|
||||
}
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func summarizeNodes(nodes []Node) RebuildSummary {
|
||||
summary := RebuildSummary{TotalNodes: len(nodes)}
|
||||
for _, node := range nodes {
|
||||
switch node.NodeKind {
|
||||
case NodeKindDirectory:
|
||||
summary.DirectoryCount++
|
||||
case NodeKindFile:
|
||||
summary.FileCount++
|
||||
}
|
||||
}
|
||||
return summary
|
||||
}
|
||||
|
||||
func errorsIsNotExist(err error) bool {
|
||||
return err != nil && os.IsNotExist(err)
|
||||
}
|
||||
Reference in New Issue
Block a user