This commit is contained in:
ponzischeme89
2026-08-17 07:34:23 +12:00
parent 93fb0fd728
commit 36cb1324fd
56 changed files with 1177 additions and 168 deletions
+98
View File
@@ -15,6 +15,9 @@ import (
// MaintenanceKey is the app_settings row backing maintenance mode.
const MaintenanceKey = "maintenance"
// QuietTimeKey is the app_settings row backing the daily server quiet-time window.
const QuietTimeKey = "quiet_time"
// RequestPolicyKey controls which Emby users may send titles to Sonarr/Radarr.
const RequestPolicyKey = "request_policy"
@@ -742,6 +745,101 @@ func (s *Store) SetMaintenance(ctx context.Context, state Maintenance) error {
return nil
}
// QuietTime is a daily window in the household timezone during which Memby's data plane
// and background work stand down. The admin control plane and health checks remain live so
// an operator can change a bad schedule without restarting the container.
type QuietTime struct {
Enabled bool `json:"enabled"`
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
Message string `json:"message"`
UpdatedAt time.Time `json:"updatedAt"`
}
const DefaultQuietTimeMessage = "Memby is in quiet time. Try again later."
func DefaultQuietTime() QuietTime {
return QuietTime{StartTime: "23:00", EndTime: "07:00", Message: DefaultQuietTimeMessage}
}
// QuietTimeActive reports whether now falls in the configured local-clock window. A
// window crossing midnight includes late evening and the following morning. Equal or
// malformed endpoints are treated as inactive; the admin handler refuses both.
func QuietTimeActive(policy QuietTime, now time.Time, location *time.Location) bool {
if !policy.Enabled {
return false
}
start, startErr := time.Parse("15:04", policy.StartTime)
end, endErr := time.Parse("15:04", policy.EndTime)
if startErr != nil || endErr != nil || policy.StartTime == policy.EndTime {
return false
}
if location == nil {
location = time.UTC
}
local := now.In(location)
minute := local.Hour()*60 + local.Minute()
startMinute := start.Hour()*60 + start.Minute()
endMinute := end.Hour()*60 + end.Minute()
if startMinute < endMinute {
return minute >= startMinute && minute < endMinute
}
return minute >= startMinute || minute < endMinute
}
func normaliseQuietTime(policy QuietTime) QuietTime {
defaults := DefaultQuietTime()
if parsed, err := time.Parse("15:04", strings.TrimSpace(policy.StartTime)); err == nil {
policy.StartTime = parsed.Format("15:04")
} else {
policy.StartTime = defaults.StartTime
}
if parsed, err := time.Parse("15:04", strings.TrimSpace(policy.EndTime)); err == nil {
policy.EndTime = parsed.Format("15:04")
} else {
policy.EndTime = defaults.EndTime
}
policy.Message = strings.TrimSpace(policy.Message)
if policy.Message == "" {
policy.Message = DefaultQuietTimeMessage
}
return policy
}
func (s *Store) QuietTime(ctx context.Context) (QuietTime, error) {
var raw []byte
err := s.pool.QueryRow(ctx, `SELECT value FROM app_settings WHERE key = $1`, QuietTimeKey).Scan(&raw)
if errors.Is(err, pgx.ErrNoRows) {
return DefaultQuietTime(), nil
}
if err != nil {
return DefaultQuietTime(), fmt.Errorf("store: read quiet time: %w", err)
}
var policy QuietTime
if err := json.Unmarshal(raw, &policy); err != nil {
return DefaultQuietTime(), fmt.Errorf("store: decode quiet time: %w", err)
}
return normaliseQuietTime(policy), nil
}
func (s *Store) SetQuietTime(ctx context.Context, policy QuietTime) error {
policy = normaliseQuietTime(policy)
policy.UpdatedAt = time.Now().UTC()
raw, err := json.Marshal(policy)
if err != nil {
return err
}
_, err = s.pool.Exec(ctx, `
INSERT INTO app_settings (key, value, updated_at)
VALUES ($1, $2::jsonb, now())
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now()`,
QuietTimeKey, string(raw))
if err != nil {
return fmt.Errorf("store: write quiet time: %w", err)
}
return nil
}
// UpdatePolicyKey is the app_settings row backing the client update policy.
const UpdatePolicyKey = "update_policy"