Checkpoint: password reset, rule expiry, migrations

- Self-service and admin password-change endpoints
  (PUT /v1/auth/password, PUT /v1/accounts/{id}/password)
- Policy rule time-scoped expiry (not_before / expires_at)
  with migration 000006 and engine filtering
- golang-migrate integration; embedded SQL migrations
- PolicyRecord fieldalignment lint fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 14:38:38 -07:00
parent d7b69ed983
commit 22158824bd
25 changed files with 1574 additions and 137 deletions

View File

@@ -190,6 +190,7 @@ func New(database *db.DB, cfg *config.Config, priv ed25519.PrivateKey, pub ed255
"templates/fragments/tags_editor.html",
"templates/fragments/policy_row.html",
"templates/fragments/policy_form.html",
"templates/fragments/password_reset_form.html",
}
base, err := template.New("").Funcs(funcMap).ParseFS(web.TemplateFS, sharedFiles...)
if err != nil {
@@ -293,6 +294,7 @@ func (u *UIServer) Register(mux *http.ServeMux) {
uiMux.Handle("PATCH /policies/{id}/enabled", admin(u.handleTogglePolicyRule))
uiMux.Handle("DELETE /policies/{id}", admin(u.handleDeletePolicyRule))
uiMux.Handle("PUT /accounts/{id}/tags", admin(u.handleSetAccountTags))
uiMux.Handle("PUT /accounts/{id}/password", admin(u.handleAdminResetPassword))
// Mount the wrapped UI mux on the parent mux. The "/" pattern acts as a
// catch-all for all UI paths; the more-specific /v1/ API patterns registered
@@ -593,9 +595,13 @@ type PolicyRuleView struct {
RuleJSON string
CreatedAt string
UpdatedAt string
NotBefore string // empty if not set
ExpiresAt string // empty if not set
ID int64
Priority int
Enabled bool
IsExpired bool // true if expires_at is in the past
IsPending bool // true if not_before is in the future
}
// PoliciesData is the view model for the policies list page.