ultisuite-backend/internal/api/drive/mount_cloud_service.go
R3D347HR4Y 857b9afc43
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
feat(drive): implement external URL resolution for mounted cloud files
- Added new functionality to resolve external URLs for files on Google Drive and Microsoft OneDrive mounts.
- Introduced `mount_cloud_service.go` to handle OAuth token extraction and URL resolution.
- Enhanced `mounts_service.go` to update mount configurations with OAuth tokens.
- Updated API routes to include a new endpoint for fetching external URLs.
- Implemented enrichment functions in `cloud_native.go` to mark files that should open in the provider's web editor.
- Added tests for cloud-native file enrichment in `cloud_native_test.go` to ensure correct behavior.
2026-06-13 13:44:43 +02:00

99 lines
2.4 KiB
Go

package drive
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/ultisuite/ulti-backend/internal/driveroot"
"github.com/ultisuite/ulti-backend/internal/nextcloud"
)
func mountOAuthAccessToken(configEnc []byte) string {
if len(configEnc) == 0 {
return ""
}
var cfg map[string]any
if err := json.Unmarshal(configEnc, &cfg); err != nil {
return ""
}
if raw, ok := cfg["token"].(string); ok {
return nextcloud.ParseOAuthAccessToken(raw)
}
return ""
}
// ResolveMountExternalURL returns the provider web URL for a file on an external mount.
func (s *Service) ResolveMountExternalURL(ctx context.Context, ncUserID, mountID, platformUserID, logicalPath string) (string, error) {
store := s.ensureStore()
if store == nil {
return "", fmt.Errorf("store not configured")
}
mount, err := store.GetMount(ctx, mountID)
if err != nil {
return "", err
}
if mount.OwnerUserID == nil || *mount.OwnerUserID != platformUserID {
return "", ErrForbidden
}
logicalPath = nextcloud.NormalizeClientPath(logicalPath)
if logicalPath == "/" {
return "", ErrInvalid
}
file, err := s.StatFileAtRoot(ctx, ncUserID, driveroot.Mount(mountID, logicalPath))
if err != nil {
return "", err
}
if file.ExternalURL != "" {
return file.ExternalURL, nil
}
if !file.OpenExternally {
return "", ErrInvalid
}
configEnc, err := store.GetMountConfig(ctx, mountID)
if err != nil {
return "", err
}
accessToken := mountOAuthAccessToken(configEnc)
if accessToken == "" {
return "", fmt.Errorf("mount oauth token not available")
}
resolver := &nextcloud.MountCloudResolver{}
backend := driveroot.NormalizeMountBackend(mount.BackendType)
switch backend {
case "google":
if id := providerIDFromETag(file.ETag); id != "" {
link, err := resolver.GoogleDriveWebViewLink(ctx, accessToken, id)
if err == nil && link != "" {
return link, nil
}
}
return resolver.ResolveGoogleDrivePath(ctx, accessToken, logicalPath)
case "microsoft":
return resolver.ResolveMicrosoftDrivePath(ctx, accessToken, logicalPath)
default:
return "", ErrInvalid
}
}
func providerIDFromETag(etag string) string {
id := strings.Trim(strings.TrimSpace(etag), "\"")
if id == "" {
return ""
}
for _, r := range id {
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' {
continue
}
return ""
}
if len(id) < 10 {
return ""
}
return id
}