Seed household settings at boot and serve the settings API
Port of Feature 04 task 3 (7629cab) onto Feature 04 + Feature 03.
On first boot create the single household_settings row with the zone from
MEALQUEST_DEFAULT_TZ and the seed times and days (dinner 17:30, reminder
16:30, rotation check 09:00, plan draft Fri 09:00, suggestions Sun 08:00,
chat autosend Sat 08:00). An existing row is never overwritten.
Add household.SettingsService with Get, a validating Update (IANA zone,
zero-padded 24-hour HH:MM, day of week 0-6) that records updated_by, and
in-process Subscribe for the scheduler. Serve it as GET and PATCH
/api/v1/settings/household; invalid input is a 400 in the standard error
body. Migration 0002 adds household_settings.updated_by. Link time/tzdata
from internal/household so zones load in the minimal image.
Review fixes:
- F1: the settings routes are mounted on the protected /api/v1 subrouter,
so they need a session (RequireSession) and, for PATCH, the CSRF token.
updated_by is the session actor's subject from auth.ActorFrom; the
duplicate internal/actor package is dropped, and Update refuses an empty
member instead of storing NULL. A test drives PATCH through the
production router with a real SessionManager session.
- F2: config no longer validates MEALQUEST_DEFAULT_TZ. Seeding requires a
valid zone only when it creates the row (ErrNoDefaultZone,
ErrInvalidDefaultZone, both naming the variable at boot), so a reboot
over existing settings ignores a missing or invalid default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package household
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
// Embeds the IANA zone database in the binary, so zones load the same
|
||||
// way in a minimal image with no /usr/share/zoneinfo.
|
||||
_ "time/tzdata"
|
||||
)
|
||||
|
||||
// LoadZone loads the IANA zone name, such as "America/New_York". The empty
|
||||
// name and "Local", which time.LoadLocation maps to UTC and the process zone,
|
||||
// are refused: the household zone is never implied.
|
||||
func LoadZone(name string) (*time.Location, error) {
|
||||
if name == "" {
|
||||
return nil, errors.New("time zone is empty (want an IANA zone such as America/New_York)")
|
||||
}
|
||||
if name == "Local" {
|
||||
return nil, errors.New(`time zone "Local" is not an IANA zone (want e.g. America/New_York)`)
|
||||
}
|
||||
loc, err := time.LoadLocation(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unknown time zone %q (want an IANA zone such as America/New_York)", name)
|
||||
}
|
||||
return loc, nil
|
||||
}
|
||||
Reference in New Issue
Block a user