128 lines
3.3 KiB
Go
128 lines
3.3 KiB
Go
package ultidraw
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/ultisuite/ulti-backend/internal/auth"
|
|
"github.com/ultisuite/ulti-backend/internal/nextcloud"
|
|
)
|
|
|
|
type Service struct {
|
|
nc *nextcloud.Client
|
|
Cfg Config
|
|
hub fileChangePublisher
|
|
}
|
|
|
|
type fileChangePublisher interface {
|
|
PublishFileChanged(platformUserID, path string)
|
|
}
|
|
|
|
func NewService(nc *nextcloud.Client, cfg Config, hub fileChangePublisher) *Service {
|
|
return &Service{nc: nc, Cfg: cfg, hub: hub}
|
|
}
|
|
|
|
func (s *Service) EnsureNextcloudUser(ctx context.Context, claims *auth.Claims) (string, error) {
|
|
return s.nc.EnsurePrincipal(ctx, claims.Email, claims.Sub, claims.Name)
|
|
}
|
|
|
|
type SessionResult struct {
|
|
RoomID string `json:"roomId"`
|
|
CanonicalPath string `json:"canonicalPath"`
|
|
WsURL string `json:"wsUrl"`
|
|
Token string `json:"token"`
|
|
Mode string `json:"mode"`
|
|
Collaboration bool `json:"collaboration"`
|
|
}
|
|
|
|
type PublicSessionResult struct {
|
|
SessionResult
|
|
DocumentURL string `json:"documentUrl,omitempty"`
|
|
SaveURL string `json:"saveUrl,omitempty"`
|
|
}
|
|
|
|
func (s *Service) CreateSession(ctx context.Context, ncUser, filePath, mode, editorUserID, editorName string) (*SessionResult, error) {
|
|
if !s.Cfg.Enabled {
|
|
return nil, fmt.Errorf("ultidraw editor disabled")
|
|
}
|
|
filePath = normalizePath(filePath)
|
|
if !isExcalidrawPath(filePath) {
|
|
return nil, fmt.Errorf("not an excalidraw file: %s", filePath)
|
|
}
|
|
if _, err := s.nc.FileRevision(ctx, ncUser, filePath); err != nil {
|
|
return nil, fmt.Errorf("file not found: %s", filePath)
|
|
}
|
|
if mode == "" {
|
|
mode = "edit"
|
|
}
|
|
|
|
roomID, err := s.resolveCollabRoomID(ctx, ncUser, filePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
token, err := signRoomToken(roomTokenPayload{
|
|
Room: roomID,
|
|
Path: filePath,
|
|
User: ncUser,
|
|
Sub: editorUserID,
|
|
Name: editorName,
|
|
Mode: mode,
|
|
Expires: time.Now().Add(8 * time.Hour).Unix(),
|
|
}, s.Cfg.HocuspocusSecret)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
wsURL := strings.TrimSpace(s.Cfg.HocuspocusPublicURL)
|
|
collab := wsURL != "" && s.Cfg.HocuspocusSecret != ""
|
|
|
|
return &SessionResult{
|
|
RoomID: roomID,
|
|
CanonicalPath: filePath,
|
|
WsURL: wsURL,
|
|
Token: token,
|
|
Mode: mode,
|
|
Collaboration: collab,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Service) LoadDocument(ctx context.Context, ncUser, path string) ([]byte, error) {
|
|
path = normalizePath(path)
|
|
body, _, err := s.nc.Download(ctx, ncUser, path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer body.Close()
|
|
return io.ReadAll(body)
|
|
}
|
|
|
|
func (s *Service) LoadDocumentForUser(ctx context.Context, ncUser, path string) ([]byte, error) {
|
|
path = normalizePath(path)
|
|
if strings.HasPrefix(ncUser, "public:") {
|
|
token := strings.TrimPrefix(ncUser, "public:")
|
|
return s.LoadPublicDocumentLegacy(ctx, token, path, "")
|
|
}
|
|
return s.LoadDocument(ctx, ncUser, path)
|
|
}
|
|
|
|
func (s *Service) SaveDocument(ctx context.Context, ncUser, path string, raw []byte, platformUserID string) error {
|
|
path = normalizePath(path)
|
|
reader := bytes.NewReader(raw)
|
|
if strings.HasPrefix(ncUser, "public:") {
|
|
token := strings.TrimPrefix(ncUser, "public:")
|
|
return s.SavePublicDocumentLegacy(ctx, token, path, "", raw)
|
|
}
|
|
if err := s.nc.Upload(ctx, ncUser, path, reader, "application/json"); err != nil {
|
|
return err
|
|
}
|
|
if s.hub != nil && platformUserID != "" {
|
|
s.hub.PublishFileChanged(platformUserID, path)
|
|
}
|
|
return nil
|
|
}
|