Phase 6: schedules, blocklists, allowlists (editors + effective state)
- Weekly schedule editor per device: a Monday-Sunday grid of blocking windows, including windows that cross midnight. - Blocklist/allowlist editor per device: app (Android package / Windows process) and site (hostname) entries, tagged by the device platform. - Effective desired state: a manual lock, an active schedule window, or neither. While blocked the allowlist is honoured (Spotify stays while other apps are blocked). Published retained with reason schedule/manual. - Boundary-aware republish loop so a window turns on and off on time, and manual lock expiry is cleared promptly. - CONTROL_TIMEZONE (default Australia/Sydney) with embedded tzdata, so wall-clock windows are evaluated in the family zone, not the container. - Every change is written to the append-only audit log (FR-15). Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -11,6 +11,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
// Embed the IANA time zone database so CONTROL_TIMEZONE resolves even in a
|
||||
// container without tzdata, such as the test image.
|
||||
_ "time/tzdata"
|
||||
)
|
||||
|
||||
// Config is the fully resolved runtime configuration.
|
||||
@@ -37,6 +40,11 @@ type Config struct {
|
||||
AppEnv string
|
||||
// Version is injected at build time.
|
||||
Version string
|
||||
// ScheduleLocation is the timezone weekly schedule windows are evaluated in
|
||||
// (CONTROL_TIMEZONE). Windows are wall-clock, so this must be the family's
|
||||
// zone, not the container's default of UTC. It defaults to
|
||||
// DefaultTimezone.
|
||||
ScheduleLocation *time.Location
|
||||
|
||||
// MQTTEnabled turns the broker link on. When false the service runs
|
||||
// HTTP-only and never touches the broker.
|
||||
@@ -102,6 +110,12 @@ func Load() (*Config, error) {
|
||||
}
|
||||
cfg.LogLevel = lvl
|
||||
|
||||
loc, err := time.LoadLocation(env("CONTROL_TIMEZONE", DefaultTimezone))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("CONTROL_TIMEZONE: %w", err)
|
||||
}
|
||||
cfg.ScheduleLocation = loc
|
||||
|
||||
if err := cfg.validateMQTT(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -114,6 +128,10 @@ func Load() (*Config, error) {
|
||||
// to the host gateway.
|
||||
const DefaultMQTTBroker = "tcp://host.docker.internal:1883"
|
||||
|
||||
// DefaultTimezone is the schedule timezone used when CONTROL_TIMEZONE is unset.
|
||||
// It matches the lab host's configured zone; set CONTROL_TIMEZONE to override.
|
||||
const DefaultTimezone = "Australia/Sydney"
|
||||
|
||||
// MQTTTLS reports whether the broker URL asks for TLS.
|
||||
func (c *Config) MQTTTLS() bool {
|
||||
return strings.HasPrefix(c.MQTTBroker, "tls://") || strings.HasPrefix(c.MQTTBroker, "ssl://")
|
||||
|
||||
Reference in New Issue
Block a user