326 lines
8.2 KiB
Go
326 lines
8.2 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cynfo/rmm-backend/models"
|
|
)
|
|
|
|
func (d *Database) CreateTask(agentID, action, scheduledAt, webhookURL, createdBy string) (*models.Task, error) {
|
|
// Validate action
|
|
validActions := map[string]bool{"backup": true, "update": true}
|
|
if !validActions[action] {
|
|
return nil, fmt.Errorf("ungueltige Aktion: %s", action)
|
|
}
|
|
|
|
var schedTime time.Time
|
|
var err error
|
|
if scheduledAt == "" {
|
|
schedTime = time.Now()
|
|
} else {
|
|
schedTime, err = time.Parse(time.RFC3339, scheduledAt)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ungueltiges Datum: %w", err)
|
|
}
|
|
}
|
|
|
|
var task models.Task
|
|
err = d.db.QueryRow(`
|
|
INSERT INTO tasks (agent_id, action, scheduled_at, webhook_url, created_by)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING id, agent_id, action, status, scheduled_at, webhook_url, COALESCE(created_by, ''), created_at
|
|
`, agentID, action, schedTime, webhookURL, createdBy).Scan(
|
|
&task.ID, &task.AgentID, &task.Action, &task.Status,
|
|
&schedTime, &task.WebhookURL, &task.CreatedBy, &schedTime,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Task erstellen: %w", err)
|
|
}
|
|
|
|
// Re-read cleanly
|
|
return d.GetTask(task.ID)
|
|
}
|
|
|
|
func (d *Database) GetTask(id int) (*models.Task, error) {
|
|
var t models.Task
|
|
var scheduledAt, createdAt time.Time
|
|
var startedAt, finishedAt sql.NullTime
|
|
var resultJSON sql.NullString
|
|
var webhookURL, webhookResponse, createdBy sql.NullString
|
|
var agentName, customerName sql.NullString
|
|
|
|
err := d.db.QueryRow(`
|
|
SELECT t.id, t.agent_id, COALESCE(a.name, ''), COALESCE(c.name, ''),
|
|
t.action, t.status, t.scheduled_at, t.started_at, t.finished_at,
|
|
t.result, t.webhook_url, t.webhook_sent, t.webhook_response,
|
|
t.created_by, t.created_at
|
|
FROM tasks t
|
|
LEFT JOIN agents a ON a.id = t.agent_id
|
|
LEFT JOIN customers c ON c.id = a.customer_id
|
|
WHERE t.id = $1
|
|
`, id).Scan(
|
|
&t.ID, &t.AgentID, &agentName, &customerName,
|
|
&t.Action, &t.Status, &scheduledAt, &startedAt, &finishedAt,
|
|
&resultJSON, &webhookURL, &t.WebhookSent, &webhookResponse,
|
|
&createdBy, &createdAt,
|
|
)
|
|
if err == sql.ErrNoRows {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
t.AgentName = agentName.String
|
|
t.CustomerName = customerName.String
|
|
t.ScheduledAt = scheduledAt.Format(time.RFC3339)
|
|
t.CreatedAt = createdAt.Format(time.RFC3339)
|
|
if startedAt.Valid {
|
|
s := startedAt.Time.Format(time.RFC3339)
|
|
t.StartedAt = &s
|
|
}
|
|
if finishedAt.Valid {
|
|
s := finishedAt.Time.Format(time.RFC3339)
|
|
t.FinishedAt = &s
|
|
}
|
|
if resultJSON.Valid {
|
|
var r interface{}
|
|
json.Unmarshal([]byte(resultJSON.String), &r)
|
|
t.Result = r
|
|
}
|
|
if webhookURL.Valid {
|
|
t.WebhookURL = webhookURL.String
|
|
}
|
|
if webhookResponse.Valid {
|
|
t.WebhookResponse = webhookResponse.String
|
|
}
|
|
if createdBy.Valid {
|
|
t.CreatedBy = createdBy.String
|
|
}
|
|
|
|
return &t, nil
|
|
}
|
|
|
|
func (d *Database) GetTasks(limit, offset int, agentID, status, action string) ([]models.Task, int, error) {
|
|
if limit <= 0 {
|
|
limit = 50
|
|
}
|
|
|
|
where := []string{}
|
|
args := []interface{}{}
|
|
argIdx := 1
|
|
|
|
if agentID != "" {
|
|
where = append(where, fmt.Sprintf("t.agent_id = $%d", argIdx))
|
|
args = append(args, agentID)
|
|
argIdx++
|
|
}
|
|
if status != "" {
|
|
where = append(where, fmt.Sprintf("t.status = $%d", argIdx))
|
|
args = append(args, status)
|
|
argIdx++
|
|
}
|
|
if action != "" {
|
|
where = append(where, fmt.Sprintf("t.action = $%d", argIdx))
|
|
args = append(args, action)
|
|
argIdx++
|
|
}
|
|
|
|
whereClause := ""
|
|
if len(where) > 0 {
|
|
whereClause = "WHERE " + strings.Join(where, " AND ")
|
|
}
|
|
|
|
// Count
|
|
var total int
|
|
countQuery := "SELECT COUNT(*) FROM tasks t " + whereClause
|
|
if err := d.db.QueryRow(countQuery, args...).Scan(&total); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
// Data
|
|
query := fmt.Sprintf(`
|
|
SELECT t.id, t.agent_id, COALESCE(a.name, ''), COALESCE(c.name, ''),
|
|
t.action, t.status, t.scheduled_at, t.started_at, t.finished_at,
|
|
t.result, t.webhook_url, t.webhook_sent, t.webhook_response,
|
|
t.created_by, t.created_at
|
|
FROM tasks t
|
|
LEFT JOIN agents a ON a.id = t.agent_id
|
|
LEFT JOIN customers c ON c.id = a.customer_id
|
|
%s ORDER BY t.created_at DESC LIMIT $%d OFFSET $%d
|
|
`, whereClause, argIdx, argIdx+1)
|
|
args = append(args, limit, offset)
|
|
|
|
rows, err := d.db.Query(query, args...)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var tasks []models.Task
|
|
for rows.Next() {
|
|
var t models.Task
|
|
var scheduledAt, createdAt time.Time
|
|
var startedAt, finishedAt sql.NullTime
|
|
var resultJSON sql.NullString
|
|
var webhookURL, webhookResponse, createdBy sql.NullString
|
|
var agentName, customerName sql.NullString
|
|
|
|
if err := rows.Scan(
|
|
&t.ID, &t.AgentID, &agentName, &customerName,
|
|
&t.Action, &t.Status, &scheduledAt, &startedAt, &finishedAt,
|
|
&resultJSON, &webhookURL, &t.WebhookSent, &webhookResponse,
|
|
&createdBy, &createdAt,
|
|
); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
t.AgentName = agentName.String
|
|
t.CustomerName = customerName.String
|
|
t.ScheduledAt = scheduledAt.Format(time.RFC3339)
|
|
t.CreatedAt = createdAt.Format(time.RFC3339)
|
|
if startedAt.Valid {
|
|
s := startedAt.Time.Format(time.RFC3339)
|
|
t.StartedAt = &s
|
|
}
|
|
if finishedAt.Valid {
|
|
s := finishedAt.Time.Format(time.RFC3339)
|
|
t.FinishedAt = &s
|
|
}
|
|
if resultJSON.Valid {
|
|
var r interface{}
|
|
json.Unmarshal([]byte(resultJSON.String), &r)
|
|
t.Result = r
|
|
}
|
|
if webhookURL.Valid {
|
|
t.WebhookURL = webhookURL.String
|
|
}
|
|
if webhookResponse.Valid {
|
|
t.WebhookResponse = webhookResponse.String
|
|
}
|
|
if createdBy.Valid {
|
|
t.CreatedBy = createdBy.String
|
|
}
|
|
|
|
tasks = append(tasks, t)
|
|
}
|
|
if tasks == nil {
|
|
tasks = []models.Task{}
|
|
}
|
|
return tasks, total, rows.Err()
|
|
}
|
|
|
|
func (d *Database) UpdateTaskStatus(id int, status string, result interface{}) error {
|
|
var resultJSON *string
|
|
if result != nil {
|
|
b, err := json.Marshal(result)
|
|
if err == nil {
|
|
s := string(b)
|
|
resultJSON = &s
|
|
}
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
|
|
switch status {
|
|
case "running":
|
|
_, err := d.db.Exec(
|
|
"UPDATE tasks SET status = $1, started_at = $2 WHERE id = $3",
|
|
status, now, id,
|
|
)
|
|
return err
|
|
case "completed", "failed":
|
|
_, err := d.db.Exec(
|
|
"UPDATE tasks SET status = $1, finished_at = $2, result = $3 WHERE id = $4",
|
|
status, now, resultJSON, id,
|
|
)
|
|
return err
|
|
default:
|
|
_, err := d.db.Exec("UPDATE tasks SET status = $1 WHERE id = $2", status, id)
|
|
return err
|
|
}
|
|
}
|
|
|
|
func (d *Database) SetTaskWebhookResult(id int, sent bool, response string) error {
|
|
// Truncate response to 500 chars
|
|
if len(response) > 500 {
|
|
response = response[:500]
|
|
}
|
|
_, err := d.db.Exec(
|
|
"UPDATE tasks SET webhook_sent = $1, webhook_response = $2 WHERE id = $3",
|
|
sent, response, id,
|
|
)
|
|
return err
|
|
}
|
|
|
|
func (d *Database) GetDueTasks() ([]models.Task, error) {
|
|
rows, err := d.db.Query(`
|
|
SELECT t.id, t.agent_id, COALESCE(a.name, ''), COALESCE(c.name, ''),
|
|
t.action, t.status, t.scheduled_at, t.webhook_url, t.created_at
|
|
FROM tasks t
|
|
LEFT JOIN agents a ON a.id = t.agent_id
|
|
LEFT JOIN customers c ON c.id = a.customer_id
|
|
WHERE t.status = 'scheduled' AND t.scheduled_at <= NOW()
|
|
ORDER BY t.scheduled_at ASC
|
|
`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var tasks []models.Task
|
|
for rows.Next() {
|
|
var t models.Task
|
|
var scheduledAt, createdAt time.Time
|
|
var webhookURL sql.NullString
|
|
var agentName, customerName sql.NullString
|
|
|
|
if err := rows.Scan(
|
|
&t.ID, &t.AgentID, &agentName, &customerName,
|
|
&t.Action, &t.Status, &scheduledAt, &webhookURL, &createdAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
t.AgentName = agentName.String
|
|
t.CustomerName = customerName.String
|
|
t.ScheduledAt = scheduledAt.Format(time.RFC3339)
|
|
t.CreatedAt = createdAt.Format(time.RFC3339)
|
|
if webhookURL.Valid {
|
|
t.WebhookURL = webhookURL.String
|
|
}
|
|
tasks = append(tasks, t)
|
|
}
|
|
return tasks, rows.Err()
|
|
}
|
|
|
|
func (d *Database) CancelTask(id int) error {
|
|
res, err := d.db.Exec(
|
|
"UPDATE tasks SET status = 'cancelled' WHERE id = $1 AND status = 'scheduled'",
|
|
id,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rows, _ := res.RowsAffected()
|
|
if rows == 0 {
|
|
return fmt.Errorf("Task nicht gefunden oder nicht mehr im Status 'scheduled'")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Database) DeleteTask(id int) error {
|
|
res, err := d.db.Exec("DELETE FROM tasks WHERE id = $1", id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rows, _ := res.RowsAffected()
|
|
if rows == 0 {
|
|
return fmt.Errorf("Task nicht gefunden")
|
|
}
|
|
return nil
|
|
}
|