a00bd620a1
Self-hostable game-server control panel: controller + agent + 26 game modules. One-line install (prebuilt release, no Go required): curl -fsSL https://git.pdxtechs.com/dbledeez/panel/raw/branch/main/install.sh | sudo bash Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
140 lines
4.3 KiB
Go
140 lines
4.3 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
// ScheduleRow is the persisted form of a schedule.
|
|
type ScheduleRow struct {
|
|
ID string
|
|
InstanceID string
|
|
Description string
|
|
TriggerKind string // "cron" | "event"
|
|
CronSpec string // populated for TriggerKind == "cron"
|
|
EventSpec string // populated for TriggerKind == "event" — CEL expression returning bool
|
|
EventSustainedSeconds int32 // for event: fire only after condition true this many seconds
|
|
Action json.RawMessage
|
|
Enabled bool
|
|
LastFiredAt *time.Time
|
|
LastStatus string
|
|
LastDetail string
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
func (db *DB) InsertSchedule(ctx context.Context, r ScheduleRow) error {
|
|
var cronSpec, eventSpec *string
|
|
if r.CronSpec != "" {
|
|
cronSpec = &r.CronSpec
|
|
}
|
|
if r.EventSpec != "" {
|
|
eventSpec = &r.EventSpec
|
|
}
|
|
_, err := db.pool.Exec(ctx, `
|
|
INSERT INTO schedules (id, instance_id, description, trigger_kind, cron_spec, event_spec, event_sustained_seconds, action, enabled)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
`, r.ID, r.InstanceID, r.Description, r.TriggerKind, cronSpec, eventSpec, r.EventSustainedSeconds, []byte(r.Action), r.Enabled)
|
|
if err != nil {
|
|
return fmt.Errorf("insert schedule: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (db *DB) DeleteSchedule(ctx context.Context, id string) error {
|
|
_, err := db.pool.Exec(ctx, `DELETE FROM schedules WHERE id = $1`, id)
|
|
if err != nil {
|
|
return fmt.Errorf("delete schedule: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (db *DB) SetScheduleEnabled(ctx context.Context, id string, enabled bool) error {
|
|
_, err := db.pool.Exec(ctx, `UPDATE schedules SET enabled = $2, updated_at = NOW() WHERE id = $1`, id, enabled)
|
|
if err != nil {
|
|
return fmt.Errorf("set schedule enabled: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UpdateSchedule persists changes to an existing schedule row.
|
|
func (db *DB) UpdateSchedule(ctx context.Context, r ScheduleRow) error {
|
|
_, err := db.pool.Exec(ctx, `
|
|
UPDATE schedules
|
|
SET description = $2,
|
|
trigger_kind = $3,
|
|
cron_spec = $4,
|
|
event_spec = $5,
|
|
event_sustained_seconds = $6,
|
|
action = $7,
|
|
enabled = $8,
|
|
updated_at = NOW()
|
|
WHERE id = $1`,
|
|
r.ID, r.Description, r.TriggerKind, r.CronSpec, r.EventSpec, r.EventSustainedSeconds, []byte(r.Action), r.Enabled)
|
|
if err != nil {
|
|
return fmt.Errorf("update schedule: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UpdateScheduleFireResult records the outcome of a trigger fire — used by
|
|
// the engine to surface success/failure in the UI.
|
|
func (db *DB) UpdateScheduleFireResult(ctx context.Context, id, status, detail string) error {
|
|
_, err := db.pool.Exec(ctx, `
|
|
UPDATE schedules
|
|
SET last_fired_at = NOW(), last_status = $2, last_detail = $3, updated_at = NOW()
|
|
WHERE id = $1
|
|
`, id, status, detail)
|
|
if err != nil {
|
|
return fmt.Errorf("update schedule result: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ListSchedules returns every schedule, optionally filtered by instance_id.
|
|
func (db *DB) ListSchedules(ctx context.Context, instanceFilter string) ([]ScheduleRow, error) {
|
|
const selectAll = `
|
|
SELECT id, instance_id, description, trigger_kind,
|
|
COALESCE(cron_spec, ''), COALESCE(event_spec, ''), event_sustained_seconds,
|
|
action,
|
|
enabled, last_fired_at, COALESCE(last_status, ''), COALESCE(last_detail, ''),
|
|
created_at, updated_at
|
|
FROM schedules
|
|
`
|
|
var rows pgx.Rows
|
|
var err error
|
|
if instanceFilter != "" {
|
|
rows, err = db.pool.Query(ctx, selectAll+` WHERE instance_id = $1 ORDER BY id`, instanceFilter)
|
|
} else {
|
|
rows, err = db.pool.Query(ctx, selectAll+` ORDER BY instance_id, id`)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list schedules: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []ScheduleRow
|
|
for rows.Next() {
|
|
var r ScheduleRow
|
|
var action []byte
|
|
var lastFired *time.Time
|
|
if err := rows.Scan(
|
|
&r.ID, &r.InstanceID, &r.Description, &r.TriggerKind,
|
|
&r.CronSpec, &r.EventSpec, &r.EventSustainedSeconds,
|
|
&action,
|
|
&r.Enabled, &lastFired, &r.LastStatus, &r.LastDetail,
|
|
&r.CreatedAt, &r.UpdatedAt,
|
|
); err != nil {
|
|
return nil, fmt.Errorf("scan schedule: %w", err)
|
|
}
|
|
r.Action = action
|
|
r.LastFiredAt = lastFired
|
|
out = append(out, r)
|
|
}
|
|
return out, rows.Err()
|
|
}
|