- 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.
99 lines
2.4 KiB
Go
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
|
|
}
|