From 8201f745ba7d3240eb4017765dcfd9c729fbfb09 Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Fri, 18 Jul 2025 14:09:22 +0200 Subject: [PATCH] feat(sync): split up webdav model stuff into different files --- debug-headers.spec.ts | 2 +- src/app/pfapi/api/index.ts | 4 + src/app/pfapi/api/pfapi.model.ts | 2 +- .../providers/webdav/debug-headers.spec.ts | 2 +- .../getRecommendedServerCapabilities.ts | 53 ++++++++ .../webdav/webdav-api-additional.spec.ts | 2 +- .../webdav/webdav-api-android.spec.ts | 2 +- .../webdav-api-capability-detection.spec.ts | 2 +- .../webdav/webdav-api-configuration.spec.ts | 7 +- .../webdav/webdav-api-connection.spec.ts | 3 +- .../webdav/webdav-api-download.spec.ts | 2 +- .../webdav/webdav-api-error-handling.spec.ts | 2 +- .../webdav/webdav-api-fallback.spec.ts | 2 +- .../webdav/webdav-api-fallbacks.spec.ts | 2 +- .../webdav/webdav-api-file-operations.spec.ts | 2 +- .../webdav/webdav-api-helpers.spec.ts | 2 +- .../webdav/webdav-api-integration.spec.ts | 2 +- .../webdav/webdav-api-last-modified.spec.ts | 3 +- .../webdav/webdav-api-metadata.spec.ts | 2 +- .../webdav-api-safe-creation-simple.spec.ts | 3 +- .../webdav/webdav-api-safe-creation.spec.ts | 2 +- .../providers/webdav/webdav-api-test-utils.ts | 3 +- .../webdav/webdav-api-upload.spec.ts | 2 +- .../api/sync/providers/webdav/webdav.model.ts | 63 +++++++++ .../api/sync/providers/webdav/webdav.spec.ts | 3 +- .../pfapi/api/sync/providers/webdav/webdav.ts | 121 +----------------- 26 files changed, 151 insertions(+), 144 deletions(-) create mode 100644 src/app/pfapi/api/sync/providers/webdav/getRecommendedServerCapabilities.ts create mode 100644 src/app/pfapi/api/sync/providers/webdav/webdav.model.ts diff --git a/debug-headers.spec.ts b/debug-headers.spec.ts index c639e0edf..04bbd9445 100644 --- a/debug-headers.spec.ts +++ b/debug-headers.spec.ts @@ -1,6 +1,6 @@ import { WebdavApi } from './src/app/pfapi/api/sync/providers/webdav/webdav-api'; -import { WebdavPrivateCfg } from './src/app/pfapi/api/sync/providers/webdav/webdav'; import { createMockResponse } from './src/app/pfapi/api/sync/providers/webdav/webdav-api-test-utils'; +import { WebdavPrivateCfg } from './src/app/pfapi/api/sync/providers/webdav/webdav.model'; describe('Debug Headers', () => { let mockFetch: jasmine.Spy; diff --git a/src/app/pfapi/api/index.ts b/src/app/pfapi/api/index.ts index 473c9d2e0..170d659a2 100644 --- a/src/app/pfapi/api/index.ts +++ b/src/app/pfapi/api/index.ts @@ -6,3 +6,7 @@ export * from './sync/providers/dropbox/dropbox'; export * from './sync/providers/webdav/webdav'; export * from './sync/sync-provider.interface'; export * from './errors/errors'; +export { WebdavServerType } from './sync/providers/webdav/webdav.model'; +export { WebdavPrivateCfg } from './sync/providers/webdav/webdav.model'; +export { WebdavServerCapabilities } from './sync/providers/webdav/webdav.model'; +export { getRecommendedServerCapabilities } from './sync/providers/webdav/getRecommendedServerCapabilities'; diff --git a/src/app/pfapi/api/pfapi.model.ts b/src/app/pfapi/api/pfapi.model.ts index 910733410..1e0908e1b 100644 --- a/src/app/pfapi/api/pfapi.model.ts +++ b/src/app/pfapi/api/pfapi.model.ts @@ -2,8 +2,8 @@ import { DatabaseAdapter } from './db/database-adapter.model'; import { ModelCtrl } from './model-ctrl/model-ctrl'; import { ConflictReason, SyncProviderId, SyncStatus } from './pfapi.const'; import { DropboxPrivateCfg } from './sync/providers/dropbox/dropbox'; -import { WebdavPrivateCfg } from './sync/providers/webdav/webdav'; import { IValidation } from 'typia'; +import { WebdavPrivateCfg } from './sync/providers/webdav/webdav.model'; type JSONPrimitive = string | number | boolean | null; type Serializable = JSONPrimitive | SerializableObject | SerializableArray; diff --git a/src/app/pfapi/api/sync/providers/webdav/debug-headers.spec.ts b/src/app/pfapi/api/sync/providers/webdav/debug-headers.spec.ts index 407975ed1..e5b2d25d2 100644 --- a/src/app/pfapi/api/sync/providers/webdav/debug-headers.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/debug-headers.spec.ts @@ -1,6 +1,6 @@ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { createMockResponse } from './webdav-api-test-utils'; +import { WebdavPrivateCfg } from './webdav.model'; describe('Debug Headers', () => { let mockFetch: jasmine.Spy; diff --git a/src/app/pfapi/api/sync/providers/webdav/getRecommendedServerCapabilities.ts b/src/app/pfapi/api/sync/providers/webdav/getRecommendedServerCapabilities.ts new file mode 100644 index 000000000..c39875e06 --- /dev/null +++ b/src/app/pfapi/api/sync/providers/webdav/getRecommendedServerCapabilities.ts @@ -0,0 +1,53 @@ +import { WebdavServerCapabilities, WebdavServerType } from './webdav.model'; + +/** + * Helper function to get recommended server capabilities for common WebDAV server types + */ +export const getRecommendedServerCapabilities = ( + serverType: WebdavServerType, +): WebdavServerCapabilities => { + switch (serverType) { + case WebdavServerType.NEXTCLOUD: + case WebdavServerType.OWNCLOUD: + return { + supportsETags: true, + supportsIfHeader: true, + supportsLocking: true, + supportsLastModified: true, + }; + + case WebdavServerType.APACHE_MOD_DAV: + return { + supportsETags: true, + supportsIfHeader: false, // mod_dav has limited If header support + supportsLocking: true, + supportsLastModified: true, + }; + + case WebdavServerType.NGINX_DAV: + return { + supportsETags: false, // nginx dav module has limited ETag support + supportsIfHeader: false, + supportsLocking: false, + supportsLastModified: true, + }; + + case WebdavServerType.BASIC_WEBDAV: + return { + supportsETags: false, + supportsIfHeader: false, + supportsLocking: false, + supportsLastModified: true, + }; + + case WebdavServerType.CUSTOM: + default: + // Return null to trigger auto-detection + return { + supportsETags: false, + supportsIfHeader: false, + supportsLocking: false, + supportsLastModified: false, + }; + } +}; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-additional.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-additional.spec.ts index b9e9c3fd8..8ed786831 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-additional.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-additional.spec.ts @@ -1,11 +1,11 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { RemoteFileNotFoundAPIError, NoEtagAPIError, HttpNotOkAPIError, } from '../../../errors/errors'; +import { WebdavPrivateCfg } from './webdav.model'; describe('WebdavApi - Additional Coverage Tests', () => { let api: WebdavApi; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-android.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-android.spec.ts index 88863d647..83c6b710f 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-android.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-android.spec.ts @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; // import { HttpNotOkAPIError, RemoteFileNotFoundAPIError } from '../../../errors/errors'; import { CapacitorHttp } from '@capacitor/core'; import { IS_ANDROID_WEB_VIEW } from '../../../../../util/is-android-web-view'; +import { WebdavPrivateCfg } from './webdav.model'; describe('WebdavApi - Android WebView', () => { let mockGetCfgOrError: jasmine.Spy; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-capability-detection.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-capability-detection.spec.ts index c2e995e10..833ebb648 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-capability-detection.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-capability-detection.spec.ts @@ -1,6 +1,6 @@ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { createMockResponse, createPropfindResponse } from './webdav-api-test-utils'; +import { WebdavPrivateCfg } from './webdav.model'; describe('WebdavApi Capability Detection', () => { let api: WebdavApi; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-configuration.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-configuration.spec.ts index ea9117405..a2f15327b 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-configuration.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-configuration.spec.ts @@ -1,10 +1,7 @@ import { WebdavApi } from './webdav-api'; -import { - WebdavPrivateCfg, - WebdavServerType, - getRecommendedServerCapabilities, -} from './webdav'; import { createMockResponse } from './webdav-api-test-utils'; +import { WebdavPrivateCfg, WebdavServerType } from './webdav.model'; +import { getRecommendedServerCapabilities } from './getRecommendedServerCapabilities'; /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-connection.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-connection.spec.ts index 22dc440d7..9acbf0efc 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-connection.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-connection.spec.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; + +import { WebdavPrivateCfg } from './webdav.model'; describe('WebdavApi - Connection Testing', () => { let api: WebdavApi; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-download.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-download.spec.ts index 6a84f4b64..569a98062 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-download.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-download.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { HttpNotOkAPIError, RemoteFileNotFoundAPIError } from '../../../errors/errors'; +import { WebdavPrivateCfg } from './webdav.model'; describe('WebdavApi - Download Operations', () => { let api: WebdavApi; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-error-handling.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-error-handling.spec.ts index 4adf301c4..c098f11d4 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-error-handling.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-error-handling.spec.ts @@ -1,5 +1,4 @@ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { createMockResponse, createMockResponseFactory } from './webdav-api-test-utils'; import { NoRevAPIError, @@ -7,6 +6,7 @@ import { FileExistsAPIError, RemoteFileNotFoundAPIError, } from '../../../errors/errors'; +import { WebdavPrivateCfg } from './webdav.model'; /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-fallback.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-fallback.spec.ts index 4ef1039b9..87050190a 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-fallback.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-fallback.spec.ts @@ -1,11 +1,11 @@ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { createMockResponse, createMockResponseFactory, createPropfindResponse, } from './webdav-api-test-utils'; import { RemoteFileNotFoundAPIError } from '../../../errors/errors'; +import { WebdavPrivateCfg } from './webdav.model'; /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-fallbacks.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-fallbacks.spec.ts index 9ca106138..b9f9e70b9 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-fallbacks.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-fallbacks.spec.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { NoEtagAPIError, RemoteFileNotFoundAPIError } from '../../../errors/errors'; import { createMockResponseFactory } from './webdav-api-test-utils'; +import { WebdavPrivateCfg } from './webdav.model'; describe('WebdavApi - Fallback Paths', () => { let api: WebdavApi; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-file-operations.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-file-operations.spec.ts index af09de551..8afc3cf49 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-file-operations.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-file-operations.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { HttpNotOkAPIError, RemoteFileNotFoundAPIError } from '../../../errors/errors'; +import { WebdavPrivateCfg } from './webdav.model'; describe('WebdavApi - File Operations', () => { let api: WebdavApi; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-helpers.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-helpers.spec.ts index a61a0bece..35ac56b66 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-helpers.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-helpers.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { AuthFailSPError } from '../../../errors/errors'; +import { WebdavPrivateCfg } from './webdav.model'; describe('WebdavApi - Helper Methods', () => { let api: WebdavApi; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-integration.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-integration.spec.ts index 5c952d921..19733f310 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-integration.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-integration.spec.ts @@ -1,7 +1,7 @@ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { createMockResponse } from './webdav-api-test-utils'; import { RemoteFileNotFoundAPIError, NoEtagAPIError } from '../../../errors/errors'; +import { WebdavPrivateCfg } from './webdav.model'; /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-last-modified.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-last-modified.spec.ts index 6807a9c24..8849d5c8f 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-last-modified.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-last-modified.spec.ts @@ -1,5 +1,6 @@ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; + +import { WebdavPrivateCfg } from './webdav.model'; /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-metadata.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-metadata.spec.ts index d700e7cf7..48c05edc7 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-metadata.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-metadata.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { RemoteFileNotFoundAPIError, NoEtagAPIError } from '../../../errors/errors'; +import { WebdavPrivateCfg } from './webdav.model'; describe('WebdavApi - Metadata Operations', () => { let api: WebdavApi; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-safe-creation-simple.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-safe-creation-simple.spec.ts index 76e24a303..350b7d576 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-safe-creation-simple.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-safe-creation-simple.spec.ts @@ -1,5 +1,6 @@ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; + +import { WebdavPrivateCfg } from './webdav.model'; describe('WebdavApi Safe Creation Simple Test', () => { it('should be created', () => { diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-safe-creation.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-safe-creation.spec.ts index 25fc5444a..93d93de5f 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-safe-creation.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-safe-creation.spec.ts @@ -1,7 +1,7 @@ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { createMockResponse } from './webdav-api-test-utils'; import { FileExistsAPIError } from '../../../errors/errors'; +import { WebdavPrivateCfg } from './webdav.model'; /* eslint-disable @typescript-eslint/naming-convention */ diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-test-utils.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-test-utils.ts index d2abb2314..0baacc78b 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-test-utils.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-test-utils.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { WebdavPrivateCfg } from './webdav'; + +import { WebdavPrivateCfg } from './webdav.model'; export const createMockResponse = ( status: number, diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api-upload.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api-upload.spec.ts index 51824031a..08a862ba4 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api-upload.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api-upload.spec.ts @@ -1,12 +1,12 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { WebdavApi } from './webdav-api'; -import { WebdavPrivateCfg } from './webdav'; import { FileExistsAPIError, HttpNotOkAPIError, NoEtagAPIError, RemoteFileNotFoundAPIError, } from '../../../errors/errors'; +import { WebdavPrivateCfg } from './webdav.model'; describe('WebdavApi - Upload Operations', () => { let api: WebdavApi; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav.model.ts b/src/app/pfapi/api/sync/providers/webdav/webdav.model.ts new file mode 100644 index 000000000..8cbf2ca31 --- /dev/null +++ b/src/app/pfapi/api/sync/providers/webdav/webdav.model.ts @@ -0,0 +1,63 @@ +import { SyncProviderPrivateCfgBase } from '../../../pfapi.model'; + +export interface WebdavServerCapabilities { + /** Whether the server supports ETag headers for versioning */ + supportsETags: boolean; + /** Whether the server supports WebDAV If headers (RFC 4918) */ + supportsIfHeader: boolean; + /** Whether the server supports WebDAV LOCK/UNLOCK operations */ + supportsLocking: boolean; + /** Whether the server supports Last-Modified headers for versioning */ + supportsLastModified: boolean; +} + +export interface WebdavPrivateCfg extends SyncProviderPrivateCfgBase { + baseUrl: string; + userName: string; + password: string; + syncFolderPath: string; + + /** + * Server capabilities configuration. If not provided, capabilities will be + * detected automatically on first use. Providing this configuration can + * improve performance by skipping detection and ensure consistent behavior. + * + * Recommended settings for common servers: + * - Nextcloud/ownCloud: { supportsETags: true, supportsIfHeader: true, supportsLocking: true, supportsLastModified: true } + * - Apache mod_dav: { supportsETags: true, supportsIfHeader: false, supportsLocking: true, supportsLastModified: true } + * - Basic WebDAV: { supportsETags: false, supportsIfHeader: false, supportsLocking: false, supportsLastModified: true } + */ + serverCapabilities?: WebdavServerCapabilities; + + /** + * Force the use of Last-Modified headers instead of ETags, even if ETags are available. + * This can be useful for testing fallback behavior or working with servers that have + * unreliable ETag implementations. + */ + preferLastModified?: boolean; + + /** + * Compatibility mode for servers with limited WebDAV support. + * When enabled, disables conditional operations and safe creation mechanisms. + * Use only for very basic WebDAV servers that don't support any conditional headers. + */ + basicCompatibilityMode?: boolean; + + /** + * Maximum number of retry attempts for capability detection and fallback operations. + * Default: 2 + */ + maxRetries?: number; +} + +/** + * Server type enum for common WebDAV implementations + */ +export enum WebdavServerType { + NEXTCLOUD = 'nextcloud', + OWNCLOUD = 'owncloud', + APACHE_MOD_DAV = 'apache_mod_dav', + NGINX_DAV = 'nginx_dav', + BASIC_WEBDAV = 'basic_webdav', + CUSTOM = 'custom', +} diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav.spec.ts b/src/app/pfapi/api/sync/providers/webdav/webdav.spec.ts index 4b0a36326..966a8ee27 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav.spec.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav.spec.ts @@ -1,4 +1,4 @@ -import { Webdav, WebdavPrivateCfg } from './webdav'; +import { Webdav } from './webdav'; import { WebdavApi } from './webdav-api'; import { SyncProviderId } from '../../../pfapi.const'; import { SyncProviderPrivateCfgStore } from '../../sync-provider-private-cfg-store'; @@ -7,6 +7,7 @@ import { MissingCredentialsSPError, NoRevAPIError, } from '../../../errors/errors'; +import { WebdavPrivateCfg } from './webdav.model'; describe('Webdav', () => { let webdav: Webdav; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav.ts b/src/app/pfapi/api/sync/providers/webdav/webdav.ts index 9ac39b965..d905c4e43 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav.ts @@ -7,121 +7,7 @@ import { MissingCredentialsSPError, NoRevAPIError, } from '../../../errors/errors'; -import { SyncProviderPrivateCfgBase } from '../../../pfapi.model'; - -export interface WebdavServerCapabilities { - /** Whether the server supports ETag headers for versioning */ - supportsETags: boolean; - /** Whether the server supports WebDAV If headers (RFC 4918) */ - supportsIfHeader: boolean; - /** Whether the server supports WebDAV LOCK/UNLOCK operations */ - supportsLocking: boolean; - /** Whether the server supports Last-Modified headers for versioning */ - supportsLastModified: boolean; -} - -export interface WebdavPrivateCfg extends SyncProviderPrivateCfgBase { - baseUrl: string; - userName: string; - password: string; - syncFolderPath: string; - - /** - * Server capabilities configuration. If not provided, capabilities will be - * detected automatically on first use. Providing this configuration can - * improve performance by skipping detection and ensure consistent behavior. - * - * Recommended settings for common servers: - * - Nextcloud/ownCloud: { supportsETags: true, supportsIfHeader: true, supportsLocking: true, supportsLastModified: true } - * - Apache mod_dav: { supportsETags: true, supportsIfHeader: false, supportsLocking: true, supportsLastModified: true } - * - Basic WebDAV: { supportsETags: false, supportsIfHeader: false, supportsLocking: false, supportsLastModified: true } - */ - serverCapabilities?: WebdavServerCapabilities; - - /** - * Force the use of Last-Modified headers instead of ETags, even if ETags are available. - * This can be useful for testing fallback behavior or working with servers that have - * unreliable ETag implementations. - */ - preferLastModified?: boolean; - - /** - * Compatibility mode for servers with limited WebDAV support. - * When enabled, disables conditional operations and safe creation mechanisms. - * Use only for very basic WebDAV servers that don't support any conditional headers. - */ - basicCompatibilityMode?: boolean; - - /** - * Maximum number of retry attempts for capability detection and fallback operations. - * Default: 2 - */ - maxRetries?: number; -} - -/** - * Server type enum for common WebDAV implementations - */ -export enum WebdavServerType { - NEXTCLOUD = 'nextcloud', - OWNCLOUD = 'owncloud', - APACHE_MOD_DAV = 'apache_mod_dav', - NGINX_DAV = 'nginx_dav', - BASIC_WEBDAV = 'basic_webdav', - CUSTOM = 'custom', -} - -/** - * Helper function to get recommended server capabilities for common WebDAV server types - */ -export const getRecommendedServerCapabilities = ( - serverType: WebdavServerType, -): WebdavServerCapabilities => { - switch (serverType) { - case WebdavServerType.NEXTCLOUD: - case WebdavServerType.OWNCLOUD: - return { - supportsETags: true, - supportsIfHeader: true, - supportsLocking: true, - supportsLastModified: true, - }; - - case WebdavServerType.APACHE_MOD_DAV: - return { - supportsETags: true, - supportsIfHeader: false, // mod_dav has limited If header support - supportsLocking: true, - supportsLastModified: true, - }; - - case WebdavServerType.NGINX_DAV: - return { - supportsETags: false, // nginx dav module has limited ETag support - supportsIfHeader: false, - supportsLocking: false, - supportsLastModified: true, - }; - - case WebdavServerType.BASIC_WEBDAV: - return { - supportsETags: false, - supportsIfHeader: false, - supportsLocking: false, - supportsLastModified: true, - }; - - case WebdavServerType.CUSTOM: - default: - // Return null to trigger auto-detection - return { - supportsETags: false, - supportsIfHeader: false, - supportsLocking: false, - supportsLastModified: false, - }; - } -}; +import { WebdavPrivateCfg } from './webdav.model'; export class Webdav implements SyncProviderServiceInterface { private static readonly L = 'Webdav'; @@ -205,14 +91,13 @@ export class Webdav implements SyncProviderServiceInterface