Files
glchat/internal/server/fallback.go
T
grendervill f61751ed26 feat(api): REST на chi + huma с auth-ручками и OpenAPI 3.1
- переход на chi + huma (решение D-006): huma отдаёт типизированные ручки и
  генерирует документ, auth-ручки живут на chi (cookie и заголовки напрямую)
- единый формат ошибок: код (auth.invalid_credentials, auth.2fa_required,
  perm.denied и т.д.) + человекочитаемое сообщение (AGENT.md 8.5)
- cookie сессии __Host-session: HttpOnly, SameSite=Lax, Secure при TLS;
  альтернатива — Bearer-токен для desktop/CLI (AGENT.md 8.1)
- ручки: register, login, logout, logout-all, sessions, step-up, 2fa/setup,
  2fa/enable, users/@me; IP и User-Agent прокидываются из запроса в контекст
- /api/v1/openapi.json: объединённый документ (схемы huma + контракт auth)
- тесты: регистрация через API с cookie, ошибки входа, обязательная сессия,
  валидация, наличие всех путей в OpenAPI
2026-09-19 21:36:00 +03:00

34 lines
886 B
Go

package server
import (
"net/http"
"strings"
"glchat/internal/httpx"
)
// reservedPrefixes are handled by the API, the gateway or the file CDN; an
// unknown path under them is a real 404 and must not receive the SPA shell.
var reservedPrefixes = []string{"/api/", "/gateway", "/files/", "/rtc"}
func isReservedPath(path string) bool {
for _, prefix := range reservedPrefixes {
if path == strings.TrimSuffix(prefix, "/") || strings.HasPrefix(path, prefix) {
return true
}
}
return false
}
func (s *Server) handleFallback(w http.ResponseWriter, r *http.Request) {
if isReservedPath(r.URL.Path) {
httpx.WriteError(w, httpx.NewError(httpx.CodeNotFound, "resource not found"))
return
}
if r.Method == http.MethodGet || r.Method == http.MethodHead {
s.static.serve(w, r)
return
}
httpx.WriteError(w, httpx.NewError(httpx.CodeNotFound, "resource not found"))
}