Espaces personnels et favoris : API par utilisateur, /api/me.id, tests

- Chaque route de schémas passe par la vue de l'utilisateur connecté
  (storage.Space) : arborescence commune, .perso et .favoris.
- /api/public résout aussi les liens des espaces personnels.
- /api/me renvoie id (dossier de l'utilisateur : email OIDC, sinon « local »).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0174sFqehj4uGqJHQZvWy1gn
This commit is contained in:
claude BotandClaude Opus 5.5 committed 2026-10-08 10:26:31 +02:00
1 parent 781fc8f5f5
commit 824871bd24
5 files changed
+514 -29

No files matched your search

+20 -2
View File
@@ -90,7 +90,8 @@ func main() {
}
mux := http.NewServeMux()
(&api.Handler{Store: store, MaxBody: maxMB << 20, Update: checker}).Register(mux)
spaces := storage.NewSpaces(store)
(&api.Handler{Store: store, Spaces: spaces, User: userID, MaxBody: maxMB << 20, Update: checker}).Register(mux)
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("ok"))
})
@@ -168,11 +169,28 @@ func authenticate(ctx context.Context, mux *http.ServeMux, dataDir string) (http
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "no-store")
_ = json.NewEncoder(w).Encode(m)
// id : identifiant de l'utilisateur, qui nomme son espace personnel.
_ = json.NewEncoder(w).Encode(struct {
auth.Me
ID string `json:"id"`
}{m, userID(r)})
})
return h, nil
}
// userID identifie l'utilisateur d'une requête pour son espace personnel et
// ses favoris : email OIDC (sinon sub), utilisateur unique storage.LocalUser
// en authentification locale ou sans authentification.
func userID(r *http.Request) string {
if u := auth.UserFrom(r.Context()); u != nil {
if u.Email != "" {
return storage.UserID(u.Email)
}
return storage.UserID("sub-" + u.Sub)
}
return storage.LocalUser
}
// oidcConfig lit les variables OIDC_* et SESSION_*.
func oidcConfig(dataDir string) (auth.Config, error) {
cfg := auth.Config{