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>
132 lines
3.8 KiB
Go
132 lines
3.8 KiB
Go
package files
|
||
|
||
import (
|
||
"encoding/json"
|
||
"errors"
|
||
_ "image/gif"
|
||
_ "image/jpeg"
|
||
_ "image/png"
|
||
"net/http"
|
||
|
||
"github.com/google/uuid"
|
||
|
||
"aether/internal/auth"
|
||
"aether/pkg/response"
|
||
)
|
||
|
||
// Favoriten der Seitenleiste: anlegen, entfernen, sortieren, auf- und
|
||
// zuklappen. Der Zustand liegt serverseitig, damit er auf jedem Gerät gleich ist.
|
||
|
||
// GET /api/v1/favorites
|
||
func (h *Handler) ListFavorites(w http.ResponseWriter, r *http.Request) {
|
||
user := auth.UserFromContext(r.Context())
|
||
favs, err := h.store.ListFavorites(r.Context(), user.ID)
|
||
if err != nil {
|
||
response.Error(w, http.StatusInternalServerError, "database error")
|
||
return
|
||
}
|
||
if favs == nil {
|
||
favs = []*Favorite{}
|
||
}
|
||
response.JSON(w, http.StatusOK, favs)
|
||
}
|
||
|
||
// POST /api/v1/favorites
|
||
func (h *Handler) AddFavorite(w http.ResponseWriter, r *http.Request) {
|
||
user := auth.UserFromContext(r.Context())
|
||
var body struct {
|
||
FolderID string `json:"folder_id"`
|
||
}
|
||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.FolderID == "" {
|
||
response.Error(w, http.StatusBadRequest, "folder_id required")
|
||
return
|
||
}
|
||
folderID, err := uuid.Parse(body.FolderID)
|
||
if err != nil {
|
||
response.Error(w, http.StatusBadRequest, "invalid folder_id")
|
||
return
|
||
}
|
||
// Lesezugriff genügt: Ein Favorit ist ein Lesezeichen, keine Änderung.
|
||
// Die frühere Besitzprüfung schloss Gruppenordner und geteilte Ordner aus –
|
||
// sie gehören dem Anfragenden nicht, sind aber genau die, die man sich
|
||
// merken will.
|
||
if _, ok := h.resolveFolder(w, r, &folderID, false); !ok {
|
||
return
|
||
}
|
||
fav, err := h.store.AddFavorite(r.Context(), user.ID, folderID)
|
||
if err != nil {
|
||
response.Error(w, http.StatusInternalServerError, "database error")
|
||
return
|
||
}
|
||
response.JSON(w, http.StatusCreated, fav)
|
||
}
|
||
|
||
// DELETE /api/v1/favorites/{folderId}
|
||
func (h *Handler) RemoveFavorite(w http.ResponseWriter, r *http.Request) {
|
||
user := auth.UserFromContext(r.Context())
|
||
folderID, err := parseID(r, "folderId")
|
||
if err != nil {
|
||
response.Error(w, http.StatusBadRequest, "invalid folder id")
|
||
return
|
||
}
|
||
if err := h.store.RemoveFavorite(r.Context(), user.ID, folderID); errors.Is(err, ErrNotFound) {
|
||
response.Error(w, http.StatusNotFound, "favorite not found")
|
||
return
|
||
} else if err != nil {
|
||
response.Error(w, http.StatusInternalServerError, "database error")
|
||
return
|
||
}
|
||
response.NoContent(w)
|
||
}
|
||
|
||
// PATCH /api/v1/favorites/{folderId}/expand
|
||
func (h *Handler) SetFavoriteExpanded(w http.ResponseWriter, r *http.Request) {
|
||
user := auth.UserFromContext(r.Context())
|
||
folderID, err := parseID(r, "folderId")
|
||
if err != nil {
|
||
response.Error(w, http.StatusBadRequest, "invalid folder id")
|
||
return
|
||
}
|
||
var body struct {
|
||
Expanded bool `json:"expanded"`
|
||
}
|
||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||
response.Error(w, http.StatusBadRequest, "invalid body")
|
||
return
|
||
}
|
||
if err := h.store.SetFavoriteExpanded(r.Context(), user.ID, folderID, body.Expanded); errors.Is(err, ErrNotFound) {
|
||
response.Error(w, http.StatusNotFound, "favorite not found")
|
||
return
|
||
} else if err != nil {
|
||
response.Error(w, http.StatusInternalServerError, "database error")
|
||
return
|
||
}
|
||
response.NoContent(w)
|
||
}
|
||
|
||
// PATCH /api/v1/favorites/reorder
|
||
func (h *Handler) ReorderFavorites(w http.ResponseWriter, r *http.Request) {
|
||
user := auth.UserFromContext(r.Context())
|
||
var body struct {
|
||
FolderIDs []string `json:"folder_ids"`
|
||
}
|
||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||
response.Error(w, http.StatusBadRequest, "invalid body")
|
||
return
|
||
}
|
||
ids := make([]uuid.UUID, 0, len(body.FolderIDs))
|
||
for _, s := range body.FolderIDs {
|
||
id, err := uuid.Parse(s)
|
||
if err != nil {
|
||
response.Error(w, http.StatusBadRequest, "invalid folder_id: "+s)
|
||
return
|
||
}
|
||
ids = append(ids, id)
|
||
}
|
||
if err := h.store.ReorderFavorites(r.Context(), user.ID, ids); err != nil {
|
||
response.Error(w, http.StatusInternalServerError, "database error")
|
||
return
|
||
}
|
||
response.NoContent(w)
|
||
}
|