This commit is contained in:
@@ -3,66 +3,170 @@ 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.ScriptInterpreterRepo
|
||||
Repo *repository.Repository
|
||||
InterpreterRepo *repository.ScriptInterpreterRepo
|
||||
}
|
||||
|
||||
func NewScriptService(repo *repository.ScriptInterpreterRepo) *ScriptService {
|
||||
return &ScriptService{repo: repo}
|
||||
// NewScriptService creates a new ScriptService with both script and interpreter repos.
|
||||
func NewScriptService(repo *repository.Repository) *ScriptService {
|
||||
return &ScriptService{Repo: repo}
|
||||
}
|
||||
|
||||
// ResolveCommand builds the full argv[] by prepending the interpreter's argv
|
||||
// to the script text (as the last argument).
|
||||
func (self *ScriptService) ResolveCommand(
|
||||
ctx context.Context,
|
||||
interpreterID int64,
|
||||
scriptText string,
|
||||
) ([]string, error) {
|
||||
interpreter, err := self.repo.GetByID(ctx, interpreterID)
|
||||
// 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
|
||||
}
|
||||
|
||||
if len(interpreter.Argv) == 0 {
|
||||
return nil, fmt.Errorf("interpreter %q has empty argv", interpreter.Name)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
argv := make([]string, len(interpreter.Argv)+1)
|
||||
copy(argv, interpreter.Argv)
|
||||
argv[len(argv)-1] = scriptText
|
||||
return argv, nil
|
||||
return buildTreeSlice(root), nil
|
||||
}
|
||||
|
||||
func (self *ScriptService) Create(
|
||||
ctx context.Context,
|
||||
in repository.ScriptInterpreterCreate,
|
||||
) (*repository.ScriptInterpreter, error) {
|
||||
return self.repo.Create(ctx, in)
|
||||
// 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
|
||||
}
|
||||
|
||||
func (self *ScriptService) GetByID(
|
||||
ctx context.Context,
|
||||
id int64,
|
||||
) (*repository.ScriptInterpreter, error) {
|
||||
return self.repo.GetByID(ctx, id)
|
||||
// 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
|
||||
}
|
||||
|
||||
func (self *ScriptService) List(ctx context.Context) ([]repository.ScriptInterpreter, error) {
|
||||
return self.repo.List(ctx)
|
||||
// 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
|
||||
}
|
||||
|
||||
func (self *ScriptService) Update(
|
||||
ctx context.Context,
|
||||
id int64,
|
||||
in repository.ScriptInterpreterUpdate,
|
||||
) (*repository.ScriptInterpreter, error) {
|
||||
return self.repo.Update(ctx, id, in)
|
||||
// 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)
|
||||
}
|
||||
|
||||
func (self *ScriptService) Delete(ctx context.Context, id int64) error {
|
||||
return self.repo.Delete(ctx, id)
|
||||
// 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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user