Backend:
- New GET/PUT /api/me endpoint to read and update the authenticated
user's profile (currently display_name). UserStore.UpdateDisplayName.
- Wired into the authed router.
App:
- New SettingsScreen + SettingsViewModel with five sections:
* Account & Sync: login status, email, server URL, logout, connect.
* Profile: edit display name (pushed to PUT /api/me when logged in).
* Appearance: System / Light / Dark theme switch, persisted in
SessionManager and applied via MitbringslTheme(sessionManager).
* Data: 'Reset local data' wipes Room tables (server data kept).
* About: version, 'Developed by Janik Dietz', and credits to
GLM-5.2 + Gemini 3.6 Flash.
- Theme.kt now reads the user's theme preference (StateFlow) instead
of only the system default; MainActivity passes SessionManager in.
- Navigation: new SettingsNavKey; settings gear icon in ListsScreen
top bar; Settings links back to the Sync/Account screen.
- DTOs/API: UpdateMeRequestDto + getMe()/updateMe() for /api/me.
108 lines
3.7 KiB
Go
108 lines
3.7 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"github.com/mitbringsl/backend/internal/auth"
|
|
"github.com/mitbringsl/backend/internal/config"
|
|
"github.com/mitbringsl/backend/internal/store"
|
|
)
|
|
|
|
// API bundles all handler groups and wires the router.
|
|
type API struct {
|
|
cfg *config.Config
|
|
pool *pgxpool.Pool
|
|
|
|
health *HealthHandler
|
|
auth *AuthHandler
|
|
lists *ListHandler
|
|
ops *OpsHandler
|
|
suggest *SuggestHandler
|
|
config *ConfigHandler
|
|
me *MeHandler
|
|
}
|
|
|
|
// NewAPI constructs the API with all handler groups.
|
|
func NewAPI(cfg *config.Config, pool *pgxpool.Pool) *API {
|
|
// Auth stores
|
|
users := auth.NewUserStore(pool)
|
|
sessions := auth.NewSessionStore(pool, auth.SessionConfig{
|
|
TokenBytes: cfg.SessionTokenBytes,
|
|
TTL: cfg.SessionTokenTTL,
|
|
})
|
|
oidcSvc := auth.NewOIDCService(buildProviders(cfg))
|
|
if len(oidcSvc.Providers()) == 0 {
|
|
oidcSvc = nil
|
|
}
|
|
|
|
// Sync stores
|
|
listStore := store.NewListStore(pool)
|
|
itemStore := store.NewItemStore(pool)
|
|
opStore := store.NewOpStore(pool)
|
|
suggestStore := store.NewSuggestStore(pool)
|
|
|
|
return &API{
|
|
cfg: cfg,
|
|
pool: pool,
|
|
health: &HealthHandler{Pool: pool},
|
|
auth: NewAuthHandler(users, sessions, oidcSvc, cfg),
|
|
lists: NewListHandler(listStore, itemStore),
|
|
ops: NewOpsHandler(opStore, listStore),
|
|
suggest: NewSuggestHandler(suggestStore),
|
|
config: NewConfigHandler(cfg),
|
|
me: NewMeHandler(users),
|
|
}
|
|
}
|
|
|
|
// buildProviders assembles the OIDC provider map from config. Only enabled
|
|
// providers with a non-empty issuer and client_id are included.
|
|
func buildProviders(cfg *config.Config) map[string]auth.ProviderConfig {
|
|
p := map[string]auth.ProviderConfig{}
|
|
if cfg.GoogleOIDC.Enabled && cfg.GoogleOIDC.Issuer != "" && cfg.GoogleOIDC.ClientID != "" {
|
|
p["google"] = auth.ProviderConfig{Issuer: cfg.GoogleOIDC.Issuer, ClientID: cfg.GoogleOIDC.ClientID}
|
|
}
|
|
if cfg.GenericOIDC.Enabled && cfg.GenericOIDC.Issuer != "" && cfg.GenericOIDC.ClientID != "" {
|
|
p["generic"] = auth.ProviderConfig{Issuer: cfg.GenericOIDC.Issuer, ClientID: cfg.GenericOIDC.ClientID}
|
|
}
|
|
return p
|
|
}
|
|
|
|
// Handler returns the fully wired http.Handler with all middleware applied.
|
|
func (a *API) Handler() http.Handler {
|
|
mux := http.NewServeMux()
|
|
|
|
// --- public health endpoints (no auth) ---
|
|
mux.HandleFunc("GET /healthz", a.health.Healthz)
|
|
mux.HandleFunc("GET /readyz", a.health.Readyz)
|
|
|
|
// --- public server config (auth capabilities, no auth required) ---
|
|
mux.HandleFunc("GET /api/config", a.config.Config)
|
|
|
|
// --- auth endpoints ---
|
|
mux.HandleFunc("POST /auth/register", a.auth.Register)
|
|
mux.HandleFunc("POST /auth/login", a.auth.Login)
|
|
mux.HandleFunc("POST /auth/oidc", a.auth.OIDC)
|
|
mux.HandleFunc("POST /auth/logout", a.auth.Logout)
|
|
|
|
// --- authenticated API endpoints ---
|
|
mux.Handle("GET /api/me", a.RequireAuth(http.HandlerFunc(a.me.Get)))
|
|
mux.Handle("PUT /api/me", a.RequireAuth(http.HandlerFunc(a.me.Update)))
|
|
mux.Handle("GET /api/lists", a.RequireAuth(http.HandlerFunc(a.lists.List)))
|
|
mux.Handle("POST /api/lists", a.RequireAuth(http.HandlerFunc(a.lists.Create)))
|
|
mux.Handle("POST /api/lists/join", a.RequireAuth(http.HandlerFunc(a.lists.Join)))
|
|
mux.Handle("GET /api/lists/{id}", a.RequireAuth(http.HandlerFunc(a.lists.Get)))
|
|
mux.Handle("POST /api/lists/{id}/invite", a.RequireAuth(http.HandlerFunc(a.lists.Invite)))
|
|
mux.Handle("POST /api/lists/{id}/ops", a.RequireAuth(http.HandlerFunc(a.ops.Push)))
|
|
mux.Handle("GET /api/lists/{id}/ops", a.RequireAuth(http.HandlerFunc(a.ops.Pull)))
|
|
mux.Handle("GET /api/suggestions", a.RequireAuth(http.HandlerFunc(a.suggest.Search)))
|
|
|
|
return Chain(
|
|
mux,
|
|
requestIDMiddleware,
|
|
loggingMiddleware,
|
|
recoverMiddleware,
|
|
corsMiddleware(a.cfg),
|
|
)
|
|
}
|