Files
HellreigN/backend/internal/service/script_service.go
T
d3m0k1d b516a54c17
ci-agent / build (push) Failing after 2m42s
fixsess and logic for web ide
2026-04-04 23:56:28 +03:00

173 lines
5.0 KiB
Go

package service
import (
"context"
"fmt"
"sort"
"strings"
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/repository"
)
// ScriptService handles script CRUD, tree building, and interpreter resolution.
type ScriptService struct {
Repo *repository.Repository
InterpreterRepo *repository.ScriptInterpreterRepo
}
// NewScriptService creates a new ScriptService with both script and interpreter repos.
func NewScriptService(repo *repository.Repository) *ScriptService {
return &ScriptService{Repo: repo}
}
// NewScriptServiceWithInterpreters creates a ScriptService with interpreter support.
func NewScriptServiceWithInterpreters(repo *repository.Repository, interpRepo *repository.ScriptInterpreterRepo) *ScriptService {
return &ScriptService{Repo: repo, InterpreterRepo: interpRepo}
}
// treeNode is an internal representation for building the tree.
type treeNode struct {
name string
typ string // "folder" or "file"
children map[string]*treeNode
// File-specific fields
id *int64
content *string
interpreterID *int64
}
// BuildTree builds a directory tree from all scripts in the database.
// Each script path is treated as a file path (e.g. "deploy/nginx/restart.sh").
func (s *ScriptService) BuildTree() ([]repository.ScriptTreeNode, error) {
scripts, err := s.Repo.ListScripts()
if err != nil {
return nil, err
}
root := make(map[string]*treeNode)
for _, sc := range scripts {
parts := strings.Split(sc.Path, "/")
// Walk through path parts, creating folders as needed
currentMap := root
for i, part := range parts {
isFile := i == len(parts)-1
if _, exists := currentMap[part]; !exists {
node := &treeNode{
name: part,
children: make(map[string]*treeNode),
}
if isFile {
node.typ = "file"
id := sc.ID
content := sc.Content
interpreterID := sc.InterpreterID
node.id = &id
node.content = &content
node.interpreterID = &interpreterID
} else {
node.typ = "folder"
}
currentMap[part] = node
}
currentMap = currentMap[part].children
}
}
return buildTreeSlice(root), nil
}
// buildTreeSlice converts a map of treeNodes to a sorted slice of ScriptTreeNode.
func buildTreeSlice(m map[string]*treeNode) []repository.ScriptTreeNode {
result := make([]repository.ScriptTreeNode, 0, len(m))
for _, node := range m {
result = append(result, toScriptTreeNode(node))
}
// Sort: folders first, then files, alphabetically within each group
sort.Slice(result, func(i, j int) bool {
if result[i].Type != result[j].Type {
return result[i].Type == "folder"
}
return result[i].Name < result[j].Name
})
return result
}
// toScriptTreeNode converts a treeNode to a ScriptTreeNode with recursively converted children.
func toScriptTreeNode(node *treeNode) repository.ScriptTreeNode {
result := repository.ScriptTreeNode{
Name: node.name,
Type: node.typ,
Children: []repository.ScriptTreeNode{},
}
if node.typ == "file" {
result.ID = node.id
result.Content = node.content
result.InterpreterID = node.interpreterID
} else {
result.Children = buildTreeSlice(node.children)
}
return result
}
// ResolveCommand resolves the full command for a script using its interpreter.
func (s *ScriptService) ResolveCommand(ctx context.Context, interpreterID int64, scriptText string) ([]string, error) {
if s.InterpreterRepo == nil {
return nil, fmt.Errorf("interpreter repo not configured")
}
interpreter, err := s.InterpreterRepo.GetByID(ctx, interpreterID)
if err != nil {
return nil, fmt.Errorf("get interpreter: %w", err)
}
// Build command: argv[0] argv[1] ... -c scriptText
cmd := append(interpreter.Argv, "-c", scriptText)
return cmd, nil
}
// List returns all interpreters.
func (s *ScriptService) List(ctx context.Context) ([]repository.ScriptInterpreter, error) {
if s.InterpreterRepo == nil {
return nil, fmt.Errorf("interpreter repo not configured")
}
return s.InterpreterRepo.List(ctx)
}
// Create creates a new interpreter.
func (s *ScriptService) Create(ctx context.Context, in repository.ScriptInterpreterCreate) (*repository.ScriptInterpreter, error) {
if s.InterpreterRepo == nil {
return nil, fmt.Errorf("interpreter repo not configured")
}
return s.InterpreterRepo.Create(ctx, in)
}
// GetByID returns an interpreter by ID.
func (s *ScriptService) GetByID(ctx context.Context, id int64) (*repository.ScriptInterpreter, error) {
if s.InterpreterRepo == nil {
return nil, fmt.Errorf("interpreter repo not configured")
}
return s.InterpreterRepo.GetByID(ctx, id)
}
// Update updates an interpreter.
func (s *ScriptService) Update(ctx context.Context, id int64, in repository.ScriptInterpreterUpdate) (*repository.ScriptInterpreter, error) {
if s.InterpreterRepo == nil {
return nil, fmt.Errorf("interpreter repo not configured")
}
return s.InterpreterRepo.Update(ctx, id, in)
}
// Delete deletes an interpreter.
func (s *ScriptService) Delete(ctx context.Context, id int64) error {
if s.InterpreterRepo == nil {
return fmt.Errorf("interpreter repo not configured")
}
return s.InterpreterRepo.Delete(ctx, id)
}