Files
ttstd-infinite-canvas/backend/internal/app/canvas_bridge.go

325 lines
11 KiB
Go

package app
import (
"encoding/json"
"io"
"yingce/backend/internal/assets"
"yingce/backend/internal/canvas"
"yingce/backend/internal/model"
"yingce/backend/internal/repository"
)
type (
CanvasShareRequest = canvas.CanvasShareRequest
CanvasShareStatus = canvas.CanvasShareStatus
PublicCanvasShare = canvas.PublicCanvasShare
AssetsSyncRequest = canvas.AssetsSyncRequest
CanvasHistoryList = canvas.CanvasHistoryList
UserDataSummary = canvas.UserDataSummary
UserDataSnapshot = canvas.UserDataSnapshot
CanvasLibrarySummary = canvas.CanvasLibrarySummary
CanvasLibraryPage = canvas.CanvasLibraryPage
CanvasOperationsRequest = canvas.CanvasOperationsRequest
CanvasOperation = canvas.CanvasOperation
)
type canvasHost struct {
svc *Service
}
func (h canvasHost) EncryptSecret(value string) (string, error) {
if h.svc == nil {
return value, nil
}
return h.svc.encryptSettingSecret(value)
}
func (h canvasHost) DecryptSecret(value string) (string, error) {
if h.svc == nil {
return value, nil
}
return h.svc.decryptSettingSecret(value)
}
func (h canvasHost) OpenResourceRange(userID string, resource *model.Resource, rangeHeader string) (*assets.ResourceStream, error) {
if h.svc == nil {
return nil, nil
}
return h.svc.openResourceRange(userID, resource, rangeHeader)
}
func (h canvasHost) PrepareResourceDelivery(userID string, resource *model.Resource, options assets.AccessOptions, rangeHeader string) (*assets.ResourceDelivery, error) {
if h.svc == nil {
return nil, nil
}
return h.svc.prepareResourceDelivery(userID, resource, options, rangeHeader)
}
func (h canvasHost) WithStorageLock(fn func() error) error {
if h.svc == nil {
if fn == nil {
return nil
}
return fn()
}
h.svc.storageMu.Lock()
defer h.svc.storageMu.Unlock()
return fn()
}
func (h canvasHost) CanvasQuotaCheck(userID string) (func(*repository.Repository, bool, int64) error, error) {
if h.svc == nil {
return func(*repository.Repository, bool, int64) error { return nil }, nil
}
policy, err := h.svc.RuntimePolicy()
if err != nil {
return nil, err
}
return func(repo *repository.Repository, creating bool, deltaBytes int64) error {
usage, err := repo.UserStructuredStorageUsage(userID)
if err != nil {
return err
}
return validateStructuredStorageQuotaWithPolicy(usage, "canvas", creating, deltaBytes, policy.Resource)
}, nil
}
func (h canvasHost) StructuredQuota(userID, kind string, creating bool, deltaBytes int64) error {
if h.svc == nil {
return nil
}
policy, err := h.svc.RuntimePolicy()
if err != nil {
return err
}
usage, err := h.svc.repo.UserStructuredStorageUsage(userID)
if err != nil {
return err
}
return validateStructuredStorageQuotaWithPolicy(usage, kind, creating, deltaBytes, policy.Resource)
}
func (h canvasHost) StructuredReplacementQuota(userID, kind string, count int, bytes int64) error {
if h.svc == nil {
return nil
}
policy, err := h.svc.RuntimePolicy()
if err != nil {
return err
}
usage, err := h.svc.repo.UserStructuredStorageUsage(userID)
if err != nil {
return err
}
return validateStructuredReplacementQuotaWithPolicy(usage, kind, count, bytes, policy.Resource)
}
func (h canvasHost) DeleteUserAssetWithResources(userID, assetID string, purge bool) error {
if h.svc == nil {
return nil
}
return h.svc.deleteUserAssetWithResources(userID, assetID, purge)
}
func (h canvasHost) PurgeUserAssetsWithResources(userID string, assetIDs []string) error {
return h.svc.deleteUserAssetsWithResources(userID, assetIDs, true)
}
func (h canvasHost) RecordActivity(userID, event string, count int) {
if h.svc == nil {
return
}
h.svc.recordActivity(userID, event, count)
}
func (s *Service) canvasDomain() *canvas.Service {
if s == nil {
return canvas.New(nil, nil)
}
if s.canvas != nil {
return s.canvas
}
return canvas.New(s.repo, canvasHost{svc: s})
}
func (s *Service) CanvasShareStatus(userID string, projectID string) (CanvasShareStatus, error) {
return s.canvasDomain().CanvasShareStatus(userID, projectID)
}
func (s *Service) CreateCanvasShare(userID string, projectID string, req CanvasShareRequest) (CanvasShareStatus, error) {
return s.canvasDomain().CreateCanvasShare(userID, projectID, req)
}
func (s *Service) DeleteCanvasShare(userID string, projectID string) error {
return s.canvasDomain().DeleteCanvasShare(userID, projectID)
}
func (s *Service) PublicCanvasShare(token string) (PublicCanvasShare, error) {
return s.canvasDomain().PublicCanvasShare(token)
}
func (s *Service) OpenSharedCanvasResource(token string, resourceID string) (*model.Resource, io.ReadCloser, error) {
return s.canvasDomain().OpenSharedCanvasResource(token, resourceID)
}
func (s *Service) OpenSharedCanvasResourceRange(token string, resourceID string, rangeHeader string) (*ResourceStream, error) {
return s.canvasDomain().OpenSharedCanvasResourceRange(token, resourceID, rangeHeader)
}
func (s *Service) PrepareSharedCanvasResourceDelivery(token string, resourceID string, options ResourceAccessOptions, rangeHeader string) (*ResourceDelivery, error) {
return s.canvasDomain().PrepareSharedCanvasResourceDelivery(token, resourceID, options, rangeHeader)
}
func (s *Service) validateCanvasMediaAssets(userID string, raw json.RawMessage) error {
return s.canvasDomain().ValidateCanvasMediaAssets(userID, raw)
}
func (s *Service) validateAssetCanvasReferences(userID string, asset model.Asset) error {
return s.canvasDomain().ValidateAssetCanvasReferences(userID, asset)
}
func (s *Service) validateAssetReplacementCanvasReferences(userID string, replacement []model.Asset) error {
return s.canvasDomain().ValidateAssetReplacementCanvasReferences(userID, replacement)
}
func (s *Service) UserDataSnapshot(userID string) (UserDataSnapshot, error) {
return s.canvasDomain().UserDataSnapshot(userID)
}
func (s *Service) UserAssetSummaries(userID string) ([]UserDataSummary, error) {
return s.canvasDomain().UserAssetSummaries(userID)
}
func (s *Service) UserAsset(userID string, id string) (json.RawMessage, error) {
return s.canvasDomain().UserAsset(userID, id)
}
func (s *Service) UpsertUserAsset(userID string, raw json.RawMessage) (UserDataSummary, error) {
return s.canvasDomain().UpsertUserAsset(userID, raw)
}
func (s *Service) DeleteUserAsset(userID string, id string) error {
return s.canvasDomain().DeleteUserAsset(userID, id)
}
func (s *Service) PurgeUserAsset(userID string, id string) error {
return s.canvasDomain().PurgeUserAsset(userID, id)
}
func (s *Service) PurgeUserAssets(userID string, ids []string) error {
return s.canvasDomain().PurgeUserAssets(userID, ids)
}
func (s *Service) UserAssets(userID string) ([]json.RawMessage, error) {
return s.canvasDomain().UserAssets(userID)
}
func (s *Service) ReplaceUserAssets(userID string, req AssetsSyncRequest) ([]json.RawMessage, error) {
return s.canvasDomain().ReplaceUserAssets(userID, req)
}
func (s *Service) UserCanvasProjects(userID string) ([]json.RawMessage, error) {
return s.canvasDomain().UserCanvasProjects(userID)
}
func (s *Service) UserCanvasProjectSummaries(userID string) ([]UserDataSummary, error) {
return s.canvasDomain().UserCanvasProjectSummaries(userID)
}
func (s *Service) UserCanvasProjectMetadata(userID string, id string) (*model.CanvasProject, error) {
return s.canvasDomain().UserCanvasProjectMetadata(userID, id)
}
func (s *Service) UserCanvasProject(userID string, id string) (json.RawMessage, error) {
return s.canvasDomain().UserCanvasProject(userID, id)
}
func (s *Service) UpsertUserCanvasProject(userID string, raw json.RawMessage) (UserDataSummary, error) {
return s.canvasDomain().UpsertUserCanvasProject(userID, raw)
}
func (s *Service) ApplyUserCanvasOperations(userID, canvasID string, req CanvasOperationsRequest) (UserDataSummary, error) {
return s.canvasDomain().ApplyUserCanvasOperations(userID, canvasID, req)
}
func (s *Service) RepairUserCanvasProject(userID string, raw json.RawMessage) (UserDataSummary, error) {
return s.canvasDomain().RepairUserCanvasProject(userID, raw)
}
func (s *Service) DeleteUserCanvasProject(userID string, id string) error {
return s.canvasDomain().DeleteUserCanvasProject(userID, id)
}
func (s *Service) CanvasHistory(userID, canvasID string) (CanvasHistoryList, error) {
return s.canvasDomain().CanvasHistory(userID, canvasID)
}
func (s *Service) CanvasHistorySnapshot(userID, canvasID, snapshotID string) (*model.CanvasSnapshot, error) {
return s.canvasDomain().CanvasHistorySnapshot(userID, canvasID, snapshotID)
}
func (s *Service) RestoreCanvasHistory(userID, canvasID, snapshotID string, revision *int64) (UserDataSummary, error) {
return s.canvasDomain().RestoreCanvasHistory(userID, canvasID, snapshotID, revision)
}
func saveCreationCanvasWithHistory(repo *repository.Repository, project *model.CanvasProject, previous string) error {
before, err := repo.CanvasProjectForUser(project.UserID, project.ID)
if err != nil {
return err
}
if before.PayloadJSON != previous || before.Revision != project.Revision {
return repository.ErrCreationConflict
}
beforeDoc, err := creationDocument(before.PayloadJSON)
if err != nil {
return err
}
desiredDoc, err := creationDocument(project.PayloadJSON)
if err != nil {
return err
}
operations := canvas.DiffCanvasDocuments(beforeDoc, desiredDoc)
if len(operations) == 0 {
return nil
}
baseRevision := project.Revision
request := canvas.CanvasOperationsRequest{BaseRevision: &baseRevision, Operations: operations, TrustedServerMutation: true}
request.MutationID = "creation-canvas:" + project.ID + ":" + canvas.CanvasOperationsDigest(request)
result, err := canvas.New(repo, nil).ApplyUserCanvasOperations(project.UserID, project.ID, request)
if err != nil {
return err
}
project.Revision = result.Revision
project.UpdatedAt = result.UpdatedAt
return nil
}
func (s *Service) UserAssetsByIDs(userID string, ids []string) ([]json.RawMessage, error) {
return s.canvasDomain().UserAssetsByIDs(userID, ids)
}
func (s *Service) UserCanvasProjectsPage(userID string, page int, pageSize int, projectID string, search string, sort string) (CanvasLibraryPage, error) {
return s.canvasDomain().UserCanvasProjectsPage(userID, page, pageSize, projectID, search, sort)
}
func clientAssetPayload(asset model.Asset) json.RawMessage {
return canvas.ClientAssetPayload(asset)
}
func clientAssetListPayload(asset model.Asset) json.RawMessage {
return canvas.ClientAssetListPayload(asset)
}
func validateSyncedPayload(raw json.RawMessage, label string) error {
return canvas.ValidateSyncedPayload(raw, label)
}
func containsInlineMediaDataURL(value interface{}) bool {
return canvas.ContainsInlineMediaDataURL(value)
}
func assetFromJSON(userID string, raw json.RawMessage) (model.Asset, error) {
return canvas.AssetFromJSON(userID, raw)
}