ultisuite-backend/internal/api/ultidraw/public_handlers.go
R3D347HR4Y 0466a1c169
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
wow
2026-06-11 01:22:52 +02:00

141 lines
4.3 KiB
Go

package ultidraw
import (
"io"
"net/http"
"strings"
"github.com/go-chi/chi/v5"
"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
"github.com/ultisuite/ulti-backend/internal/nextcloud"
)
type publicSessionRequest struct {
Path string `json:"path"`
Mode string `json:"mode"`
Password string `json:"password"`
GuestID string `json:"guest_id"`
GuestName string `json:"guest_name"`
DisplayName string `json:"display_name"`
}
func (h *Handler) PublicShareSession(w http.ResponseWriter, r *http.Request) {
token := strings.TrimSpace(chi.URLParam(r, "token"))
if token == "" {
apivalidate.WriteNotFound(w, r, "not found")
return
}
var req publicSessionRequest
if err := apivalidate.DecodeJSON(w, r, 32<<10, &req); err != nil {
return
}
if strings.TrimSpace(req.Path) == "" {
apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
apivalidate.FieldDetail{Field: "path", Message: "required"},
))
return
}
password := strings.TrimSpace(req.Password)
perms, err := h.svc.EffectivePublicSharePermissions(r.Context(), token, req.Path, password)
if err != nil {
apivalidate.WriteInternal(w, r)
return
}
if !nextcloud.PublicShareCanRead(perms) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
mode := strings.TrimSpace(req.Mode)
if mode == "" {
mode = "edit"
}
if mode == "edit" && !nextcloud.PublicShareCanUpdate(perms) {
mode = "view"
}
guestID := strings.TrimSpace(req.GuestID)
if guestID == "" {
guestID = "public-guest"
} else {
guestID = "public:" + guestID
}
guestName := strings.TrimSpace(req.GuestName)
if guestName == "" {
guestName = "Invité"
}
result, err := h.svc.CreatePublicSession(r.Context(), token, req.Path, mode, password, guestID, guestName, strings.TrimSpace(req.DisplayName))
if err != nil {
h.logger.Error("public ultidraw session", "error", err)
apivalidate.WriteInternal(w, r)
return
}
result.Mode = mode
apiresponse.WriteJSON(w, http.StatusOK, result)
}
func (h *Handler) PublicShareDocument(w http.ResponseWriter, r *http.Request) {
token := strings.TrimSpace(chi.URLParam(r, "token"))
path := strings.TrimSpace(r.URL.Query().Get("path"))
password := strings.TrimSpace(r.URL.Query().Get("password"))
sig := strings.TrimSpace(r.URL.Query().Get("sig"))
if h.svc.Cfg.HocuspocusSecret != "" && !verifyPublicDocAccess(token, path, password, sig, h.svc.Cfg.HocuspocusSecret) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
body, err := h.svc.LoadPublicDocumentLegacy(r.Context(), token, path, password)
if err != nil {
http.Error(w, "not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(body)
}
func (h *Handler) PublicSharePutDocument(w http.ResponseWriter, r *http.Request) {
token := strings.TrimSpace(chi.URLParam(r, "token"))
path := strings.TrimSpace(r.URL.Query().Get("path"))
password := strings.TrimSpace(r.URL.Query().Get("password"))
sig := strings.TrimSpace(r.URL.Query().Get("sig"))
if h.svc.Cfg.HocuspocusSecret != "" && !verifyPublicDocAccess(token, path, password, sig, h.svc.Cfg.HocuspocusSecret) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
perms, err := h.svc.EffectivePublicSharePermissions(r.Context(), token, path, password)
if err != nil || !nextcloud.PublicShareCanUpdate(perms) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
raw, err := io.ReadAll(r.Body)
if err != nil {
apivalidate.WriteInternal(w, r)
return
}
existingRaw, loadErr := h.svc.LoadPublicDocumentLegacy(r.Context(), token, path, password)
var existing UltiDrawDoc
if loadErr == nil && len(existingRaw) > 0 {
if parsed, parseErr := ParseUltiDrawDoc(existingRaw); parseErr == nil {
existing = parsed
}
}
doc, err := ApplyUltiDrawPatch(existing, raw)
if err != nil {
apivalidate.WriteValidationError(w, r, apivalidate.NewValidationError(
apivalidate.FieldDetail{Field: "document", Message: "invalid JSON"},
))
return
}
payload, err := doc.Marshal()
if err != nil {
apivalidate.WriteInternal(w, r)
return
}
if err := h.svc.SavePublicDocumentLegacy(r.Context(), token, path, password, payload); err != nil {
http.Error(w, "save failed", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent)
}