diff --git a/__tests__/utils/uploader.spec.ts b/__tests__/utils/uploader.spec.ts new file mode 100644 index 00000000..75ceff37 --- /dev/null +++ b/__tests__/utils/uploader.spec.ts @@ -0,0 +1,49 @@ +import { beforeEach, describe, expect, test, vi } from 'vitest' +import { Uploader } from '../../lib/uploader.js' + +import type { NextcloudUser } from '@nextcloud/auth' + +const initialState = vi.hoisted(() => ({ loadState: vi.fn() })) +const auth = vi.hoisted(() => ({ getCurrentUser: vi.fn(() => null) })) +vi.mock('@nextcloud/initial-state', () => initialState) +vi.mock('@nextcloud/auth', () => auth) + +describe('uploader', () => { + beforeEach(() => { + vi.resetAllMocks() + const node = document.getElementById('sharingToken') + if (node) { + document.body.removeChild(node) + } + }) + + test('constructor sets default target folder for user', async () => { + auth.getCurrentUser.mockImplementationOnce(() => ({ uid: 'my-user', displayName: 'User', isAdmin: false })) + const uploader = new Uploader() + expect(uploader.destination.source).match(/\/remote\.php\/dav\/files\/my-user\/?$/) + }) + + test('constructor sets default target folder for public share', async () => { + initialState.loadState.mockImplementationOnce((app, key) => app === 'files_sharing' && key === 'sharingToken' ? 'token-123' : null) + const uploader = new Uploader(true) + expect(uploader.destination.source).match(/\/public\.php\/dav\/files\/token-123\/?$/) + }) + + test('constructor sets default target folder for legacy public share', async () => { + const input = document.createElement('input') + input.id = 'sharingToken' + input.value = 'legacy-token' + document.body.appendChild(input) + const uploader = new Uploader(true) + expect(uploader.destination.source).match(/\/public\.php\/dav\/files\/legacy-token\/?$/) + }) + + test('fails if no sharingToken on public share', async () => { + expect(() => new Uploader(true)).toThrow(/No sharing token found/) + }) + + test('fails if not logged in and not on public share', async () => { + expect(() => new Uploader()).toThrow(/User is not logged in/) + expect(initialState.loadState).not.toBeCalled() + }) +}) diff --git a/lib/uploader.ts b/lib/uploader.ts index 2d1eddb0..9bef99cd 100644 --- a/lib/uploader.ts +++ b/lib/uploader.ts @@ -3,6 +3,7 @@ import type { WebDAVClient } from 'webdav' import { getCurrentUser } from '@nextcloud/auth' import { Folder, Permission, davGetClient } from '@nextcloud/files' +import { loadState } from '@nextcloud/initial-state' import { encodePath } from '@nextcloud/paths' import { generateRemoteUrl } from '@nextcloud/router' import { normalize } from 'path' @@ -53,11 +54,26 @@ export class Uploader { this._isPublic = isPublic if (!destinationFolder) { - const owner = getCurrentUser()?.uid - const source = generateRemoteUrl(`dav/files/${owner}`) - if (!owner) { - throw new Error('User is not logged in') + let owner: string + let source: string + + if (isPublic) { + const sharingToken = loadState('files_sharing', 'sharingToken', null) ?? document.querySelector('input#sharingToken')?.value + if (!sharingToken) { + logger.error('No sharing token found for public shares, please specify the destination folder manually.') + throw new Error('No sharing token found.') + } + owner = sharingToken + source = generateRemoteUrl(`dav/files/${sharingToken}`).replace('remote.php', 'public.php') + } else { + const user = getCurrentUser()?.uid + if (!user) { + throw new Error('User is not logged in') + } + owner = user + source = generateRemoteUrl(`dav/files/${owner}`) } + destinationFolder = new Folder({ id: 0, owner, diff --git a/vitest.config.ts b/vitest.config.ts index 672d0138..78fbc934 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -15,6 +15,11 @@ export default async (env) => { exclude: ['lib/utils/l10n.ts'], reporter: ['lcov', 'text'], }, + server: { + deps: { + inline: ['@nextcloud/files'], + }, + }, } as UserConfig return cfg }