Files
terdut-server/internal/models/user.go
T
Niklas Ye b114251f45 Add notification channels and a provider abstraction
Users get a notification_channels table (replacing users.ntfy_topic); pages fan
out to every enabled channel through a notify.Provider registry, with ntfy as
the first provider. Fallbacks are typed {type, address}. Schema edited in place:
existing databases must be recreated.

Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
2026-10-12 21:00:40 +02:00

64 lines
2.5 KiB
Go

package models
import "time"
type User struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
// Channels are where this user's notifications go. Empty means they get none
// of their own; incidents assigned to them fall back to the configured
// fallback target instead. Only filled in for the user themselves and for an
// administrator, and the address only for the user themselves.
Channels []NotificationChannel `json:"channels,omitempty"`
// DisabledAt is when the account was taken out of use, or nil. A disabled
// user cannot authenticate by either credential, and keeps their name on
// every acknowledgement and timeline entry they made.
DisabledAt *time.Time `json:"disabled_at,omitempty"`
// IsAdmin is the system administrator flag: managing users and API keys.
// Not omitempty — a client has to be able to tell "false" from "this server
// is too old to have the field", and the web UI decides what to show from
// it.
IsAdmin bool `json:"is_admin"`
// AdminSource is who granted the flag: "manual" or "oidc". An "oidc"
// administrator follows the identity provider's groups, so the UI shows it as
// managed there and the API refuses to revoke it by hand. Only set on the
// user endpoints that show it.
AdminSource string `json:"admin_source,omitempty"`
}
// NotificationChannel is one way of reaching a user: a provider type and an
// address that provider understands.
type NotificationChannel struct {
ID int64 `json:"id"`
Type string `json:"type"`
// Address is an ntfy topic, later an email address. Blank unless the caller is
// the owner: it can be a secret, and an administrator has no need of it.
Address string `json:"address,omitempty"`
Enabled bool `json:"enabled"`
CreatedAt time.Time `json:"created_at"`
}
type APIKey struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
// ExpiresAt is nil for a key that never expires, which is every key
// created before this field existed and still the default for a new one
// unless its creator asks otherwise (see handleCreateAPIKey's
// expires_in_days). AuthMiddleware stops accepting a key once this
// passes; nothing deletes the row for it.
ExpiresAt *time.Time `json:"expires_at,omitempty"`
Key string `json:"key,omitempty"` // populated only on creation, never stored
}