173 lines
5.0 KiB
Go
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)
|
|
}
|