mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
refactor(files_external): adjust files integration
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
a05c285979
commit
e76f9284ce
8 changed files with 72 additions and 73 deletions
|
|
@ -1,14 +1,14 @@
|
|||
/**
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { View } from '@nextcloud/files'
|
||||
import type { StorageConfig } from '../services/externalStorage.ts'
|
||||
import type { IStorage } from '../types.ts'
|
||||
|
||||
import { DefaultType, File, FileAction, Folder, Permission } from '@nextcloud/files'
|
||||
import { describe, expect, test } from 'vitest'
|
||||
import { STORAGE_STATUS } from '../utils/credentialsUtils.ts'
|
||||
import { StorageStatus } from '../types.ts'
|
||||
import { action } from './enterCredentialsAction.ts'
|
||||
|
||||
const view = {
|
||||
|
|
@ -31,8 +31,8 @@ describe('Enter credentials action conditions tests', () => {
|
|||
permissions: Permission.ALL,
|
||||
attributes: {
|
||||
config: {
|
||||
status: STORAGE_STATUS.SUCCESS,
|
||||
} as StorageConfig,
|
||||
status: StorageStatus.Success,
|
||||
} as IStorage,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -72,8 +72,8 @@ describe('Enter credentials action enabled tests', () => {
|
|||
scope: 'system',
|
||||
backend: 'SFTP',
|
||||
config: {
|
||||
status: STORAGE_STATUS.SUCCESS,
|
||||
} as StorageConfig,
|
||||
status: StorageStatus.Success,
|
||||
} as IStorage,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -87,9 +87,9 @@ describe('Enter credentials action enabled tests', () => {
|
|||
scope: 'system',
|
||||
backend: 'SFTP',
|
||||
config: {
|
||||
status: STORAGE_STATUS.INCOMPLETE_CONF,
|
||||
status: StorageStatus.IncompleteConf,
|
||||
userProvided: true,
|
||||
} as StorageConfig,
|
||||
} as IStorage,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -103,9 +103,9 @@ describe('Enter credentials action enabled tests', () => {
|
|||
scope: 'system',
|
||||
backend: 'SFTP',
|
||||
config: {
|
||||
status: STORAGE_STATUS.INCOMPLETE_CONF,
|
||||
status: StorageStatus.IncompleteConf,
|
||||
authMechanism: 'password::global::user',
|
||||
} as StorageConfig,
|
||||
} as IStorage,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ describe('Enter credentials action enabled tests', () => {
|
|||
scope: 'system',
|
||||
backend: 'SFTP',
|
||||
config: {
|
||||
} as StorageConfig,
|
||||
} as IStorage,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
/**
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { AxiosResponse } from '@nextcloud/axios'
|
||||
import type { Node } from '@nextcloud/files'
|
||||
import type { StorageConfig } from '../services/externalStorage.ts'
|
||||
import type { INode } from '@nextcloud/files'
|
||||
import type { IStorage } from '../types.ts'
|
||||
|
||||
import LoginSvg from '@mdi/svg/svg/login.svg?raw'
|
||||
import axios from '@nextcloud/axios'
|
||||
|
|
@ -17,7 +17,8 @@ import { addPasswordConfirmationInterceptors, PwdConfirmationMode } from '@nextc
|
|||
import { generateUrl } from '@nextcloud/router'
|
||||
import { spawnDialog } from '@nextcloud/vue/functions/dialog'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { isMissingAuthConfig, STORAGE_STATUS } from '../utils/credentialsUtils.ts'
|
||||
import { StorageStatus } from '../types.ts'
|
||||
import { isMissingAuthConfig } from '../utils/credentialsUtils.ts'
|
||||
import { isNodeExternalStorage } from '../utils/externalStorageUtils.ts'
|
||||
|
||||
// Add password confirmation interceptors as
|
||||
|
|
@ -31,7 +32,7 @@ addPasswordConfirmationInterceptors(axios)
|
|||
* @param login The username
|
||||
* @param password The password
|
||||
*/
|
||||
async function setCredentials(node: Node, login: string, password: string): Promise<null | true> {
|
||||
async function setCredentials(node: INode, login: string, password: string): Promise<null | true> {
|
||||
const configResponse = await axios.request({
|
||||
method: 'PUT',
|
||||
url: generateUrl('apps/files_external/userglobalstorages/{id}', { id: node.attributes.id }),
|
||||
|
|
@ -39,10 +40,10 @@ async function setCredentials(node: Node, login: string, password: string): Prom
|
|||
data: {
|
||||
backendOptions: { user: login, password },
|
||||
},
|
||||
}) as AxiosResponse<StorageConfig>
|
||||
}) as AxiosResponse<IStorage>
|
||||
|
||||
const config = configResponse.data
|
||||
if (config.status !== STORAGE_STATUS.SUCCESS) {
|
||||
if (config.status !== StorageStatus.Success) {
|
||||
showError(t('files_external', 'Unable to update this external storage config. {statusMessage}', {
|
||||
statusMessage: config?.statusMessage || '',
|
||||
}))
|
||||
|
|
@ -75,7 +76,7 @@ export const action = new FileAction({
|
|||
return false
|
||||
}
|
||||
|
||||
const config = (node.attributes?.config || {}) as StorageConfig
|
||||
const config = (node.attributes?.config || {}) as IStorage
|
||||
if (isMissingAuthConfig(config)) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -87,7 +88,7 @@ export const action = new FileAction({
|
|||
const { login, password } = await spawnDialog(defineAsyncComponent(() => import('../views/CredentialsDialog.vue'))) ?? {}
|
||||
if (login && password) {
|
||||
try {
|
||||
await setCredentials(nodes[0], login, password)
|
||||
await setCredentials(nodes[0]!, login, password)
|
||||
showSuccess(t('files_external', 'Credentials successfully set'))
|
||||
} catch (error) {
|
||||
showError(t('files_external', 'Error while setting credentials: {error}', {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
|
||||
import type { AxiosError } from '@nextcloud/axios'
|
||||
import type { StorageConfig } from '../services/externalStorage.ts'
|
||||
import type { IStorage } from '../types.ts'
|
||||
|
||||
import AlertSvg from '@mdi/svg/svg/alert-circle.svg?raw'
|
||||
import { showWarning } from '@nextcloud/dialogs'
|
||||
|
|
@ -12,7 +12,8 @@ import { emit } from '@nextcloud/event-bus'
|
|||
import { FileAction } from '@nextcloud/files'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { getStatus } from '../services/externalStorage.ts'
|
||||
import { isMissingAuthConfig, STORAGE_STATUS } from '../utils/credentialsUtils.ts'
|
||||
import { StorageStatus } from '../types.ts'
|
||||
import { isMissingAuthConfig } from '../utils/credentialsUtils.ts'
|
||||
import { isNodeExternalStorage } from '../utils/externalStorageUtils.ts'
|
||||
|
||||
import '../css/fileEntryStatus.scss'
|
||||
|
|
@ -44,14 +45,14 @@ export const action = new FileAction({
|
|||
span.className = 'files-list__row-status'
|
||||
span.innerHTML = t('files_external', 'Checking storage …')
|
||||
|
||||
let config = null as unknown as StorageConfig
|
||||
let config: IStorage | undefined
|
||||
try {
|
||||
const { data } = await getStatus(node.attributes.id, node.attributes.scope === 'system')
|
||||
config = data
|
||||
node.attributes.config = config
|
||||
emit('files:node:updated', node)
|
||||
|
||||
if (config.status !== STORAGE_STATUS.SUCCESS) {
|
||||
if (config.status !== StorageStatus.Success) {
|
||||
throw new Error(config?.statusMessage || t('files_external', 'There was an error with this external storage.'))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
/**
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { View } from '@nextcloud/files'
|
||||
import type { StorageConfig } from '../services/externalStorage.ts'
|
||||
import type { IStorage } from '../types.ts'
|
||||
|
||||
import * as dialogs from '@nextcloud/dialogs'
|
||||
import { DefaultType, FileAction, Folder, Permission } from '@nextcloud/files'
|
||||
import { describe, expect, test, vi } from 'vitest'
|
||||
import { STORAGE_STATUS } from '../utils/credentialsUtils.ts'
|
||||
import { StorageStatus } from '../types.ts'
|
||||
import { action } from './openInFilesAction.ts'
|
||||
|
||||
vi.mock('@nextcloud/dialogs', { spy: true })
|
||||
|
||||
const view = {
|
||||
id: 'files',
|
||||
name: 'Files',
|
||||
|
|
@ -31,8 +34,8 @@ describe('Open in files action conditions tests', () => {
|
|||
permissions: Permission.ALL,
|
||||
attributes: {
|
||||
config: {
|
||||
status: STORAGE_STATUS.SUCCESS,
|
||||
} as StorageConfig,
|
||||
status: StorageStatus.Success,
|
||||
} as IStorage,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -64,8 +67,8 @@ describe('Open in files action conditions tests', () => {
|
|||
permissions: Permission.ALL,
|
||||
attributes: {
|
||||
config: {
|
||||
status: STORAGE_STATUS.ERROR,
|
||||
} as StorageConfig,
|
||||
status: StorageStatus.Error,
|
||||
} as IStorage,
|
||||
},
|
||||
})
|
||||
expect(action.displayName({
|
||||
|
|
@ -102,6 +105,7 @@ describe('Open in files action enabled tests', () => {
|
|||
describe('Open in files action execute tests', () => {
|
||||
test('Open in files', async () => {
|
||||
const goToRouteMock = vi.fn()
|
||||
// @ts-expect-error - mocking for tests
|
||||
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
|
||||
|
||||
const storage = new Folder({
|
||||
|
|
@ -112,8 +116,8 @@ describe('Open in files action execute tests', () => {
|
|||
permissions: Permission.ALL,
|
||||
attributes: {
|
||||
config: {
|
||||
status: STORAGE_STATUS.SUCCESS,
|
||||
} as StorageConfig,
|
||||
status: StorageStatus.Success,
|
||||
} as IStorage,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -130,8 +134,8 @@ describe('Open in files action execute tests', () => {
|
|||
})
|
||||
|
||||
test('Open in files broken storage', async () => {
|
||||
const confirmMock = vi.fn()
|
||||
window.OC = { dialogs: { confirm: confirmMock } }
|
||||
// @ts-expect-error - spy added by vitest
|
||||
dialogs.showConfirmation.mockImplementationOnce(() => Promise.resolve(true))
|
||||
|
||||
const storage = new Folder({
|
||||
id: 1,
|
||||
|
|
@ -141,8 +145,8 @@ describe('Open in files action execute tests', () => {
|
|||
permissions: Permission.ALL,
|
||||
attributes: {
|
||||
config: {
|
||||
status: STORAGE_STATUS.ERROR,
|
||||
} as StorageConfig,
|
||||
status: StorageStatus.Error,
|
||||
} as IStorage,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -154,6 +158,6 @@ describe('Open in files action execute tests', () => {
|
|||
})
|
||||
// Silent action
|
||||
expect(exec).toBe(null)
|
||||
expect(confirmMock).toBeCalledTimes(1)
|
||||
expect(dialogs.showConfirmation).toHaveBeenCalledOnce()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
/**
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
import type { StorageConfig } from '../services/externalStorage.ts'
|
||||
|
||||
import type { IStorage } from '../types.ts'
|
||||
|
||||
import { getCurrentUser } from '@nextcloud/auth'
|
||||
import { showConfirmation } from '@nextcloud/dialogs'
|
||||
import { DefaultType, FileAction } from '@nextcloud/files'
|
||||
import { t } from '@nextcloud/l10n'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { STORAGE_STATUS } from '../utils/credentialsUtils.ts'
|
||||
import { StorageStatus } from '../types.ts'
|
||||
|
||||
export const action = new FileAction({
|
||||
id: 'open-in-files-external-storage',
|
||||
displayName: ({ nodes }) => {
|
||||
const config = nodes?.[0]?.attributes?.config as StorageConfig || { status: STORAGE_STATUS.INDETERMINATE }
|
||||
if (config.status !== STORAGE_STATUS.SUCCESS) {
|
||||
const config = nodes?.[0]?.attributes?.config as IStorage || { status: StorageStatus.Indeterminate }
|
||||
if (config.status !== StorageStatus.Success) {
|
||||
return t('files_external', 'Examine this faulty external storage configuration')
|
||||
}
|
||||
return t('files', 'Open in Files')
|
||||
|
|
@ -24,18 +26,18 @@ export const action = new FileAction({
|
|||
enabled: ({ view }) => view.id === 'extstoragemounts',
|
||||
|
||||
async exec({ nodes }) {
|
||||
const config = nodes[0]?.attributes?.config as StorageConfig
|
||||
if (config?.status !== STORAGE_STATUS.SUCCESS) {
|
||||
window.OC.dialogs.confirm(
|
||||
t('files_external', 'There was an error with this external storage. Do you want to review this mount point config in the settings page?'),
|
||||
t('files_external', 'External mount error'),
|
||||
(redirect) => {
|
||||
if (redirect === true) {
|
||||
const scope = getCurrentUser()?.isAdmin ? 'admin' : 'user'
|
||||
window.location.href = generateUrl(`/settings/${scope}/externalstorages`)
|
||||
}
|
||||
},
|
||||
)
|
||||
const config = nodes[0]?.attributes?.config as IStorage
|
||||
if (config?.status !== StorageStatus.Success) {
|
||||
const redirect = await showConfirmation({
|
||||
name: t('files_external', 'External mount error'),
|
||||
text: t('files_external', 'There was an error with this external storage. Do you want to review this mount point config in the settings page?'),
|
||||
labelConfirm: t('files_external', 'Open settings'),
|
||||
labelReject: t('files_external', 'Ignore'),
|
||||
})
|
||||
if (redirect === true) {
|
||||
const scope = getCurrentUser()?.isAdmin ? 'admin' : 'user'
|
||||
window.location.href = generateUrl(`/settings/${scope}/externalstorages`)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { getCurrentUser } from '@nextcloud/auth'
|
|||
import axios from '@nextcloud/axios'
|
||||
import { Folder, Permission } from '@nextcloud/files'
|
||||
import { generateOcsUrl, generateRemoteUrl, generateUrl } from '@nextcloud/router'
|
||||
import { STORAGE_STATUS } from '../utils/credentialsUtils.ts'
|
||||
import { StorageStatus } from '../types.ts'
|
||||
|
||||
export const rootPath = `/files/${getCurrentUser()?.uid}`
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ function entryToFolder(ocsEntry: MountEntry): Folder {
|
|||
source: generateRemoteUrl('dav' + rootPath + '/' + path),
|
||||
root: rootPath,
|
||||
owner: getCurrentUser()?.uid || null,
|
||||
permissions: ocsEntry.config.status !== STORAGE_STATUS.SUCCESS
|
||||
permissions: ocsEntry.config.status !== StorageStatus.Success
|
||||
? Permission.NONE
|
||||
: ocsEntry?.permissions || Permission.READ,
|
||||
attributes: {
|
||||
|
|
|
|||
|
|
@ -1,29 +1,20 @@
|
|||
/**
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { StorageConfig } from '../services/externalStorage.ts'
|
||||
import type { IStorage } from '../types.ts'
|
||||
|
||||
// @see https://github.com/nextcloud/server/blob/ac2bc2384efe3c15ff987b87a7432bc60d545c67/lib/public/Files/StorageNotAvailableException.php#L41
|
||||
export enum STORAGE_STATUS {
|
||||
SUCCESS = 0,
|
||||
ERROR = 1,
|
||||
INDETERMINATE = 2,
|
||||
INCOMPLETE_CONF = 3,
|
||||
UNAUTHORIZED = 4,
|
||||
TIMEOUT = 5,
|
||||
NETWORK_ERROR = 6,
|
||||
}
|
||||
import { StorageStatus } from '../types.ts'
|
||||
|
||||
/**
|
||||
* Check if the given storage configuration is missing authentication configuration
|
||||
*
|
||||
* @param config - The storage configuration to check
|
||||
*/
|
||||
export function isMissingAuthConfig(config: StorageConfig) {
|
||||
export function isMissingAuthConfig(config: IStorage) {
|
||||
// If we don't know the status, assume it is ok
|
||||
if (!config.status || config.status === STORAGE_STATUS.SUCCESS) {
|
||||
if (config.status === undefined || config.status === StorageStatus.Success) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/*!
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue