ultisuite-backend/internal/api/ultidraw/public_share.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

163 lines
5.1 KiB
Go

package ultidraw
import (
"context"
"fmt"
"io"
"net/url"
"strings"
"time"
"github.com/ultisuite/ulti-backend/internal/nextcloud"
)
func (s *Service) CreatePublicSession(ctx context.Context, token, filePath, mode, password, guestID, guestName, displayName string) (*PublicSessionResult, error) {
if !s.Cfg.Enabled {
return nil, fmt.Errorf("ultidraw editor disabled")
}
resolvedPath, err := s.resolvePublicFilePath(ctx, token, filePath, password, displayName)
if err != nil {
return nil, err
}
filePath = resolvedPath
if mode == "" {
mode = "edit"
}
ownerID, ownerPath, err := s.ownerPathForPublic(ctx, token, password, filePath, displayName)
if err != nil {
return nil, err
}
roomID, err := s.resolveCollabRoomID(ctx, ownerID, ownerPath)
if err != nil {
return nil, err
}
tokenJWT, err := signRoomToken(roomTokenPayload{
Room: roomID,
Path: filePath,
User: "public:" + token,
Sub: guestID,
Name: guestName,
Mode: mode,
Expires: time.Now().Add(8 * time.Hour).Unix(),
}, s.Cfg.HocuspocusSecret)
if err != nil {
return nil, err
}
apiBase := strings.TrimRight(s.Cfg.APIInternalURL, "/")
sig, _ := signPublicDocAccess(token, filePath, password, s.Cfg.HocuspocusSecret)
docURL := fmt.Sprintf("%s/api/v1/drive/public/shares/%s/ultidraw/document?path=%s&password=%s&sig=%s",
apiBase, url.PathEscape(token), url.QueryEscape(filePath), url.QueryEscape(password), url.QueryEscape(sig))
saveURL := docURL
wsURL := strings.TrimSpace(s.Cfg.HocuspocusPublicURL)
collab := wsURL != "" && s.Cfg.HocuspocusSecret != ""
return &PublicSessionResult{
SessionResult: SessionResult{
RoomID: roomID,
CanonicalPath: filePath,
WsURL: wsURL,
Token: tokenJWT,
Mode: mode,
Collaboration: collab,
},
DocumentURL: docURL,
SaveURL: saveURL,
}, nil
}
func (s *Service) resolvePublicFilePath(ctx context.Context, token, filePath, password, displayName string) (string, error) {
filePath = normalizePath(filePath)
if filePath == "/" {
filePath = s.publicClientSourcePath(ctx, token, password, filePath, displayName)
}
checkPath := filePath
if !isExcalidrawPath(checkPath) && strings.TrimSpace(displayName) != "" {
checkPath = normalizePath("/" + strings.TrimSpace(displayName))
}
if !isExcalidrawPath(checkPath) {
return "", fmt.Errorf("not an excalidraw file")
}
if !isExcalidrawPath(filePath) {
filePath = checkPath
}
if _, err := s.publicFileExists(ctx, token, filePath, password); err != nil {
if checkPath != filePath {
if _, err2 := s.publicFileExists(ctx, token, checkPath, password); err2 != nil {
return "", fmt.Errorf("file not found")
}
filePath = checkPath
} else {
return "", fmt.Errorf("file not found")
}
}
return filePath, nil
}
func (s *Service) publicClientSourcePath(ctx context.Context, token, password, clientPath, displayName string) string {
binding, err := s.nc.ResolvePublicShareBinding(ctx, token, password)
if err == nil {
return binding.ClientSourcePath(clientPath, displayName)
}
if name := strings.TrimSpace(displayName); name != "" {
return normalizePath("/" + name)
}
return clientPath
}
func (s *Service) publicFileExists(ctx context.Context, token, path, password string) (bool, error) {
_, err := s.nc.PublicShareFileRevision(ctx, token, path, password)
if err != nil {
return false, err
}
return true, nil
}
func (s *Service) LoadPublicDocument(ctx context.Context, token, clientPath, password, displayName string) ([]byte, error) {
if binding, err := s.nc.ResolvePublicShareBinding(ctx, token, password); err == nil {
ownerPath := binding.OwnerPathForClient(clientPath, displayName)
body, _, err := s.nc.Download(ctx, binding.OwnerID, ownerPath)
if err == nil {
defer body.Close()
return io.ReadAll(body)
}
}
body, _, err := s.nc.DownloadPublicShare(ctx, token, clientPath, password)
if err != nil {
return nil, err
}
defer body.Close()
return io.ReadAll(body)
}
func (s *Service) SavePublicDocument(ctx context.Context, token, clientPath, password, displayName string, raw []byte) error {
if binding, err := s.nc.ResolvePublicShareBinding(ctx, token, password); err == nil {
ownerPath := binding.OwnerPathForClient(clientPath, displayName)
reader := strings.NewReader(string(raw))
if err := s.nc.Upload(ctx, binding.OwnerID, ownerPath, reader, "application/json"); err == nil {
return nil
}
}
reader := strings.NewReader(string(raw))
return s.nc.UploadPublicShare(ctx, token, clientPath, password, reader, "application/json")
}
func (s *Service) LoadPublicDocumentLegacy(ctx context.Context, token, path, password string) ([]byte, error) {
return s.LoadPublicDocument(ctx, token, path, password, "")
}
func (s *Service) SavePublicDocumentLegacy(ctx context.Context, token, path, password string, raw []byte) error {
return s.SavePublicDocument(ctx, token, path, password, "", raw)
}
func (s *Service) EffectivePublicSharePermissions(ctx context.Context, token, path, password string) (int, error) {
return s.nc.EffectivePublicSharePermissions(ctx, token, path, password)
}
func (s *Service) PublicShareCanUpdate(perms int) bool {
return nextcloud.PublicShareCanUpdate(perms)
}