Files
Aether/backend/internal/documents/handler.go
T
HoneyandClaude Opus 5 31a027c93a feat(aether): initial commit – self-hosted Cloud, Phasen 1–7
Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA.
Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb,
Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming,
Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office
(Collabora/OnlyOffice).

Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort)
sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 09:25:36 +02:00

87 lines
2.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package documents
import (
"net/http"
"github.com/google/uuid"
"aether/pkg/response"
)
// Handler zeigt den Stand der Texterkennung.
//
// Ohne diese Auskunft ist nicht zu erkennen, ob der Volltext funktioniert:
// Eine Suche, die nichts findet, sieht genauso aus wie eine Suche in einem
// Bestand, der noch gar nicht erkannt wurde. Nach dem ersten Start läuft der
// Arbeiter je nach Menge Stunden – ohne Zähler weiß niemand, ob er
// vorankommt.
type Handler struct {
store *Store
// isAdmin und userID trennen die Rechteprüfung von diesem Paket.
// documents kennt weder Sessions noch Rollen, und soll es auch nicht.
isAdmin func(r *http.Request) bool
userID func(r *http.Request) uuid.UUID
}
func NewHandler(store *Store, isAdmin func(r *http.Request) bool, userID func(r *http.Request) uuid.UUID) *Handler {
return &Handler{store: store, isAdmin: isAdmin, userID: userID}
}
// Status beantwortet GET /api/v1/admin/ocr – der Gesamtstand des Servers.
func (h *Handler) Status(w http.ResponseWriter, r *http.Request) {
if !h.isAdmin(r) {
response.Error(w, http.StatusForbidden, "admin required")
return
}
// h.store nil heißt: OCR ist abgeschaltet oder die Programme fehlen. Das
// ist eine Auskunft, kein Fehler – die Oberfläche soll „aus" anzeigen
// können, statt in einen Fehlerzustand zu laufen.
if h == nil || h.store == nil {
response.JSON(w, http.StatusOK, map[string]any{"enabled": false})
return
}
stats, err := h.store.Stats(r.Context())
if err != nil {
response.Error(w, http.StatusInternalServerError, "ocr status failed")
return
}
response.JSON(w, http.StatusOK, map[string]any{
"enabled": true,
"pending": stats.Pending,
"running": stats.Running,
"done": stats.Done,
"failed": stats.Failed,
"indexed": stats.Indexed,
})
}
// MyStatus beantwortet GET /api/v1/ocr – der Stand der **eigenen** Dateien.
//
// Ohne diesen Endpunkt wüsste nur der Administrator, ob ein Dokument schon
// gelesen wurde. Das ist die falsche Person: Wissen muss es, wer die Rechnung
// hochgeladen hat und sie gerade nicht findet.
func (h *Handler) MyStatus(w http.ResponseWriter, r *http.Request) {
uid := h.userID(r)
if uid == uuid.Nil {
response.Error(w, http.StatusUnauthorized, "not signed in")
return
}
if h == nil || h.store == nil {
response.JSON(w, http.StatusOK, map[string]any{"enabled": false})
return
}
stats, err := h.store.StatsForUser(r.Context(), uid)
if err != nil {
response.Error(w, http.StatusInternalServerError, "ocr status failed")
return
}
response.JSON(w, http.StatusOK, map[string]any{
"enabled": true,
"pending": stats.Pending,
"running": stats.Running,
"done": stats.Done,
"failed": stats.Failed,
"indexed": stats.Indexed,
})
}