Signed-in users get their preferences (hidden feeds, weather location, weather toggle) stored server-side keyed by their OIDC subject and synced across devices. Anonymous visitors keep using browser localStorage, so the site stays public. First sign-in migrates existing localStorage prefs up. - config: [web.auth] section (issuer, client_id/secret, redirect, session_secret) - storage: user_preferences table + Get/PutUserPrefs - web/auth: OIDC code flow, HMAC-signed session cookie, CSRF state + nonce - web/prefs_api: GET/PUT /api/preferences (auth-gated, 64KB cap) - frontend: prefs.js sync layer seeds localStorage from server, pushes on write - header: sign-in / account control OIDC discovery is non-fatal at boot: if Authentik is down, Pete serves anonymously rather than refusing to start.
51 lines
1.5 KiB
Go
51 lines
1.5 KiB
Go
package storage
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// UserPrefs is one signed-in user's stored state. Prefs is an opaque JSON blob
|
|
// owned by the web frontend (disabled feeds, weather location, toggles, …) —
|
|
// the backend just persists and returns it, keyed by the OIDC subject.
|
|
type UserPrefs struct {
|
|
Sub string
|
|
Prefs string
|
|
Username string
|
|
Email string
|
|
UpdatedAt int64
|
|
}
|
|
|
|
// GetUserPrefs returns the stored prefs blob for an OIDC subject. It returns
|
|
// ("", nil) when the user has no record yet (first sign-in).
|
|
func GetUserPrefs(sub string) (string, error) {
|
|
var prefs string
|
|
err := Get().QueryRow(`SELECT prefs FROM user_preferences WHERE user_sub = ?`, sub).Scan(&prefs)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return "", nil
|
|
}
|
|
if err != nil {
|
|
return "", fmt.Errorf("get user prefs: %w", err)
|
|
}
|
|
return prefs, nil
|
|
}
|
|
|
|
// PutUserPrefs upserts a user's prefs blob along with identity hints (username,
|
|
// email) for later features. The prefs string is stored verbatim.
|
|
func PutUserPrefs(sub, prefs, username, email string) error {
|
|
_, err := Get().Exec(`
|
|
INSERT INTO user_preferences (user_sub, prefs, username, email, updated_at)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
ON CONFLICT(user_sub) DO UPDATE SET
|
|
prefs = excluded.prefs,
|
|
username = excluded.username,
|
|
email = excluded.email,
|
|
updated_at = excluded.updated_at`,
|
|
sub, prefs, username, email, nowUnix())
|
|
if err != nil {
|
|
return fmt.Errorf("put user prefs: %w", err)
|
|
}
|
|
return nil
|
|
}
|