1f77473d8f
Proxy / base-URL: - ORCHESTRAD_TRUSTED_PROXIES now defaults to "local", trusting reverse proxies in loopback + RFC1918 + link-local/ULA ranges out of the box, so X-Forwarded-* (client IP, scheme, host) is honored behind an edge proxy without extra config. New keywords: local/private, all/any, none. - OIDC redirect URI derivation now uses the trust-gated request base URL instead of reading X-Forwarded-Proto directly, and audit client IP now trusts the middleware-rewritten RemoteAddr rather than the raw (spoofable) X-Forwarded-For header. Both honor forwarded values only from trusted peers. Schedules: - Seed eight built-in schedules on startup (every 5/15/30 min, hourly, every 6/12h, daily, weekly), idempotent by name, so operators have ready-made cadences in the Schedules page and the rule editor's schedule dropdown without hand-building one. Test covers the trusted-proxy keyword expansion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
81 lines
2.7 KiB
Go
81 lines
2.7 KiB
Go
// Package services - built-in schedule seeding
|
|
package services
|
|
|
|
import (
|
|
"database/sql"
|
|
|
|
"github.com/Grace-Solutions/OrchestrAD/internal/logging"
|
|
"github.com/Grace-Solutions/OrchestrAD/internal/models"
|
|
"github.com/Grace-Solutions/OrchestrAD/internal/repository"
|
|
)
|
|
|
|
// defaultSchedule describes one built-in schedule offered out of the box.
|
|
type defaultSchedule struct {
|
|
name string
|
|
kind string // "Easy" or "Cron"
|
|
interval int // for Easy
|
|
unit string // for Easy: Minutes/Hours/Days
|
|
cron string // for Cron (6-field, UTC)
|
|
desc string
|
|
}
|
|
|
|
var builtinSchedules = []defaultSchedule{
|
|
{name: "Every 5 minutes", kind: "Easy", interval: 5, unit: "Minutes", desc: "Built-in"},
|
|
{name: "Every 15 minutes", kind: "Easy", interval: 15, unit: "Minutes", desc: "Built-in"},
|
|
{name: "Every 30 minutes", kind: "Easy", interval: 30, unit: "Minutes", desc: "Built-in"},
|
|
{name: "Hourly", kind: "Easy", interval: 1, unit: "Hours", desc: "Built-in"},
|
|
{name: "Every 6 hours", kind: "Easy", interval: 6, unit: "Hours", desc: "Built-in"},
|
|
{name: "Every 12 hours", kind: "Easy", interval: 12, unit: "Hours", desc: "Built-in"},
|
|
{name: "Daily (00:00 UTC)", kind: "Cron", cron: "0 0 0 * * *", desc: "Built-in — every day at midnight UTC"},
|
|
{name: "Weekly (Sun 00:00 UTC)", kind: "Cron", cron: "0 0 0 * * 0", desc: "Built-in — every Sunday at midnight UTC"},
|
|
}
|
|
|
|
// EnsureDefaultSchedules idempotently seeds the built-in schedules so operators
|
|
// have ready-made cadences (in the Schedules page and the rule editor) without
|
|
// hand-building one. Existing schedules with the same name are left untouched;
|
|
// deleting a built-in schedule will not resurrect it within the same run but it
|
|
// reappears on next startup unless renamed.
|
|
func EnsureDefaultSchedules(db *sql.DB, logger *logging.Logger) {
|
|
repo := repository.NewScheduleRepository(db)
|
|
existing, _, err := repo.List(0, 500)
|
|
if err != nil {
|
|
logger.Warn("Schedules", "Could not list schedules for seeding: %v", err)
|
|
return
|
|
}
|
|
have := make(map[string]bool, len(existing))
|
|
for _, s := range existing {
|
|
have[s.Name] = true
|
|
}
|
|
|
|
seeded := 0
|
|
for _, d := range builtinSchedules {
|
|
if have[d.name] {
|
|
continue
|
|
}
|
|
desc := d.desc
|
|
sched := &models.Schedule{
|
|
Name: d.name,
|
|
Description: &desc,
|
|
IsEnabled: true,
|
|
ScheduleKind: d.kind,
|
|
TimezoneMode: "UTC",
|
|
}
|
|
if d.kind == "Easy" {
|
|
v, u := d.interval, d.unit
|
|
sched.EasyIntervalValue = &v
|
|
sched.EasyIntervalUnit = &u
|
|
} else {
|
|
c := d.cron
|
|
sched.CronExpression = &c
|
|
}
|
|
if err := repo.Create(sched); err != nil {
|
|
logger.Warn("Schedules", "Failed to seed built-in schedule %q: %v", d.name, err)
|
|
continue
|
|
}
|
|
seeded++
|
|
}
|
|
if seeded > 0 {
|
|
logger.Info("Schedules", "Seeded %d built-in schedule(s)", seeded)
|
|
}
|
|
}
|