package grouptasks import ( "encoding/json" "fmt" "janex/admin-dashboard-backend/modules/cache" "janex/admin-dashboard-backend/modules/database" "janex/admin-dashboard-backend/modules/structs" "janex/admin-dashboard-backend/modules/utils" "janex/admin-dashboard-backend/socketclients" "os" "os/exec" "time" "github.com/google/uuid" "github.com/rs/zerolog/log" ) var root = "./groupTasks/groups/" func ReadGroups() { entries, err := os.ReadDir(root) if err != nil { log.Error().Msg("Failed to read groups directory, error: " + err.Error()) return } for _, entry := range entries { log.Info().Msgf("Entry: %s", entry.Name()) files, err := os.ReadDir(root + entry.Name()) if err != nil { log.Error().Msg("Failed to read groups directory files, error: " + err.Error()) return } for _, file := range files { log.Info().Msgf("File: %s", file.Name()) if file.Name() == "index.json" { content, err := os.ReadFile(root + entry.Name() + "/index.json") if err != nil { log.Error().Msg("Failed to read file content, error: " + err.Error()) return } var group structs.Group json.Unmarshal(content, &group) group.Id = entry.Name() log.Info().Msgf("Group: %s", group) cache.AddCategoryGroup(group) } } } } const ( RunGroupTaskStartTypeNormal = 0 RunGroupTaskStartTypeTryAgain = 1 ) type RunGroupTaskArgs struct { StartType uint8 GroupTaskId string Category string GroupId string Step uint8 TaskStepId string GlobalInputs string TaskParameters string } type InputParameters struct { ParameterName string `json:"parameterName"` Value string `json:"value"` } func RunGroupTask(args RunGroupTaskArgs) { log.Debug().Msgf("global input: %v", args.GlobalInputs) categoryGroup := GetCategoryGroupTaskByCategoryAndGroupId(args.Category, args.GroupId) log.Debug().Msgf("RunGroupTask %s", categoryGroup) log.Debug().Msgf("script path %s", root+categoryGroup.Id+"/"+categoryGroup.Tasks[args.Step-1].ScriptPath) groupTaskStep := structs.GroupTaskSteps{ GroupTasksId: args.GroupTaskId, Step: args.Step, Status: structs.GroupTasksStatusRunning, StartedAt: time.Now(), } // task type if args.StartType == RunGroupTaskStartTypeNormal { groupTaskStep.Id = uuid.New().String() database.DB.Create(&groupTaskStep) socketclients.BroadcastMessage(structs.SendSocketMessage{ Cmd: utils.SentCmdNewGroupTaskStep, Body: groupTaskStep, }) } else if args.StartType == RunGroupTaskStartTypeTryAgain { groupTaskStep.Id = args.TaskStepId updateGroupTaskSteps(groupTaskStep) } // set group task to running dbGroupTask := updateGroupTask(groupTaskStep.GroupTasksId, structs.GroupTasks{ Status: structs.GroupTasksStatusRunning, }) // global inputs var globalInputParameters []InputParameters if len(args.GlobalInputs) > 0 { // global inputs given in args because the group task was just created log.Info().Msgf("global inputs given %s", args.GlobalInputs) if err := json.Unmarshal([]byte(args.GlobalInputs), &globalInputParameters); err != nil { log.Error().Msgf("err unmarshalling global inputs %s", err.Error()) } } else { // global inputs not given in args - fetch it from the database log.Info().Msgf("global inputs not given in args %s", dbGroupTask.GlobalInputs) if err := json.Unmarshal([]byte(dbGroupTask.GlobalInputs), &globalInputParameters); err != nil { log.Error().Msgf("err unmarshalling global inputs %s", err.Error()) } } // task parameters log.Info().Msgf("unmarshalled global inputs %s", globalInputParameters) log.Info().Msgf("task parameters %s", categoryGroup.Tasks[args.Step-1].Parameters) commandArgs := []string{root + categoryGroup.Id + "/" + categoryGroup.Tasks[args.Step-1].ScriptPath} if len(categoryGroup.Tasks[args.Step-1].Parameters) != 0 && len(args.TaskParameters) == 0 { log.Error().Msg("task parameters not specified") updateGroupTask(groupTaskStep.GroupTasksId, structs.GroupTasks{ Status: structs.GroupTasksStatusInputRequired, }) groupTaskStep.Status = structs.GroupTasksStatusInputRequired updateGroupTaskSteps(groupTaskStep) return } else { var taskParameterInputs []InputParameters if err := json.Unmarshal([]byte(args.TaskParameters), &taskParameterInputs); err != nil { log.Error().Msgf("err unmarshalling task parameter inputs %s", err.Error()) } log.Info().Msgf("task parameters %s", taskParameterInputs) for _, taskParameterInput := range taskParameterInputs { //commandArgs = append(commandArgs, "--"+taskParameterInput.ParameterName) commandArgs = append(commandArgs, taskParameterInput.Value) } log.Info().Msgf("task parameters %s", commandArgs) } // execute script cmd, err := exec.Command("python3", commandArgs...).Output() cmdLog := string(cmd) if err != nil { if exitErr, ok := err.(*exec.ExitError); ok { exitCode := exitErr.ExitCode() log.Error().Msgf("exit code %d", exitCode) cmdLog += fmt.Sprintf("\nExit code: %v", exitCode) } log.Error().Msgf("error exec command %s", err.Error()) groupTaskStep.Status = structs.GroupTasksStatusFailed } else { groupTaskStep.Status = structs.GroupTasksStatusFinished } fmt.Println(cmdLog) groupTaskStep.Log = cmdLog groupTaskStep.EndedAt = time.Now() updateGroupTaskSteps(groupTaskStep) log.Info().Msgf("run next task") if int(args.Step) < len(categoryGroup.Tasks) { if groupTaskStep.Status == structs.GroupTasksStatusFailed { // set group task to failed updateGroupTask(groupTaskStep.GroupTasksId, structs.GroupTasks{ Status: structs.GroupTasksStatusFailed, }) } else { args.StartType = RunGroupTaskStartTypeNormal args.Step = args.Step + 1 updateGroupTask(groupTaskStep.GroupTasksId, structs.GroupTasks{ CurrentTasksStep: args.Step, }) log.Debug().Msgf("RUN NEXT TASK %s", groupTaskStep) // clear task parameters, because otherwise the next task would have the parameters from the previous task args.TaskParameters = "" RunGroupTask(args) } } else { // set group task to finished updateGroupTask(groupTaskStep.GroupTasksId, structs.GroupTasks{ Status: structs.GroupTasksStatusFinished, EndedAt: time.Now(), }) log.Info().Msg("SET TO FINISHED") } } // Updates group task and send it to websocket users func updateGroupTask(groupTasksId string, updates structs.GroupTasks) structs.GroupTasks { database.DB.Model(&structs.GroupTasks{}).Where("id = ?", groupTasksId).Updates(updates) var dbGroupTask structs.GroupTasks database.DB.First(&dbGroupTask, "id = ?", groupTasksId) socketclients.BroadcastMessage(structs.SendSocketMessage{ Cmd: utils.SentCmdUpdateGroupTask, Body: dbGroupTask, }) return dbGroupTask } // Updates group task steps and send it to websocket users func updateGroupTaskSteps(groupTaskStep structs.GroupTaskSteps) { database.DB.Model(&structs.GroupTaskSteps{}).Where("id = ?", groupTaskStep.Id).Updates(groupTaskStep) socketclients.BroadcastMessage(structs.SendSocketMessage{ Cmd: utils.SentCmdUpdateGroupTaskStep, Body: groupTaskStep, }) } func GetAllGroupTasks() []structs.GroupTasks { var groupTasks []structs.GroupTasks database.DB.Find(&groupTasks) return groupTasks } func GetAllGroupTasksSteps() []structs.GroupTaskSteps { var groupTaskStepsLogs []structs.GroupTaskSteps database.DB.Find(&groupTaskStepsLogs) return groupTaskStepsLogs } func GetCategoryGroupTaskByCategoryAndGroupId(category string, groupId string) structs.Group { for _, categoryGroup := range cache.GetCategoryGroups() { if categoryGroup.Category == category { for _, group := range categoryGroup.Groups { if group.Id == groupId { return group } } } } return structs.Group{} }