aboutsummaryrefslogtreecommitdiff
path: root/modules/ext
diff options
context:
space:
mode:
authorAgando <roduccomm@gmail.com>2026-06-03 06:00:40 +0200
committerAgando <roduccomm@gmail.com>2026-06-03 06:00:40 +0200
commit7a3c8e74ecbf28d7921433430023f4ccaf8426dc (patch)
treec16ae627d307990cd049a6113ab2685826c29733 /modules/ext
parentaafb0fbde5289a45f9f818a00141f80b149be283 (diff)
auto: 2026-06-03_06:00 changes
Diffstat (limited to 'modules/ext')
-rw-r--r--modules/ext/webdav.js149
1 files changed, 106 insertions, 43 deletions
diff --git a/modules/ext/webdav.js b/modules/ext/webdav.js
index eea513b..01856ce 100644
--- a/modules/ext/webdav.js
+++ b/modules/ext/webdav.js
@@ -24,6 +24,27 @@ class WebDAVModule {
return '';
}
+ _detectSessionUser() {
+ try {
+ const cookies = document.cookie.split(';').map(c => c.trim());
+ for (const c of cookies) {
+ if (c.startsWith('webx11_user=')) {
+ return decodeURIComponent(c.substring('webx11_user='.length));
+ }
+ }
+ } catch (e) {}
+ return null;
+ }
+
+ _promptForToken(spaceName) {
+ // Use textarea+execCommand for mobile WebView compatibility
+ const input = window.prompt('Enter your WebDAV token for space "' + spaceName + '":', '');
+ if (input && input.trim()) {
+ return input.trim();
+ }
+ return null;
+ }
+
requirements() {
const cfg = localStorage.getItem('webx11_webdav_config');
if (!cfg) return { met: false, missing: ['WebDAV endpoint not configured. Use webdav-config to set it up.'] };
@@ -59,24 +80,33 @@ class WebDAVModule {
const saved = localStorage.getItem(this._STORAGE_KEY);
if (saved) {
this.config = JSON.parse(saved);
- // Try to get password from SecretVault
+ // Try to get password from SecretVault (async — will be resolved on first use)
if (window.webx11_secretVault && this.config && this.config.username) {
- let encryptedPass = window.webx11_secretVault.decrypt('webdav_pass_' + this.config.username);
- if (encryptedPass !== null && encryptedPass !== undefined) {
- this.config.password = encryptedPass;
- } else {
- // Fallback to old token key
- encryptedPass = window.webx11_secretVault.decrypt('webdav_token_' + this.config.username);
- if (encryptedPass !== null && encryptedPass !== undefined) {
- this.config.password = encryptedPass;
- }
- }
+ // Store a placeholder; actual decrypt happens on first auth call
+ this._cachedPassUser = this.config.username;
+ }
+ }
+ } catch (e) { /* ignore */ }
+ }
+
+ async _resolvePassword() {
+ if (!window.webx11_secretVault || !this._cachedPassUser) return;
+ try {
+ const pass = await window.webx11_secretVault.decrypt('webdav_pass_' + this._cachedPassUser);
+ if (pass !== null && typeof pass === 'string') {
+ this.config.password = pass;
+ } else {
+ // Fallback to old token key
+ const tokenPass = await window.webx11_secretVault.decrypt('webdav_token_' + this._cachedPassUser);
+ if (tokenPass !== null && typeof tokenPass === 'string') {
+ this.config.password = tokenPass;
}
}
} catch (e) { /* ignore */ }
+ this._cachedPassUser = null;
}
- _saveConfig() {
+ async _saveConfig() {
try {
if (this.config) {
const toStore = {
@@ -85,14 +115,13 @@ class WebDAVModule {
};
// Try SecretVault for encrypted password storage
if (window.webx11_secretVault && this.config.password) {
- window.webx11_secretVault.encrypt('webdav_pass_' + this.config.username, this.config.password);
+ await window.webx11_secretVault.encrypt('webdav_pass_' + this.config.username, this.config.password);
// Also store space token
if (this._spaceName) {
- window.webx11_secretVault.encrypt('webdav_token_' + this._spaceName, this.config.password);
+ await window.webx11_secretVault.encrypt('webdav_token_' + this._spaceName, this.config.password);
}
}
- // Also store in localStorage for backward compat (SecretVault decrypts later)
- // Store only url and username hint in plaintext lS, encrypted password handled above
+ // Also store in localStorage for backward compat (url and username only)
localStorage.setItem(this._STORAGE_KEY, JSON.stringify(toStore));
} else {
localStorage.removeItem(this._STORAGE_KEY);
@@ -110,6 +139,10 @@ class WebDAVModule {
if (!this.config || !this.config.url) {
throw new Error('WebDAV not configured. Use webdav-config first.');
}
+ // Resolve password from SecretVault if needed (lazy load)
+ if (this._cachedPassUser) {
+ await this._resolvePassword();
+ }
const baseUrl = this.config.url.replace(/\/+$/, '');
const urlPath = path ? (path.startsWith('/') ? path : '/' + path) : '/';
const url = baseUrl + urlPath;
@@ -372,7 +405,12 @@ class WebDAVModule {
// Check SecretVault first
if (window.webx11_secretVault) {
- token = window.webx11_secretVault.decrypt('webdav_token_' + spaceName);
+ const vaultToken = window.webx11_secretVault.decrypt('webdav_token_' + spaceName);
+ if (vaultToken && typeof vaultToken.then === 'function') {
+ token = await vaultToken;
+ } else if (vaultToken !== null && vaultToken !== undefined) {
+ token = vaultToken;
+ }
}
// Fallback to localStorage
@@ -380,18 +418,30 @@ class WebDAVModule {
token = localStorage.getItem('webx11_spaces_' + spaceName);
}
- // Fallback: fetch .token directly from server
+ // If still no token, use session-aware prompting
if (!token) {
- try {
- const tokenResp = await fetch('/spaces/' + spaceName + '/.token');
- if (tokenResp.ok) {
- token = (await tokenResp.text()).trim();
- // Cache in SecretVault for future use
- if (window.webx11_secretVault) {
- window.webx11_secretVault.encrypt('webdav_token_' + spaceName, token);
- }
- }
- } catch (e) { /* .token not accessible */ }
+ const sessionUser = self._detectSessionUser();
+ if (sessionUser && sessionUser === spaceName) {
+ self.terminal.print('👋 Welcome back, ' + sessionUser + '!', 'success');
+ self.terminal.print('No saved WebDAV config found. Enter your token:', 'info');
+ } else if (sessionUser) {
+ self.terminal.print('👋 You are logged in as "' + sessionUser + '" — this space is "' + spaceName + '".', 'info');
+ self.terminal.print('Enter the WebDAV token for space "' + spaceName + '":', 'info');
+ } else {
+ self.terminal.print('No saved WebDAV config found for space "' + spaceName + '".', 'info');
+ self.terminal.print('Enter the WebDAV token (get it from the WebX11 dashboard):', 'info');
+ }
+ self.terminal.print('', 'text-muted');
+ self.terminal.print(' webdav-config ' + autoUrl + ' ' + spaceName + ' <your-token>', 'success');
+ self.terminal.print('', 'text-muted');
+ // Pre-configure URL + username so user only needs token
+ self.config = {
+ url: autoUrl,
+ username: spaceName,
+ password: ''
+ };
+ await self._saveConfig();
+ return;
}
if (token) {
@@ -400,7 +450,7 @@ class WebDAVModule {
username: spaceName,
password: token
};
- self._saveConfig();
+ await self._saveConfig();
self._autoConfigured = true;
self.terminal.print('Auto-configured WebDAV for space: ' + spaceName, 'success');
try {
@@ -413,7 +463,7 @@ class WebDAVModule {
}
} else {
self.terminal.print('No WebDAV token found for space: ' + spaceName, 'warning');
- self.terminal.print('Get your token from /spaces/' + spaceName + '/.token or the dashboard.', 'info');
+ self.terminal.print('Get your token from the WebX11 dashboard.', 'info');
self.terminal.print('Auto-setting URL: ' + autoUrl, 'info');
self.terminal.print('Then configure with: webdav-config ' + autoUrl + ' ' + spaceName + ' <your-token>', 'info');
// At minimum set the URL
@@ -422,7 +472,7 @@ class WebDAVModule {
username: spaceName,
password: ''
};
- self._saveConfig();
+ await self._saveConfig();
self._autoConfigured = true;
}
return;
@@ -434,7 +484,7 @@ class WebDAVModule {
username: args[1] || (self.config ? self.config.username : ''),
password: args[2] || (self.config ? self.config.password : '')
};
- self._saveConfig();
+ await self._saveConfig();
self.terminal.print('Testing WebDAV connection...', 'info');
try {
const resp = await self._webdavRequest('OPTIONS', '/');
@@ -447,11 +497,15 @@ class WebDAVModule {
};
// --- webdav-status: show current WebDAV config ---
- this.commands['webdav-status'] = () => {
+ this.commands['webdav-status'] = async () => {
if (!self.config || !self.config.url) {
self.terminal.print('WebDAV not configured. Use webdav-config <url> [username] [password]', 'warning');
return;
}
+ // Resolve password from SecretVault if needed
+ if (self._cachedPassUser) {
+ await self._resolvePassword();
+ }
self.terminal.print('WebDAV Status:', 'text-muted');
self.terminal.print(' URL: ' + self.config.url);
self.terminal.print(' User: ' + (self.config.username || '(none)'));
@@ -1197,24 +1251,31 @@ class WebDAVModule {
async previewWebdavFile(entry) {
try {
- var resp = await this._webdavRequest('GET', entry.href);
+ var basePath = (this._browsingPath || '/').replace(/\/+$/, '');
+ var fullPath = basePath + '/' + entry.name;
+ var resp = await this._webdavRequest('GET', fullPath);
if (!resp.ok) { this.terminal.print('Failed to fetch: HTTP ' + resp.status, 'error'); return; }
- var content = await resp.text();
var isHtml = entry.name.match(/\.html?$/i);
var isImage = entry.name.match(/\.(png|jpg|jpeg|gif|svg|webp)$/i);
- var size = content.length;
+ var size = parseInt(resp.headers.get('content-length') || '0');
if (size > 512000 && !isHtml && !isImage) {
// Large non-html file — offer download instead
this.terminal.print(entry.name + ' is ' + this._formatSize(size) + ' — double-click to download instead of preview.', 'warning');
+ var textPreview = await resp.text();
this.terminal.print('--- ' + entry.name + ' (first 2000 chars) ---');
- this.terminal.print(content.substring(0, 2000));
- if (content.length > 2000) this.terminal.print('...(truncated ' + this._formatSize(size) + ' total)');
+ this.terminal.print(textPreview.substring(0, 2000));
+ if (textPreview.length > 2000) this.terminal.print('...(truncated ' + this._formatSize(size) + ' total)');
return;
}
if (isImage) {
- var dataUrl = 'data:image/' + (entry.name.split('.').pop()) + ';base64,' + btoa(unescape(encodeURIComponent(content)));
+ var blob = await resp.blob();
+ var dataUrl = await new Promise(function(resolve) {
+ var reader = new FileReader();
+ reader.onloadend = function() { resolve(reader.result); };
+ reader.readAsDataURL(blob);
+ });
// Use preview modal
var previewTitle = document.getElementById('preview-title');
var previewIframe = document.getElementById('preview-iframe');
@@ -1227,6 +1288,7 @@ class WebDAVModule {
}
if (isHtml) {
+ var content = await resp.text();
var previewTitle = document.getElementById('preview-title');
var previewIframe = document.getElementById('preview-iframe');
if (previewTitle && previewIframe) {
@@ -1238,7 +1300,8 @@ class WebDAVModule {
}
// Text file — print to terminal
- this.terminal.print('--- ' + entry.name + ' (' + this._formatSize(size) + ') ---', 'info');
+ var content = await resp.text();
+ this.terminal.print('--- ' + entry.name + ' (' + this._formatSize(content.length) + ') ---', 'info');
this.terminal.print(content);
this.terminal.print('--- End: ' + entry.name + ' ---', 'text-muted');
} catch (e) {
@@ -1286,8 +1349,8 @@ class WebDAVModule {
var fullPath = basePath + '/' + entry.name;
var resp = await this._webdavRequest('GET', fullPath);
if (!resp.ok) { this.terminal.print('Download failed: HTTP ' + resp.status, 'error'); return; }
- var content = await resp.text();
- var blob = new Blob([content], { type: 'application/octet-stream' });
+ // Use blob() instead of text() to preserve binary data
+ var blob = await resp.blob();
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
@@ -1299,7 +1362,7 @@ class WebDAVModule {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 200);
- this.terminal.print('⬇ Downloaded: ' + entry.name + ' (' + this._formatSize(content.length) + ')', 'success');
+ this.terminal.print('⬇ Downloaded: ' + entry.name + ' (' + this._formatSize(blob.size) + ')', 'success');
} catch (e) {
this.terminal.print('Download error: ' + e.message, 'error');
}