Panel — public source drop (v0.9.0)
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>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user