@eclipse-docks/extension-webdav 0.7.71 → 0.7.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ extensionRegistry.registerExtension({
|
|
|
9
9
|
id: pkg.name,
|
|
10
10
|
name: t.EXT_WEBDAV_NAME,
|
|
11
11
|
description: t.EXT_WEBDAV_DESC,
|
|
12
|
-
loader: () => import("./webdav-extension-
|
|
12
|
+
loader: () => import("./webdav-extension-BnWGwdSi.js"),
|
|
13
13
|
icon: "cloud",
|
|
14
14
|
experimental: true
|
|
15
15
|
});
|
|
@@ -12,7 +12,13 @@ var WebDAVClient = class {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
async propfind(path, depth = 1) {
|
|
15
|
-
const
|
|
15
|
+
const response = await fetch(new URL(path, this.baseUrl).href, {
|
|
16
|
+
method: "PROPFIND",
|
|
17
|
+
headers: this.buildHeaders({
|
|
18
|
+
"Depth": depth.toString(),
|
|
19
|
+
"Content-Type": "application/xml"
|
|
20
|
+
}),
|
|
21
|
+
body: `<?xml version="1.0" encoding="UTF-8"?>
|
|
16
22
|
<d:propfind xmlns:d="DAV:">
|
|
17
23
|
<d:prop>
|
|
18
24
|
<d:displayname/>
|
|
@@ -22,14 +28,7 @@ var WebDAVClient = class {
|
|
|
22
28
|
<d:getlastmodified/>
|
|
23
29
|
<d:getetag/>
|
|
24
30
|
</d:prop>
|
|
25
|
-
</d:propfind
|
|
26
|
-
const response = await fetch(new URL(path, this.baseUrl).href, {
|
|
27
|
-
method: "PROPFIND",
|
|
28
|
-
headers: this.buildHeaders({
|
|
29
|
-
"Depth": depth.toString(),
|
|
30
|
-
"Content-Type": "application/xml"
|
|
31
|
-
}),
|
|
32
|
-
body: requestBody
|
|
31
|
+
</d:propfind>`
|
|
33
32
|
});
|
|
34
33
|
if (!response.ok) throw new Error(`WebDAV PROPFIND failed with status ${response.status}`);
|
|
35
34
|
const xml = await response.text();
|
|
@@ -732,4 +731,4 @@ function decodePassword(encoded) {
|
|
|
732
731
|
//#endregion
|
|
733
732
|
export { WebDAVClient, WebDAVDirectoryResource, WebDAVFileResource };
|
|
734
733
|
|
|
735
|
-
//# sourceMappingURL=webdav-extension-
|
|
734
|
+
//# sourceMappingURL=webdav-extension-BnWGwdSi.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webdav-extension-DQQKG1sl.js","names":[],"sources":["../src/webdav-client.ts","../src/webdav-filesys.ts","../src/webdav-connect.ts","../src/webdav-commands.ts","../src/webdav-extension.ts"],"sourcesContent":["export interface WebDAVConnectionInfo {\n url: string;\n username?: string;\n password?: string;\n /** Workspace root label (composite path prefix); defaults to webdav1, webdav2, … */\n name?: string;\n}\n\nexport interface WebDAVResource {\n href: string;\n displayName: string;\n isDirectory: boolean;\n contentType?: string;\n contentLength?: number;\n lastModified?: Date;\n etag?: string;\n}\n\nexport class WebDAVClient {\n private baseUrl: string;\n private authHeader?: string;\n\n constructor(connectionInfo: WebDAVConnectionInfo) {\n this.baseUrl = connectionInfo.url;\n if (connectionInfo.username !== undefined) {\n const raw = `${connectionInfo.username}:${connectionInfo.password ?? ''}`;\n this.authHeader = `Basic ${btoa(raw)}`;\n }\n }\n\n async propfind(path: string, depth: number = 1): Promise<WebDAVResource[]> {\n const requestBody = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <d:propfind xmlns:d=\"DAV:\">\n <d:prop>\n <d:displayname/>\n <d:resourcetype/>\n <d:getcontenttype/>\n <d:getcontentlength/>\n <d:getlastmodified/>\n <d:getetag/>\n </d:prop>\n </d:propfind>`;\n\n const response = await fetch(new URL(path, this.baseUrl).href, {\n method: 'PROPFIND',\n headers: this.buildHeaders({\n 'Depth': depth.toString(),\n 'Content-Type': 'application/xml'\n }),\n body: requestBody\n });\n\n if (!response.ok) {\n throw new Error(`WebDAV PROPFIND failed with status ${response.status}`);\n }\n\n const xml = await response.text();\n return this.parseMultiStatus(xml);\n }\n\n async getFile(path: string): Promise<Blob> {\n const response = await fetch(new URL(path, this.baseUrl).href, {\n method: 'GET',\n headers: this.buildHeaders()\n });\n if (!response.ok) {\n throw new Error(`WebDAV GET failed with status ${response.status}`);\n }\n return await response.blob();\n }\n\n async putFile(path: string, content: string | Blob | ArrayBuffer | ReadableStream): Promise<void> {\n let body: Blob | ArrayBuffer | string;\n if (content instanceof ReadableStream) {\n const res = new Response(content);\n body = await res.blob();\n } else {\n body = typeof content === 'string' ? new Blob([content]) : content;\n }\n const response = await fetch(new URL(path, this.baseUrl).href, {\n method: 'PUT',\n headers: this.buildHeaders({\n 'Content-Type': 'application/octet-stream'\n }),\n body\n });\n if (!response.ok) {\n throw new Error(`WebDAV PUT failed with status ${response.status}`);\n }\n }\n\n async deleteResource(path: string): Promise<void> {\n const response = await fetch(new URL(path, this.baseUrl).href, {\n method: 'DELETE',\n headers: this.buildHeaders()\n });\n if (!response.ok) {\n throw new Error(`WebDAV DELETE failed with status ${response.status}`);\n }\n }\n\n async createDirectory(path: string): Promise<void> {\n const response = await fetch(new URL(path, this.baseUrl).href, {\n method: 'MKCOL',\n headers: this.buildHeaders()\n });\n if (!response.ok) {\n throw new Error(`WebDAV MKCOL failed with status ${response.status}`);\n }\n }\n\n async moveResource(fromPath: string, toPath: string): Promise<void> {\n const destination = new URL(toPath, this.baseUrl).href;\n const response = await fetch(new URL(fromPath, this.baseUrl).href, {\n method: 'MOVE',\n headers: this.buildHeaders({\n 'Destination': destination,\n 'Overwrite': 'F'\n })\n });\n if (!response.ok) {\n throw new Error(`WebDAV MOVE failed with status ${response.status}`);\n }\n }\n\n async copyResource(fromPath: string, toPath: string): Promise<void> {\n const destination = new URL(toPath, this.baseUrl).href;\n const response = await fetch(new URL(fromPath, this.baseUrl).href, {\n method: 'COPY',\n headers: this.buildHeaders({\n 'Destination': destination,\n 'Overwrite': 'F'\n })\n });\n if (!response.ok) {\n throw new Error(`WebDAV COPY failed with status ${response.status}`);\n }\n }\n\n private buildHeaders(extra?: Record<string, string>): HeadersInit {\n const headers: Record<string, string> = { ...(extra ?? {}) };\n if (this.authHeader) {\n headers['Authorization'] = this.authHeader;\n }\n return headers;\n }\n\n private parseMultiStatus(xml: string): WebDAVResource[] {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'text/xml');\n const responses = doc.querySelectorAll('response');\n \n const resources: WebDAVResource[] = [];\n responses.forEach(response => {\n const hrefText = response.querySelector('href')?.textContent || '';\n const href = new URL(hrefText, this.baseUrl).href;\n const displayName = response.querySelector('displayname')?.textContent || '';\n const resourceType = response.querySelector('resourcetype');\n const isDirectory = !!resourceType?.querySelector('collection');\n const contentType = response.querySelector('getcontenttype')?.textContent || undefined;\n const contentLengthStr = response.querySelector('getcontentlength')?.textContent;\n const contentLength = contentLengthStr ? parseInt(contentLengthStr) : undefined;\n const lastModifiedStr = response.querySelector('getlastmodified')?.textContent;\n const lastModified = lastModifiedStr ? new Date(lastModifiedStr) : undefined;\n const etag = response.querySelector('getetag')?.textContent || undefined;\n\n resources.push({\n href,\n displayName: displayName || href.split('/').filter(Boolean).pop() || '',\n isDirectory,\n contentType,\n contentLength,\n lastModified,\n etag\n });\n });\n\n return resources;\n }\n\n getBaseUrl(): string {\n return this.baseUrl;\n }\n}\n\n\n","import {\n File,\n Directory,\n Resource,\n GetResourceOptions,\n FileContentsOptions,\n FileContentType,\n TOPIC_WORKSPACE_CHANGED,\n workspaceService\n} from '@eclipse-docks/core';\nimport { WebDAVClient, WebDAVResource, type WebDAVConnectionInfo } from './webdav-client';\nimport { publish } from '@eclipse-docks/core';\n\nexport class WebDAVFileResource extends File {\n private client: WebDAVClient;\n private resource: WebDAVResource;\n private parent: Directory;\n\n constructor(client: WebDAVClient, resource: WebDAVResource, parent: Directory) {\n super();\n this.client = client;\n this.resource = resource;\n this.parent = parent;\n }\n\n getName(): string {\n return this.resource.displayName;\n }\n\n getParent(): Directory {\n return this.parent;\n }\n\n async getContents(options?: FileContentsOptions): Promise<any> {\n const blob = await this.client.getFile(this.resource.href);\n \n if (!options || options?.contentType === FileContentType.TEXT) {\n return await blob.text();\n }\n\n if (options?.blob) {\n return blob;\n }\n\n if (options?.uri) {\n return URL.createObjectURL(blob);\n }\n\n return await blob.arrayBuffer();\n }\n\n async saveContents(contents: any, _options?: FileContentsOptions): Promise<void> {\n await this.client.putFile(this.resource.href, contents);\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n async size(): Promise<number | null> {\n return this.resource.contentLength ?? null;\n }\n\n async delete(): Promise<void> {\n await this.client.deleteResource(this.resource.href);\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n async copyTo(targetPath: string): Promise<void> {\n const targetFile = await this.getWorkspace().getResource(targetPath, { create: true }) as File;\n if (!targetFile) {\n throw new Error(`Failed to create target file: ${targetPath}`);\n }\n const contents = await this.getContents({ blob: true });\n await targetFile.saveContents(contents);\n }\n\n async rename(newName: string): Promise<void> {\n if (this.getName() === newName) {\n return;\n }\n\n const pathParts = this.resource.href.split('/');\n pathParts[pathParts.length - 1] = newName;\n const newPath = pathParts.join('/');\n \n await this.client.moveResource(this.resource.href, newPath);\n this.resource.href = newPath;\n this.resource.displayName = newName;\n \n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n}\n\nexport class WebDAVDirectoryResource extends Directory {\n private client: WebDAVClient;\n private resource: WebDAVResource;\n private parent?: Directory;\n private children?: Map<string, Resource>;\n private connectionInfo?: WebDAVConnectionInfo;\n /** Workspace root label only; nested dirs use {@link WebDAVResource.displayName}. */\n private rootFolderDisplayName?: string;\n\n constructor(client: WebDAVClient, resource: WebDAVResource, parent?: Directory, connectionInfo?: WebDAVConnectionInfo) {\n super();\n this.client = client;\n this.resource = resource;\n this.parent = parent;\n this.connectionInfo = connectionInfo;\n if (!parent) {\n const n = connectionInfo?.name?.trim();\n this.rootFolderDisplayName =\n n && n.length > 0 ? n : extractLeafNameFromUrl(connectionInfo?.url ?? resource.href);\n }\n }\n\n getName(): string {\n if (this.rootFolderDisplayName !== undefined) {\n return this.rootFolderDisplayName;\n }\n return this.resource.displayName;\n }\n\n getParent(): Directory | undefined {\n return this.parent;\n }\n\n async listChildren(forceRefresh: boolean = false): Promise<Resource[]> {\n if (forceRefresh || !this.children) {\n const resources = await this.client.propfind(this.resource.href, 1);\n this.children = new Map();\n\n // Skip first entry (it's the directory itself)\n for (let i = 1; i < resources.length; i++) {\n const res = resources[i];\n const child = res.isDirectory\n ? new WebDAVDirectoryResource(this.client, res, this)\n : new WebDAVFileResource(this.client, res, this);\n this.children.set(res.displayName, child);\n }\n }\n\n return Array.from(this.children.values());\n }\n\n async getResource(path: string, options?: GetResourceOptions): Promise<Resource | null> {\n if (!path) {\n throw new Error(\"No path provided\");\n }\n\n const isDirectoryIntent = path.endsWith(\"/\");\n const segments = path.split(\"/\").filter(s => s.trim());\n let currentResource: Resource = this;\n let workspaceChanged = false;\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i];\n \n if (currentResource instanceof WebDAVDirectoryResource) {\n await currentResource.listChildren();\n \n if (!currentResource.children) {\n return null;\n }\n\n let next = currentResource.children.get(segment);\n\n if (!next && options?.create) {\n const fullPath = this.buildPath(currentResource.resource.href, segment);\n \n // If not the last segment (or directory intent), create directory.\n if (i < segments.length - 1 || isDirectoryIntent) {\n await this.client.createDirectory(fullPath);\n const newResource: WebDAVResource = {\n href: fullPath,\n displayName: segment,\n isDirectory: true\n };\n next = new WebDAVDirectoryResource(this.client, newResource, currentResource);\n currentResource.children.set(segment, next);\n workspaceChanged = true;\n } else {\n // Last segment - create file\n await this.client.putFile(fullPath, '');\n const newResource: WebDAVResource = {\n href: fullPath,\n displayName: segment,\n isDirectory: false,\n contentLength: 0\n };\n next = new WebDAVFileResource(this.client, newResource, currentResource);\n currentResource.children.set(segment, next);\n workspaceChanged = true;\n }\n }\n\n if (!next) {\n return null;\n }\n\n if (i === segments.length - 1 && isDirectoryIntent && next instanceof WebDAVFileResource) {\n return null;\n }\n\n currentResource = next;\n }\n }\n\n if (workspaceChanged) {\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n return currentResource;\n }\n\n async delete(name?: string, _recursive: boolean = true): Promise<void> {\n if (!name) {\n return this.getParent()?.delete(this.getName());\n }\n\n const fullPath = this.buildPath(this.resource.href, name);\n await this.client.deleteResource(fullPath);\n this.children?.delete(name);\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n async copyTo(targetPath: string): Promise<void> {\n for (const resource of await this.listChildren()) {\n const targetResourceName = [targetPath, resource.getName()].join(\"/\");\n await resource.copyTo(targetResourceName);\n }\n }\n\n async rename(newName: string): Promise<void> {\n const trimmed = String(newName ?? '').trim();\n if (!trimmed || this.getName() === trimmed) {\n return;\n }\n\n if (!this.parent) {\n this.rootFolderDisplayName = trimmed;\n if (this.connectionInfo) {\n this.connectionInfo = { ...this.connectionInfo, name: trimmed };\n }\n await workspaceService.updateFolderName(this, trimmed);\n return;\n }\n\n const pathParts = this.resource.href.split('/').filter(Boolean);\n pathParts[pathParts.length - 1] = trimmed;\n const newPath = '/' + pathParts.join('/') + '/';\n\n await this.client.moveResource(this.resource.href, newPath);\n this.resource.href = newPath;\n this.resource.displayName = trimmed;\n\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n touch(): void {\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n private buildPath(basePath: string, segment: string): string {\n return basePath.endsWith('/') \n ? basePath + segment \n : basePath + '/' + segment;\n }\n\n getClient(): WebDAVClient {\n return this.client;\n }\n\n /**\n * Returns the connection info that was used to create this workspace root.\n * For non-root directories this may be undefined and we fall back to the base URL.\n */\n getConnectionInfo(): WebDAVConnectionInfo | undefined {\n if (this.connectionInfo) {\n return this.connectionInfo;\n }\n\n return {\n url: this.client.getBaseUrl()\n };\n }\n}\n\nfunction extractLeafNameFromUrl(url: string): string {\n try {\n const urlObj = new URL(url);\n const pathParts = urlObj.pathname.split('/').filter(Boolean);\n return pathParts[pathParts.length - 1] || 'workspace';\n } catch {\n return 'workspace';\n }\n}\n\n","import { customElement, state } from \"lit/decorators.js\";\nimport { DocksDialogContent } from \"@eclipse-docks/core\";\nimport { html } from \"lit\";\nimport { workspaceService } from \"@eclipse-docks/core\";\nimport type { WebDAVConnectionInfo } from \"./webdav-client\";\nimport { createLogger } from \"@eclipse-docks/core\";\n\nconst logger = createLogger('WebDAV');\n\n@customElement('docks-webdav-connect')\nexport class DocksWebDAVConnect extends DocksDialogContent {\n \n @state()\n private url = '';\n \n @state()\n private username = '';\n \n @state()\n private password = '';\n \n @state()\n private connecting = false;\n\n @state()\n private showHelp = false;\n\n public override getResult(): any {\n return this;\n }\n\n public async handleConnect(): Promise<boolean> {\n if (!this.url) {\n logger.error('Please provide a URL');\n return false;\n }\n\n // Validate URL format\n try {\n new URL(this.url);\n } catch {\n logger.error('Invalid URL format');\n return false;\n }\n\n this.connecting = true;\n \n try {\n let connectionInfo: WebDAVConnectionInfo;\n\n // Check if it's a Nextcloud/ownCloud public share URL\n const shareMatch = this.url.match(/^(https?:\\/\\/[^\\/]+)\\/(?:index\\.php\\/)?s\\/([A-Za-z0-9]+)/);\n\n if (shareMatch) {\n const server = shareMatch[1];\n const token = shareMatch[2];\n\n connectionInfo = {\n url: `${server}/public.php/webdav/`,\n username: token,\n password: this.password || ''\n };\n } else {\n // Direct WebDAV endpoint URL - use optional username/password fields\n connectionInfo = {\n url: this.url,\n ...(this.username && this.password && {\n username: this.username,\n password: this.password\n })\n };\n }\n\n await workspaceService.connectWorkspace(connectionInfo);\n logger.info('Successfully connected to WebDAV workspace');\n \n this.dispatchEvent(new CustomEvent('connected', {\n bubbles: true,\n composed: true\n }));\n return true;\n } catch (error) {\n if (error instanceof Error) {\n logger.error(`Connection failed: ${error.message}`);\n } else {\n logger.error('Failed to connect to WebDAV server');\n }\n return false;\n } finally {\n this.connecting = false;\n }\n }\n\n public toggleHelp() {\n this.showHelp = !this.showHelp;\n }\n\n protected render() {\n return html`\n <style>\n .webdav-connect-dialog {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n max-width: 500px;\n padding: 1.5rem;\n height: 420px;\n box-sizing: border-box;\n overflow-y: auto;\n }\n \n .webdav-connect-dialog h2 {\n margin: 0 0 1rem 0;\n font-size: 1.5rem;\n }\n \n .password-warning {\n font-size: 0.8rem;\n color: var(--wa-color-neutral-400);\n margin: 0.25rem 0 0.5rem 0;\n }\n \n .help-text {\n background: var(--wa-color-neutral-50);\n padding: 1rem;\n border-radius: 4px;\n font-size: 0.875rem;\n margin-top: 1rem;\n }\n \n .help-text h3 {\n margin-top: 0;\n font-size: 1rem;\n }\n \n .help-text p {\n margin: 0.5rem 0;\n }\n \n .help-text code {\n display: block;\n background: var(--wa-color-neutral-100);\n padding: 0.5rem;\n border-radius: 3px;\n margin: 0.5rem 0;\n font-family: monospace;\n word-break: breaall;\n }\n \n .help-toggle {\n cursor: pointer;\n color: var(--wa-color-primary-600);\n font-size: 0.875rem;\n text-decoration: underline;\n }\n </style>\n \n <div class=\"webdav-connect-dialog\">\n <wa-input\n label=\"WebDAV URL\"\n placeholder=\"https://cloud.example.com/remote.php/dav/files/username/\"\n .value=${this.url}\n @input=${(e: Event) => this.url = (e.target as any).value}\n required\n help-text=\"The full WebDAV endpoint URL\">\n </wa-input>\n \n <wa-input\n label=\"Username (optional)\"\n placeholder=\"username\"\n .value=${this.username}\n @input=${(e: Event) => this.username = (e.target as any).value}\n help-text=\"Leave empty for public/shared folders\">\n </wa-input>\n \n <wa-input\n type=\"password\"\n label=\"Password (optional)\"\n placeholder=\"Password or App Password\"\n .value=${this.password}\n @input=${(e: Event) => this.password = (e.target as any).value}\n help-text=\"Leave empty for public/shared folders. Use an app password if 2FA is enabled.\">\n </wa-input>\n <p class=\"password-warning\">\n Passwords are stored locally in this browser (base64 encoded). Only use this on machines you trust.\n </p>\n \n ${this.showHelp ? html`\n <div class=\"help-text\">\n <h3>WebDAV Connection Help</h3>\n \n <p><strong>Public/Shared Folders:</strong></p>\n <p>For publicly shared WebDAV folders, just enter the URL and leave username/password empty.</p>\n \n <p><strong>Nextcloud Public Shares:</strong></p>\n <p>For Nextcloud public shares (e.g., https://cloud.example.com/s/TOKEN):</p>\n <ul>\n <li><strong>URL:</strong> You can paste the share link directly into the URL field.</li>\n <li><strong>Username:</strong> Will be filled automatically from the share token.</li>\n <li><strong>Password:</strong> Leave empty (or enter the share password if the link is protected).</li>\n </ul>\n \n <p><strong>Nextcloud Personal Files:</strong></p>\n <p>Your WebDAV URL should look like:</p>\n <code>https://your-cloud.com/remote.php/dav/files/USERNAME/</code>\n \n <p><strong>ownCloud:</strong></p>\n <code>https://your-owncloud.com/remote.php/dav/files/USERNAME/</code>\n \n <p><strong>Two-Factor Authentication:</strong></p>\n <p>If you have 2FA enabled:</p>\n <ol>\n <li>Go to your account settings</li>\n <li>Find \"Security\" or \"App passwords\"</li>\n <li>Generate a new app password</li>\n <li>Use that password here instead of your regular password</li>\n </ol>\n \n <p><strong>CORS Issues:</strong></p>\n <p>If connection fails, your WebDAV server may need CORS configuration. \n Contact your administrator or check the server documentation.</p>\n \n <p><strong>Security note:</strong> Connection details (including password, if provided) are stored locally in this browser using base64 encoding. Only use this on machines and profiles you trust.</p>\n </div>\n ` : ''}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'docks-webdav-connect': DocksWebDAVConnect;\n }\n}\n","/**\n * Commands for WebDAV workspace integration\n */\n\nimport { registerAll, workspaceService, createLogger, dialogService } from \"@eclipse-docks/core\";\nimport type { WebDAVConnectionInfo } from \"./webdav-client\";\n\nconst logger = createLogger('WebDAV');\n\n/**\n * Universal WebDAV connection command\n * Handles Nextcloud shares, direct WebDAV URLs, and more\n */\nregisterAll({\n command: {\n \"id\": \"workspace.connect.webdav\",\n \"name\": \"WebDAV / NextCloud\",\n \"description\": \"Connect to WebDAV servers, Nextcloud shares, ownCloud, and other cloud storage\",\n \"parameters\": [\n {\n \"name\": \"url\",\n \"type\": \"string\",\n \"description\": \"WebDAV URL: share link (https://cloud.example.com/s/TOKEN) or direct endpoint\",\n \"required\": false\n },\n {\n \"name\": \"password\",\n \"type\": \"string\",\n \"description\": \"Password (if required)\",\n \"required\": false\n }\n ]\n },\n handler: {\n execute: async (context) => {\n const url = context.parameters?.url as string;\n const password = context.parameters?.password as string;\n\n // If no parameters provided, open the shared WebDAV connect dialog\n if (!url) {\n await dialogService.open(\"webdav-connect-dialog\");\n return;\n }\n\n try {\n let connectionInfo: WebDAVConnectionInfo;\n \n // Check if it's a Nextcloud/ownCloud share URL format\n const shareMatch = url.match(/^(https?:\\/\\/[^\\/]+)\\/(?:index\\.php\\/)?s\\/([A-Za-z0-9]+)/);\n \n if (shareMatch) {\n // It's a share URL - convert to WebDAV endpoint\n const server = shareMatch[1];\n const token = shareMatch[2];\n \n connectionInfo = {\n url: `${server}/public.php/webdav/`,\n username: token,\n password: password || ''\n };\n } else {\n // It's a direct WebDAV URL - use as-is\n connectionInfo = {\n url,\n ...(password && {\n username: '',\n password\n })\n };\n }\n\n await workspaceService.connectWorkspace(connectionInfo);\n logger.info('Connected to WebDAV workspace');\n } catch (error) {\n if (error instanceof Error) {\n logger.error(`Failed to connect: ${error.message}`);\n } else {\n logger.error('Failed to connect to WebDAV workspace');\n }\n }\n }\n },\n contribution: {\n target: \"filebrowser.connections\",\n name: \"filebrowser.connections.webdav\",\n label: \"WebDAV / NextCloud\",\n icon: \"cloud\"\n }\n});\n","/**\n * WebDAV Extension for geo!space\n * \n * This extension enables connecting to WebDAV servers (Nextcloud, ownCloud, etc.)\n * as workspace folders, providing cloud storage integration.\n * \n * Features:\n * - Connect to WebDAV servers\n * - Full file/directory operations\n * - Nextcloud and ownCloud support\n * - Seamless integration with existing workspace API\n * \n * Usage:\n * Import this file to register the WebDAV extension and its commands.\n */\n\nimport { workspaceService, createLogger, contributionRegistry } from '@eclipse-docks/core';\n\nconst logger = createLogger('WebDAVExtension');\nimport { WebDAVClient, type WebDAVConnectionInfo } from './webdav-client';\nimport { WebDAVDirectoryResource } from './webdav-filesys';\nimport type { WebDAVResource } from './webdav-client';\n\n// Export all WebDAV types and classes\nexport { WebDAVClient, type WebDAVConnectionInfo, type WebDAVResource } from './webdav-client';\nexport { WebDAVFileResource, WebDAVDirectoryResource } from './webdav-filesys';\n\n// Import UI component (registers itself)\nimport './webdav-connect';\nimport { html } from \"lit\";\nimport { DocksWebDAVConnect } from \"./webdav-connect\";\n\n// Import commands (registers themselves)\nimport './webdav-commands';\n\nasync function getNextWebdavWorkspaceName(): Promise<string> {\n const folders = await workspaceService.getFolders();\n const used = new Set(folders.filter((f) => f.type === 'webdav').map((f) => f.name));\n let n = 1;\n while (used.has(`webdav${n}`)) {\n n += 1;\n }\n return `webdav${n}`;\n}\n\n// Register WebDAV as a workspace contribution\nworkspaceService.registerContribution({\n type: 'webdav',\n name: 'webdav',\n\n canHandle(input: any): boolean {\n // Accept any connection info with a URL (credentials are optional for public/shared folders)\n return input && typeof input === 'object' && 'url' in input && typeof input.url === 'string';\n },\n\n async connect(input: WebDAVConnectionInfo) {\n const name =\n typeof input.name === 'string' && input.name.trim().length > 0\n ? input.name.trim()\n : await getNextWebdavWorkspaceName();\n const client = new WebDAVClient(input);\n const rootResource: WebDAVResource = {\n href: input.url,\n displayName: extractWorkspaceNameFromUrl(input.url),\n isDirectory: true\n };\n return new WebDAVDirectoryResource(client, rootResource, undefined, { ...input, name });\n },\n\n async restore(data: WebDAVConnectionInfo & { name?: string }) {\n if (!data || !data.url) {\n return undefined;\n }\n\n try {\n const restored: WebDAVConnectionInfo = {\n url: data.url,\n username: data.username,\n password: data.password ? decodePassword(data.password) : undefined\n };\n const name =\n typeof data.name === 'string' && data.name.trim().length > 0\n ? data.name.trim()\n : await getNextWebdavWorkspaceName();\n\n const client = new WebDAVClient(restored);\n const rootResource: WebDAVResource = {\n href: data.url,\n displayName: extractWorkspaceNameFromUrl(data.url),\n isDirectory: true\n };\n return new WebDAVDirectoryResource(client, rootResource, undefined, { ...restored, name });\n } catch (error) {\n logger.error('Failed to restore WebDAV workspace:', error);\n return undefined;\n }\n },\n\n async persist(workspace) {\n if (workspace instanceof WebDAVDirectoryResource) {\n const connectionInfo = workspace.getConnectionInfo();\n if (!connectionInfo) {\n return null;\n }\n\n return {\n url: connectionInfo.url,\n name: workspace.getName(),\n ...(connectionInfo.username !== undefined ? { username: connectionInfo.username } : {}),\n ...(connectionInfo.password !== undefined ? { password: encodePassword(connectionInfo.password) } : {})\n };\n }\n return null;\n }\n});\n\ninterface WebDAVDialogState {\n close?: () => void;\n}\n\ncontributionRegistry.registerContribution(\"dialogs\", {\n label: \"Connect to WebDAV / NextCloud\",\n icon: \"cloud\",\n name: \"dialog.webdav.connect\",\n id: \"webdav-connect-dialog\",\n buttons: [\n { id: \"help\", label: \"Show help\", variant: \"neutral\" },\n { id: \"cancel\", label: \"Cancel\", variant: \"default\" },\n { id: \"connect\", label: \"Connect\", variant: \"primary\" }\n ],\n component: (_state?: WebDAVDialogState) =>\n html`<docks-webdav-connect></docks-webdav-connect>`,\n onButton: async (id: string, result: any, state?: WebDAVDialogState) => {\n const component = result as DocksWebDAVConnect | undefined;\n\n if (id === \"help\") {\n component?.toggleHelp();\n return false;\n }\n\n if (id === \"connect\") {\n if (component?.handleConnect) {\n const ok = await component.handleConnect();\n if (ok) {\n state?.close?.();\n return true;\n }\n return false;\n }\n return true;\n }\n\n if (id === \"cancel\") {\n state?.close?.();\n return true;\n }\n\n return true;\n }\n} as any);\n\nfunction extractWorkspaceNameFromUrl(url: string): string {\n try {\n const urlObj = new URL(url);\n const pathParts = urlObj.pathname.split('/').filter(Boolean);\n return pathParts[pathParts.length - 1] || 'workspace';\n } catch {\n return 'workspace';\n }\n}\n\nfunction encodePassword(password: string): string {\n try {\n const encoder = new TextEncoder();\n const bytes = encoder.encode(password);\n let binary = '';\n for (const b of bytes) {\n binary += String.fromCharCode(b);\n }\n return btoa(binary);\n } catch {\n return password;\n }\n}\n\nfunction decodePassword(encoded: string): string {\n try {\n const binary = atob(encoded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const decoder = new TextDecoder();\n return decoder.decode(bytes);\n } catch {\n // If it's not valid base64 (e.g. older plain-text entries), return as-is\n return encoded;\n }\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webdav-extension-BnWGwdSi.js","names":[],"sources":["../src/webdav-client.ts","../src/webdav-filesys.ts","../src/webdav-connect.ts","../src/webdav-commands.ts","../src/webdav-extension.ts"],"sourcesContent":["export interface WebDAVConnectionInfo {\n url: string;\n username?: string;\n password?: string;\n /** Workspace root label (composite path prefix); defaults to webdav1, webdav2, … */\n name?: string;\n}\n\nexport interface WebDAVResource {\n href: string;\n displayName: string;\n isDirectory: boolean;\n contentType?: string;\n contentLength?: number;\n lastModified?: Date;\n etag?: string;\n}\n\nexport class WebDAVClient {\n private baseUrl: string;\n private authHeader?: string;\n\n constructor(connectionInfo: WebDAVConnectionInfo) {\n this.baseUrl = connectionInfo.url;\n if (connectionInfo.username !== undefined) {\n const raw = `${connectionInfo.username}:${connectionInfo.password ?? ''}`;\n this.authHeader = `Basic ${btoa(raw)}`;\n }\n }\n\n async propfind(path: string, depth: number = 1): Promise<WebDAVResource[]> {\n const requestBody = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <d:propfind xmlns:d=\"DAV:\">\n <d:prop>\n <d:displayname/>\n <d:resourcetype/>\n <d:getcontenttype/>\n <d:getcontentlength/>\n <d:getlastmodified/>\n <d:getetag/>\n </d:prop>\n </d:propfind>`;\n\n const response = await fetch(new URL(path, this.baseUrl).href, {\n method: 'PROPFIND',\n headers: this.buildHeaders({\n 'Depth': depth.toString(),\n 'Content-Type': 'application/xml'\n }),\n body: requestBody\n });\n\n if (!response.ok) {\n throw new Error(`WebDAV PROPFIND failed with status ${response.status}`);\n }\n\n const xml = await response.text();\n return this.parseMultiStatus(xml);\n }\n\n async getFile(path: string): Promise<Blob> {\n const response = await fetch(new URL(path, this.baseUrl).href, {\n method: 'GET',\n headers: this.buildHeaders()\n });\n if (!response.ok) {\n throw new Error(`WebDAV GET failed with status ${response.status}`);\n }\n return await response.blob();\n }\n\n async putFile(path: string, content: string | Blob | ArrayBuffer | ReadableStream): Promise<void> {\n let body: Blob | ArrayBuffer | string;\n if (content instanceof ReadableStream) {\n const res = new Response(content);\n body = await res.blob();\n } else {\n body = typeof content === 'string' ? new Blob([content]) : content;\n }\n const response = await fetch(new URL(path, this.baseUrl).href, {\n method: 'PUT',\n headers: this.buildHeaders({\n 'Content-Type': 'application/octet-stream'\n }),\n body\n });\n if (!response.ok) {\n throw new Error(`WebDAV PUT failed with status ${response.status}`);\n }\n }\n\n async deleteResource(path: string): Promise<void> {\n const response = await fetch(new URL(path, this.baseUrl).href, {\n method: 'DELETE',\n headers: this.buildHeaders()\n });\n if (!response.ok) {\n throw new Error(`WebDAV DELETE failed with status ${response.status}`);\n }\n }\n\n async createDirectory(path: string): Promise<void> {\n const response = await fetch(new URL(path, this.baseUrl).href, {\n method: 'MKCOL',\n headers: this.buildHeaders()\n });\n if (!response.ok) {\n throw new Error(`WebDAV MKCOL failed with status ${response.status}`);\n }\n }\n\n async moveResource(fromPath: string, toPath: string): Promise<void> {\n const destination = new URL(toPath, this.baseUrl).href;\n const response = await fetch(new URL(fromPath, this.baseUrl).href, {\n method: 'MOVE',\n headers: this.buildHeaders({\n 'Destination': destination,\n 'Overwrite': 'F'\n })\n });\n if (!response.ok) {\n throw new Error(`WebDAV MOVE failed with status ${response.status}`);\n }\n }\n\n async copyResource(fromPath: string, toPath: string): Promise<void> {\n const destination = new URL(toPath, this.baseUrl).href;\n const response = await fetch(new URL(fromPath, this.baseUrl).href, {\n method: 'COPY',\n headers: this.buildHeaders({\n 'Destination': destination,\n 'Overwrite': 'F'\n })\n });\n if (!response.ok) {\n throw new Error(`WebDAV COPY failed with status ${response.status}`);\n }\n }\n\n private buildHeaders(extra?: Record<string, string>): HeadersInit {\n const headers: Record<string, string> = { ...(extra ?? {}) };\n if (this.authHeader) {\n headers['Authorization'] = this.authHeader;\n }\n return headers;\n }\n\n private parseMultiStatus(xml: string): WebDAVResource[] {\n const parser = new DOMParser();\n const doc = parser.parseFromString(xml, 'text/xml');\n const responses = doc.querySelectorAll('response');\n \n const resources: WebDAVResource[] = [];\n responses.forEach(response => {\n const hrefText = response.querySelector('href')?.textContent || '';\n const href = new URL(hrefText, this.baseUrl).href;\n const displayName = response.querySelector('displayname')?.textContent || '';\n const resourceType = response.querySelector('resourcetype');\n const isDirectory = !!resourceType?.querySelector('collection');\n const contentType = response.querySelector('getcontenttype')?.textContent || undefined;\n const contentLengthStr = response.querySelector('getcontentlength')?.textContent;\n const contentLength = contentLengthStr ? parseInt(contentLengthStr) : undefined;\n const lastModifiedStr = response.querySelector('getlastmodified')?.textContent;\n const lastModified = lastModifiedStr ? new Date(lastModifiedStr) : undefined;\n const etag = response.querySelector('getetag')?.textContent || undefined;\n\n resources.push({\n href,\n displayName: displayName || href.split('/').filter(Boolean).pop() || '',\n isDirectory,\n contentType,\n contentLength,\n lastModified,\n etag\n });\n });\n\n return resources;\n }\n\n getBaseUrl(): string {\n return this.baseUrl;\n }\n}\n\n\n","import {\n File,\n Directory,\n Resource,\n GetResourceOptions,\n FileContentsOptions,\n FileContentType,\n TOPIC_WORKSPACE_CHANGED,\n workspaceService\n} from '@eclipse-docks/core';\nimport { WebDAVClient, WebDAVResource, type WebDAVConnectionInfo } from './webdav-client';\nimport { publish } from '@eclipse-docks/core';\n\nexport class WebDAVFileResource extends File {\n private client: WebDAVClient;\n private resource: WebDAVResource;\n private parent: Directory;\n\n constructor(client: WebDAVClient, resource: WebDAVResource, parent: Directory) {\n super();\n this.client = client;\n this.resource = resource;\n this.parent = parent;\n }\n\n getName(): string {\n return this.resource.displayName;\n }\n\n getParent(): Directory {\n return this.parent;\n }\n\n async getContents(options?: FileContentsOptions): Promise<any> {\n const blob = await this.client.getFile(this.resource.href);\n \n if (!options || options?.contentType === FileContentType.TEXT) {\n return await blob.text();\n }\n\n if (options?.blob) {\n return blob;\n }\n\n if (options?.uri) {\n return URL.createObjectURL(blob);\n }\n\n return await blob.arrayBuffer();\n }\n\n async saveContents(contents: any, _options?: FileContentsOptions): Promise<void> {\n await this.client.putFile(this.resource.href, contents);\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n async size(): Promise<number | null> {\n return this.resource.contentLength ?? null;\n }\n\n async delete(): Promise<void> {\n await this.client.deleteResource(this.resource.href);\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n async copyTo(targetPath: string): Promise<void> {\n const targetFile = await this.getWorkspace().getResource(targetPath, { create: true }) as File;\n if (!targetFile) {\n throw new Error(`Failed to create target file: ${targetPath}`);\n }\n const contents = await this.getContents({ blob: true });\n await targetFile.saveContents(contents);\n }\n\n async rename(newName: string): Promise<void> {\n if (this.getName() === newName) {\n return;\n }\n\n const pathParts = this.resource.href.split('/');\n pathParts[pathParts.length - 1] = newName;\n const newPath = pathParts.join('/');\n \n await this.client.moveResource(this.resource.href, newPath);\n this.resource.href = newPath;\n this.resource.displayName = newName;\n \n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n}\n\nexport class WebDAVDirectoryResource extends Directory {\n private client: WebDAVClient;\n private resource: WebDAVResource;\n private parent?: Directory;\n private children?: Map<string, Resource>;\n private connectionInfo?: WebDAVConnectionInfo;\n /** Workspace root label only; nested dirs use {@link WebDAVResource.displayName}. */\n private rootFolderDisplayName?: string;\n\n constructor(client: WebDAVClient, resource: WebDAVResource, parent?: Directory, connectionInfo?: WebDAVConnectionInfo) {\n super();\n this.client = client;\n this.resource = resource;\n this.parent = parent;\n this.connectionInfo = connectionInfo;\n if (!parent) {\n const n = connectionInfo?.name?.trim();\n this.rootFolderDisplayName =\n n && n.length > 0 ? n : extractLeafNameFromUrl(connectionInfo?.url ?? resource.href);\n }\n }\n\n getName(): string {\n if (this.rootFolderDisplayName !== undefined) {\n return this.rootFolderDisplayName;\n }\n return this.resource.displayName;\n }\n\n getParent(): Directory | undefined {\n return this.parent;\n }\n\n async listChildren(forceRefresh: boolean = false): Promise<Resource[]> {\n if (forceRefresh || !this.children) {\n const resources = await this.client.propfind(this.resource.href, 1);\n this.children = new Map();\n\n // Skip first entry (it's the directory itself)\n for (let i = 1; i < resources.length; i++) {\n const res = resources[i];\n const child = res.isDirectory\n ? new WebDAVDirectoryResource(this.client, res, this)\n : new WebDAVFileResource(this.client, res, this);\n this.children.set(res.displayName, child);\n }\n }\n\n return Array.from(this.children.values());\n }\n\n async getResource(path: string, options?: GetResourceOptions): Promise<Resource | null> {\n if (!path) {\n throw new Error(\"No path provided\");\n }\n\n const isDirectoryIntent = path.endsWith(\"/\");\n const segments = path.split(\"/\").filter(s => s.trim());\n let currentResource: Resource = this;\n let workspaceChanged = false;\n\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i];\n \n if (currentResource instanceof WebDAVDirectoryResource) {\n await currentResource.listChildren();\n \n if (!currentResource.children) {\n return null;\n }\n\n let next = currentResource.children.get(segment);\n\n if (!next && options?.create) {\n const fullPath = this.buildPath(currentResource.resource.href, segment);\n \n // If not the last segment (or directory intent), create directory.\n if (i < segments.length - 1 || isDirectoryIntent) {\n await this.client.createDirectory(fullPath);\n const newResource: WebDAVResource = {\n href: fullPath,\n displayName: segment,\n isDirectory: true\n };\n next = new WebDAVDirectoryResource(this.client, newResource, currentResource);\n currentResource.children.set(segment, next);\n workspaceChanged = true;\n } else {\n // Last segment - create file\n await this.client.putFile(fullPath, '');\n const newResource: WebDAVResource = {\n href: fullPath,\n displayName: segment,\n isDirectory: false,\n contentLength: 0\n };\n next = new WebDAVFileResource(this.client, newResource, currentResource);\n currentResource.children.set(segment, next);\n workspaceChanged = true;\n }\n }\n\n if (!next) {\n return null;\n }\n\n if (i === segments.length - 1 && isDirectoryIntent && next instanceof WebDAVFileResource) {\n return null;\n }\n\n currentResource = next;\n }\n }\n\n if (workspaceChanged) {\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n return currentResource;\n }\n\n async delete(name?: string, _recursive: boolean = true): Promise<void> {\n if (!name) {\n return this.getParent()?.delete(this.getName());\n }\n\n const fullPath = this.buildPath(this.resource.href, name);\n await this.client.deleteResource(fullPath);\n this.children?.delete(name);\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n async copyTo(targetPath: string): Promise<void> {\n for (const resource of await this.listChildren()) {\n const targetResourceName = [targetPath, resource.getName()].join(\"/\");\n await resource.copyTo(targetResourceName);\n }\n }\n\n async rename(newName: string): Promise<void> {\n const trimmed = String(newName ?? '').trim();\n if (!trimmed || this.getName() === trimmed) {\n return;\n }\n\n if (!this.parent) {\n this.rootFolderDisplayName = trimmed;\n if (this.connectionInfo) {\n this.connectionInfo = { ...this.connectionInfo, name: trimmed };\n }\n await workspaceService.updateFolderName(this, trimmed);\n return;\n }\n\n const pathParts = this.resource.href.split('/').filter(Boolean);\n pathParts[pathParts.length - 1] = trimmed;\n const newPath = '/' + pathParts.join('/') + '/';\n\n await this.client.moveResource(this.resource.href, newPath);\n this.resource.href = newPath;\n this.resource.displayName = trimmed;\n\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n touch(): void {\n publish(TOPIC_WORKSPACE_CHANGED, workspaceService.getWorkspaceSync() ?? this.getWorkspace());\n }\n\n private buildPath(basePath: string, segment: string): string {\n return basePath.endsWith('/') \n ? basePath + segment \n : basePath + '/' + segment;\n }\n\n getClient(): WebDAVClient {\n return this.client;\n }\n\n /**\n * Returns the connection info that was used to create this workspace root.\n * For non-root directories this may be undefined and we fall back to the base URL.\n */\n getConnectionInfo(): WebDAVConnectionInfo | undefined {\n if (this.connectionInfo) {\n return this.connectionInfo;\n }\n\n return {\n url: this.client.getBaseUrl()\n };\n }\n}\n\nfunction extractLeafNameFromUrl(url: string): string {\n try {\n const urlObj = new URL(url);\n const pathParts = urlObj.pathname.split('/').filter(Boolean);\n return pathParts[pathParts.length - 1] || 'workspace';\n } catch {\n return 'workspace';\n }\n}\n\n","import { customElement, state } from \"lit/decorators.js\";\nimport { DocksDialogContent } from \"@eclipse-docks/core\";\nimport { html } from \"lit\";\nimport { workspaceService } from \"@eclipse-docks/core\";\nimport type { WebDAVConnectionInfo } from \"./webdav-client\";\nimport { createLogger } from \"@eclipse-docks/core\";\n\nconst logger = createLogger('WebDAV');\n\n@customElement('docks-webdav-connect')\nexport class DocksWebDAVConnect extends DocksDialogContent {\n \n @state()\n private url = '';\n \n @state()\n private username = '';\n \n @state()\n private password = '';\n \n @state()\n private connecting = false;\n\n @state()\n private showHelp = false;\n\n public override getResult(): any {\n return this;\n }\n\n public async handleConnect(): Promise<boolean> {\n if (!this.url) {\n logger.error('Please provide a URL');\n return false;\n }\n\n // Validate URL format\n try {\n new URL(this.url);\n } catch {\n logger.error('Invalid URL format');\n return false;\n }\n\n this.connecting = true;\n \n try {\n let connectionInfo: WebDAVConnectionInfo;\n\n // Check if it's a Nextcloud/ownCloud public share URL\n const shareMatch = this.url.match(/^(https?:\\/\\/[^\\/]+)\\/(?:index\\.php\\/)?s\\/([A-Za-z0-9]+)/);\n\n if (shareMatch) {\n const server = shareMatch[1];\n const token = shareMatch[2];\n\n connectionInfo = {\n url: `${server}/public.php/webdav/`,\n username: token,\n password: this.password || ''\n };\n } else {\n // Direct WebDAV endpoint URL - use optional username/password fields\n connectionInfo = {\n url: this.url,\n ...(this.username && this.password && {\n username: this.username,\n password: this.password\n })\n };\n }\n\n await workspaceService.connectWorkspace(connectionInfo);\n logger.info('Successfully connected to WebDAV workspace');\n \n this.dispatchEvent(new CustomEvent('connected', {\n bubbles: true,\n composed: true\n }));\n return true;\n } catch (error) {\n if (error instanceof Error) {\n logger.error(`Connection failed: ${error.message}`);\n } else {\n logger.error('Failed to connect to WebDAV server');\n }\n return false;\n } finally {\n this.connecting = false;\n }\n }\n\n public toggleHelp() {\n this.showHelp = !this.showHelp;\n }\n\n protected render() {\n return html`\n <style>\n .webdav-connect-dialog {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n max-width: 500px;\n padding: 1.5rem;\n height: 420px;\n box-sizing: border-box;\n overflow-y: auto;\n }\n \n .webdav-connect-dialog h2 {\n margin: 0 0 1rem 0;\n font-size: 1.5rem;\n }\n \n .password-warning {\n font-size: 0.8rem;\n color: var(--wa-color-neutral-400);\n margin: 0.25rem 0 0.5rem 0;\n }\n \n .help-text {\n background: var(--wa-color-neutral-50);\n padding: 1rem;\n border-radius: 4px;\n font-size: 0.875rem;\n margin-top: 1rem;\n }\n \n .help-text h3 {\n margin-top: 0;\n font-size: 1rem;\n }\n \n .help-text p {\n margin: 0.5rem 0;\n }\n \n .help-text code {\n display: block;\n background: var(--wa-color-neutral-100);\n padding: 0.5rem;\n border-radius: 3px;\n margin: 0.5rem 0;\n font-family: monospace;\n word-break: breaall;\n }\n \n .help-toggle {\n cursor: pointer;\n color: var(--wa-color-primary-600);\n font-size: 0.875rem;\n text-decoration: underline;\n }\n </style>\n \n <div class=\"webdav-connect-dialog\">\n <wa-input\n label=\"WebDAV URL\"\n placeholder=\"https://cloud.example.com/remote.php/dav/files/username/\"\n .value=${this.url}\n @input=${(e: Event) => this.url = (e.target as any).value}\n required\n help-text=\"The full WebDAV endpoint URL\">\n </wa-input>\n \n <wa-input\n label=\"Username (optional)\"\n placeholder=\"username\"\n .value=${this.username}\n @input=${(e: Event) => this.username = (e.target as any).value}\n help-text=\"Leave empty for public/shared folders\">\n </wa-input>\n \n <wa-input\n type=\"password\"\n label=\"Password (optional)\"\n placeholder=\"Password or App Password\"\n .value=${this.password}\n @input=${(e: Event) => this.password = (e.target as any).value}\n help-text=\"Leave empty for public/shared folders. Use an app password if 2FA is enabled.\">\n </wa-input>\n <p class=\"password-warning\">\n Passwords are stored locally in this browser (base64 encoded). Only use this on machines you trust.\n </p>\n \n ${this.showHelp ? html`\n <div class=\"help-text\">\n <h3>WebDAV Connection Help</h3>\n \n <p><strong>Public/Shared Folders:</strong></p>\n <p>For publicly shared WebDAV folders, just enter the URL and leave username/password empty.</p>\n \n <p><strong>Nextcloud Public Shares:</strong></p>\n <p>For Nextcloud public shares (e.g., https://cloud.example.com/s/TOKEN):</p>\n <ul>\n <li><strong>URL:</strong> You can paste the share link directly into the URL field.</li>\n <li><strong>Username:</strong> Will be filled automatically from the share token.</li>\n <li><strong>Password:</strong> Leave empty (or enter the share password if the link is protected).</li>\n </ul>\n \n <p><strong>Nextcloud Personal Files:</strong></p>\n <p>Your WebDAV URL should look like:</p>\n <code>https://your-cloud.com/remote.php/dav/files/USERNAME/</code>\n \n <p><strong>ownCloud:</strong></p>\n <code>https://your-owncloud.com/remote.php/dav/files/USERNAME/</code>\n \n <p><strong>Two-Factor Authentication:</strong></p>\n <p>If you have 2FA enabled:</p>\n <ol>\n <li>Go to your account settings</li>\n <li>Find \"Security\" or \"App passwords\"</li>\n <li>Generate a new app password</li>\n <li>Use that password here instead of your regular password</li>\n </ol>\n \n <p><strong>CORS Issues:</strong></p>\n <p>If connection fails, your WebDAV server may need CORS configuration. \n Contact your administrator or check the server documentation.</p>\n \n <p><strong>Security note:</strong> Connection details (including password, if provided) are stored locally in this browser using base64 encoding. Only use this on machines and profiles you trust.</p>\n </div>\n ` : ''}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'docks-webdav-connect': DocksWebDAVConnect;\n }\n}\n","/**\n * Commands for WebDAV workspace integration\n */\n\nimport { registerAll, workspaceService, createLogger, dialogService } from \"@eclipse-docks/core\";\nimport type { WebDAVConnectionInfo } from \"./webdav-client\";\n\nconst logger = createLogger('WebDAV');\n\n/**\n * Universal WebDAV connection command\n * Handles Nextcloud shares, direct WebDAV URLs, and more\n */\nregisterAll({\n command: {\n \"id\": \"workspace.connect.webdav\",\n \"name\": \"WebDAV / NextCloud\",\n \"description\": \"Connect to WebDAV servers, Nextcloud shares, ownCloud, and other cloud storage\",\n \"parameters\": [\n {\n \"name\": \"url\",\n \"type\": \"string\",\n \"description\": \"WebDAV URL: share link (https://cloud.example.com/s/TOKEN) or direct endpoint\",\n \"required\": false\n },\n {\n \"name\": \"password\",\n \"type\": \"string\",\n \"description\": \"Password (if required)\",\n \"required\": false\n }\n ]\n },\n handler: {\n execute: async (context) => {\n const url = context.parameters?.url as string;\n const password = context.parameters?.password as string;\n\n // If no parameters provided, open the shared WebDAV connect dialog\n if (!url) {\n await dialogService.open(\"webdav-connect-dialog\");\n return;\n }\n\n try {\n let connectionInfo: WebDAVConnectionInfo;\n \n // Check if it's a Nextcloud/ownCloud share URL format\n const shareMatch = url.match(/^(https?:\\/\\/[^\\/]+)\\/(?:index\\.php\\/)?s\\/([A-Za-z0-9]+)/);\n \n if (shareMatch) {\n // It's a share URL - convert to WebDAV endpoint\n const server = shareMatch[1];\n const token = shareMatch[2];\n \n connectionInfo = {\n url: `${server}/public.php/webdav/`,\n username: token,\n password: password || ''\n };\n } else {\n // It's a direct WebDAV URL - use as-is\n connectionInfo = {\n url,\n ...(password && {\n username: '',\n password\n })\n };\n }\n\n await workspaceService.connectWorkspace(connectionInfo);\n logger.info('Connected to WebDAV workspace');\n } catch (error) {\n if (error instanceof Error) {\n logger.error(`Failed to connect: ${error.message}`);\n } else {\n logger.error('Failed to connect to WebDAV workspace');\n }\n }\n }\n },\n contribution: {\n target: \"filebrowser.connections\",\n name: \"filebrowser.connections.webdav\",\n label: \"WebDAV / NextCloud\",\n icon: \"cloud\"\n }\n});\n","/**\n * WebDAV Extension for geo!space\n * \n * This extension enables connecting to WebDAV servers (Nextcloud, ownCloud, etc.)\n * as workspace folders, providing cloud storage integration.\n * \n * Features:\n * - Connect to WebDAV servers\n * - Full file/directory operations\n * - Nextcloud and ownCloud support\n * - Seamless integration with existing workspace API\n * \n * Usage:\n * Import this file to register the WebDAV extension and its commands.\n */\n\nimport { workspaceService, createLogger, contributionRegistry } from '@eclipse-docks/core';\n\nconst logger = createLogger('WebDAVExtension');\nimport { WebDAVClient, type WebDAVConnectionInfo } from './webdav-client';\nimport { WebDAVDirectoryResource } from './webdav-filesys';\nimport type { WebDAVResource } from './webdav-client';\n\n// Export all WebDAV types and classes\nexport { WebDAVClient, type WebDAVConnectionInfo, type WebDAVResource } from './webdav-client';\nexport { WebDAVFileResource, WebDAVDirectoryResource } from './webdav-filesys';\n\n// Import UI component (registers itself)\nimport './webdav-connect';\nimport { html } from \"lit\";\nimport { DocksWebDAVConnect } from \"./webdav-connect\";\n\n// Import commands (registers themselves)\nimport './webdav-commands';\n\nasync function getNextWebdavWorkspaceName(): Promise<string> {\n const folders = await workspaceService.getFolders();\n const used = new Set(folders.filter((f) => f.type === 'webdav').map((f) => f.name));\n let n = 1;\n while (used.has(`webdav${n}`)) {\n n += 1;\n }\n return `webdav${n}`;\n}\n\n// Register WebDAV as a workspace contribution\nworkspaceService.registerContribution({\n type: 'webdav',\n name: 'webdav',\n\n canHandle(input: any): boolean {\n // Accept any connection info with a URL (credentials are optional for public/shared folders)\n return input && typeof input === 'object' && 'url' in input && typeof input.url === 'string';\n },\n\n async connect(input: WebDAVConnectionInfo) {\n const name =\n typeof input.name === 'string' && input.name.trim().length > 0\n ? input.name.trim()\n : await getNextWebdavWorkspaceName();\n const client = new WebDAVClient(input);\n const rootResource: WebDAVResource = {\n href: input.url,\n displayName: extractWorkspaceNameFromUrl(input.url),\n isDirectory: true\n };\n return new WebDAVDirectoryResource(client, rootResource, undefined, { ...input, name });\n },\n\n async restore(data: WebDAVConnectionInfo & { name?: string }) {\n if (!data || !data.url) {\n return undefined;\n }\n\n try {\n const restored: WebDAVConnectionInfo = {\n url: data.url,\n username: data.username,\n password: data.password ? decodePassword(data.password) : undefined\n };\n const name =\n typeof data.name === 'string' && data.name.trim().length > 0\n ? data.name.trim()\n : await getNextWebdavWorkspaceName();\n\n const client = new WebDAVClient(restored);\n const rootResource: WebDAVResource = {\n href: data.url,\n displayName: extractWorkspaceNameFromUrl(data.url),\n isDirectory: true\n };\n return new WebDAVDirectoryResource(client, rootResource, undefined, { ...restored, name });\n } catch (error) {\n logger.error('Failed to restore WebDAV workspace:', error);\n return undefined;\n }\n },\n\n async persist(workspace) {\n if (workspace instanceof WebDAVDirectoryResource) {\n const connectionInfo = workspace.getConnectionInfo();\n if (!connectionInfo) {\n return null;\n }\n\n return {\n url: connectionInfo.url,\n name: workspace.getName(),\n ...(connectionInfo.username !== undefined ? { username: connectionInfo.username } : {}),\n ...(connectionInfo.password !== undefined ? { password: encodePassword(connectionInfo.password) } : {})\n };\n }\n return null;\n }\n});\n\ninterface WebDAVDialogState {\n close?: () => void;\n}\n\ncontributionRegistry.registerContribution(\"dialogs\", {\n label: \"Connect to WebDAV / NextCloud\",\n icon: \"cloud\",\n name: \"dialog.webdav.connect\",\n id: \"webdav-connect-dialog\",\n buttons: [\n { id: \"help\", label: \"Show help\", variant: \"neutral\" },\n { id: \"cancel\", label: \"Cancel\", variant: \"default\" },\n { id: \"connect\", label: \"Connect\", variant: \"primary\" }\n ],\n component: (_state?: WebDAVDialogState) =>\n html`<docks-webdav-connect></docks-webdav-connect>`,\n onButton: async (id: string, result: any, state?: WebDAVDialogState) => {\n const component = result as DocksWebDAVConnect | undefined;\n\n if (id === \"help\") {\n component?.toggleHelp();\n return false;\n }\n\n if (id === \"connect\") {\n if (component?.handleConnect) {\n const ok = await component.handleConnect();\n if (ok) {\n state?.close?.();\n return true;\n }\n return false;\n }\n return true;\n }\n\n if (id === \"cancel\") {\n state?.close?.();\n return true;\n }\n\n return true;\n }\n} as any);\n\nfunction extractWorkspaceNameFromUrl(url: string): string {\n try {\n const urlObj = new URL(url);\n const pathParts = urlObj.pathname.split('/').filter(Boolean);\n return pathParts[pathParts.length - 1] || 'workspace';\n } catch {\n return 'workspace';\n }\n}\n\nfunction encodePassword(password: string): string {\n try {\n const encoder = new TextEncoder();\n const bytes = encoder.encode(password);\n let binary = '';\n for (const b of bytes) {\n binary += String.fromCharCode(b);\n }\n return btoa(binary);\n } catch {\n return password;\n }\n}\n\nfunction decodePassword(encoded: string): string {\n try {\n const binary = atob(encoded);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const decoder = new TextDecoder();\n return decoder.decode(bytes);\n } catch {\n // If it's not valid base64 (e.g. older plain-text entries), return as-is\n return encoded;\n }\n}"],"mappings":"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"}
|