From 3b850d6467efdb289c73851d65e295407adb8bcf Mon Sep 17 00:00:00 2001 From: FTCHD Date: Tue, 28 Jan 2025 13:24:55 +0200 Subject: [PATCH] add auth, new password per session --- lib/menu.ts | 481 +++++++++++++++++++++------------------------ lib/rclone/api.ts | 77 ++++---- lib/store.ts | 4 + main.ts | 17 +- public/browse.html | 18 +- 5 files changed, 298 insertions(+), 299 deletions(-) diff --git a/lib/menu.ts b/lib/menu.ts index 4d43196..6968703 100644 --- a/lib/menu.ts +++ b/lib/menu.ts @@ -5,8 +5,8 @@ import { sendNotification } from '@tauri-apps/plugin-notification' import { revealItemInDir } from '@tauri-apps/plugin-opener' import { exit } from '@tauri-apps/plugin-process' import { isDirectoryEmpty } from './fs' -import { deleteRemote, mountRemote, unmountRemote } from './rclone/api' -import { dialogGetMountPlugin, needsMountPlugin } from './rclone/mount' +import { deleteRemote, mountRemote } from './rclone/api' +import { dialogGetMountPlugin, needsMountPlugin, unmountRemote } from './rclone/mount' import { usePersistedStore, useStore } from './store' import { getLoadingTray, getMainTray, rebuildTrayMenu } from './tray' import { lockWindows, openFullWindow, openTrayWindow, openWindow, unlockWindows } from './window' @@ -21,227 +21,235 @@ export async function buildMenu() { const menuItems: (MenuItem | Submenu | PredefinedMenuItem)[] = [] - // Add remote submenus - for (const remote of remotes) { - const remoteConfig = persistedStoreState.remoteConfigList?.[remote] - if (remoteConfig?.hideTray) { - continue - } - - const submenuItems: (MenuItem | Submenu | PredefinedMenuItem)[] = [] - - const alreadyMounted = storeState.mountedRemotes[remote] - - if (alreadyMounted) { - const unmountMenuItem = await MenuItem.new({ - id: `unmount-${remote}`, - text: 'Unmount', - action: async () => { - try { - const mountPoint = storeState.mountedRemotes[remote] - if (!mountPoint) { - console.error(`No mount point found for remote ${remote}`) - return - } - await unmountRemote(mountPoint) - delete storeState.mountedRemotes[remote] - await rebuildTrayMenu() - await message(`Successfully unmounted ${remote} from ${mountPoint}`, { - title: 'Unmount Success', - }) - } catch (err) { - console.error('Unmount operation failed:', err) - await message(`Failed to unmount ${remote}: ${err}`, { - title: 'Unmount Error', - }) - } - }, - }) - submenuItems.push(unmountMenuItem) - - // Add "Open in Finder" option for mounted remotes - const mountPoint = storeState.mountedRemotes[remote] - console.log('Adding Open in Finder', mountPoint) - const openInFinderItem = await MenuItem.new({ - id: `open-${remote}`, - - text: 'Open in Finder', - action: async () => { - console.log('Open in Finder') - console.log(mountPoint) - if (mountPoint) { - console.log('Opening in Finder') - await revealItemInDir(mountPoint) - console.log('Opened in Finder') - } - }, - }) - submenuItems.push(openInFinderItem) - } - - if (!alreadyMounted && !remoteConfig?.disabledActions?.includes('mount')) { - const mountMenuItem = await MenuItem.new({ - id: `mount-${remote}`, - text: 'Quick Mount', - action: async () => { - await getMainTray().then((t) => t?.setVisible(false)) - - await getLoadingTray().then((t) => t?.setVisible(true)) - - try { - const needsPlugin = await needsMountPlugin() - if (needsPlugin) { - console.log('Mount plugin not installed') - await dialogGetMountPlugin() - return - } - console.log('Mount plugin installed') - - await lockWindows() - - console.log('remoteConfig', remoteConfig) - - let selectedPath = remoteConfig?.defaultMountPoint || null - - if (!selectedPath) { - selectedPath = await open({ - title: `Select a directory to mount "${remote}"`, - multiple: false, - directory: true, - }) - } - - if (!selectedPath) { - // await resetMainWindow() - return - } - - console.log('selectedPath', selectedPath) - - // Check if directory is empty - const isEmpty = await isDirectoryEmpty(selectedPath) - if (!isEmpty) { - // await resetMainWindow() - - await message( - 'The selected directory must be empty to mount a remote.', - { - title: 'Mount Error', - kind: 'error', - } - ) - - return - } - - // Mount the remote - await mountRemote({ - remotePath: `${remote}:${remoteConfig?.defaultRemotePath || ''}`, - mountPoint: selectedPath, - mountOptions: remoteConfig?.mountDefaults, - vfsOptions: remoteConfig?.vfsDefaults, - }) - storeState.mountedRemotes[remote] = selectedPath - - let permissionGranted = await isPermissionGranted() - - if (!permissionGranted) { - const permission = await requestPermission() - permissionGranted = permission === 'granted' - } - - if (permissionGranted) { - sendNotification({ - title: 'Mounted', - body: `Successfully mounted ${remote} to ${selectedPath}`, - }) - } - - if (!remoteConfig?.defaultMountPoint) { - const answer = await ask( - `Mount successful! Do you want to set ${selectedPath} as the default mount point for ${remote}? You can always change it later in Remote settings.`, - { - title: 'Set Default?', - okLabel: 'Set', - cancelLabel: 'Cancel', - } - ) - if (answer) { - usePersistedStore.setState((state) => ({ - remoteConfigList: { - ...state.remoteConfigList, - [remote]: { - ...state.remoteConfigList[remote], - defaultMountPoint: selectedPath, - }, - }, - })) - } - } - - await rebuildTrayMenu() - } catch (err) { - // await resetMainWindow() - console.error('Mount operation failed:', err) - await message(`Failed to mount ${remote}: ${err}`, { - title: 'Mount Error', - }) - } finally { - await unlockWindows() - await getLoadingTray().then((t) => t?.setVisible(false)) - await getMainTray().then((t) => t?.setVisible(true)) - } - }, - }) - - submenuItems.push(mountMenuItem) - } - - if (!remoteConfig?.disabledActions?.includes('browse')) { - const browseMenuItem = await MenuItem.new({ - id: `browse-${remote}`, - text: 'Browse', - action: async () => { - // await openBrowser(`http://localhost:5572/[${remote}:]/`) - await openFullWindow({ - name: 'Browse', - // url: 'browse.html?url=https%3A%2F%2Fwww.google.com%2F', - url: - 'browse.html?url=' + - encodeURIComponent(`http://localhost:5572/[${remote}:]/`), - }) - }, - }) - submenuItems.push(browseMenuItem) - } - - if (!remoteConfig?.disabledActions?.includes('remove')) { - const removeMenuItem = await MenuItem.new({ - id: `remove-${remote}`, - text: 'Remove', - action: async () => { - const confirmation = await confirm( - `Are you sure you want to remove ${remote}? This action cannot be reverted.`, - { title: `Removing ${remote}`, kind: 'warning' } - ) - - if (!confirmation) { - return - } - - await deleteRemote(remote) - // await rebuildTrayMenu() - }, - }) - submenuItems.push(removeMenuItem) - } - - const sub = await Submenu.new({ - items: submenuItems, - text: remote, + if (remotes.length === 0) { + const noRemotesMenuItem = await MenuItem.new({ + id: 'no-remotes', + text: 'No Remotes', + enabled: false, }) + menuItems.push(noRemotesMenuItem) + } else { + // Add remote submenus + for (const remote of remotes) { + const remoteConfig = persistedStoreState.remoteConfigList?.[remote] + if (remoteConfig?.hideTray) { + continue + } - menuItems.push(sub) + const submenuItems: (MenuItem | Submenu | PredefinedMenuItem)[] = [] + + const alreadyMounted = storeState.mountedRemotes[remote] + + if (alreadyMounted) { + const unmountMenuItem = await MenuItem.new({ + id: `unmount-${remote}`, + text: 'Unmount', + action: async () => { + try { + const mountPoint = storeState.mountedRemotes[remote] + if (!mountPoint) { + console.error(`No mount point found for remote ${remote}`) + return + } + await unmountRemote(mountPoint) + delete storeState.mountedRemotes[remote] + await rebuildTrayMenu() + await message(`Successfully unmounted ${remote} from ${mountPoint}`, { + title: 'Unmount Success', + }) + } catch (err) { + console.error('Unmount operation failed:', err) + await message(`Failed to unmount ${remote}: ${err}`, { + title: 'Unmount Error', + }) + } + }, + }) + submenuItems.push(unmountMenuItem) + + // Add "Open in Finder" option for mounted remotes + const mountPoint = storeState.mountedRemotes[remote] + console.log('Adding Open in Finder', mountPoint) + const openInFinderItem = await MenuItem.new({ + id: `open-${remote}`, + + text: 'Open in Finder', + action: async () => { + console.log('Open in Finder') + console.log(mountPoint) + if (mountPoint) { + console.log('Opening in Finder') + await revealItemInDir(mountPoint) + console.log('Opened in Finder') + } + }, + }) + submenuItems.push(openInFinderItem) + } + + if (!alreadyMounted && !remoteConfig?.disabledActions?.includes('mount')) { + const mountMenuItem = await MenuItem.new({ + id: `mount-${remote}`, + text: 'Quick Mount', + action: async () => { + await getMainTray().then((t) => t?.setVisible(false)) + + await getLoadingTray().then((t) => t?.setVisible(true)) + + try { + const needsPlugin = await needsMountPlugin() + if (needsPlugin) { + console.log('Mount plugin not installed') + await dialogGetMountPlugin() + return + } + console.log('Mount plugin installed') + + await lockWindows() + + console.log('remoteConfig', remoteConfig) + + let selectedPath = remoteConfig?.defaultMountPoint || null + + if (!selectedPath) { + selectedPath = await open({ + title: `Select a directory to mount "${remote}"`, + multiple: false, + directory: true, + }) + } + + if (!selectedPath) { + // await resetMainWindow() + return + } + + console.log('selectedPath', selectedPath) + + // Check if directory is empty + const isEmpty = await isDirectoryEmpty(selectedPath) + if (!isEmpty) { + // await resetMainWindow() + + await message( + 'The selected directory must be empty to mount a remote.', + { + title: 'Mount Error', + kind: 'error', + } + ) + + return + } + + // Mount the remote + await mountRemote({ + remotePath: `${remote}:${remoteConfig?.defaultRemotePath || ''}`, + mountPoint: selectedPath, + mountOptions: remoteConfig?.mountDefaults, + vfsOptions: remoteConfig?.vfsDefaults, + }) + storeState.mountedRemotes[remote] = selectedPath + + let permissionGranted = await isPermissionGranted() + + if (!permissionGranted) { + const permission = await requestPermission() + permissionGranted = permission === 'granted' + } + + if (permissionGranted) { + sendNotification({ + title: 'Mounted', + body: `Successfully mounted ${remote} to ${selectedPath}`, + }) + } + + if (!remoteConfig?.defaultMountPoint) { + const answer = await ask( + `Mount successful! Do you want to set ${selectedPath} as the default mount point for ${remote}? You can always change it later in Remote settings.`, + { + title: 'Set Default?', + okLabel: 'Set', + cancelLabel: 'Cancel', + } + ) + if (answer) { + usePersistedStore.setState((state) => ({ + remoteConfigList: { + ...state.remoteConfigList, + [remote]: { + ...state.remoteConfigList[remote], + defaultMountPoint: selectedPath, + }, + }, + })) + } + } + + await rebuildTrayMenu() + } catch (err) { + // await resetMainWindow() + console.error('Mount operation failed:', err) + await message(`Failed to mount ${remote}: ${err}`, { + title: 'Mount Error', + }) + } finally { + await unlockWindows() + await getLoadingTray().then((t) => t?.setVisible(false)) + await getMainTray().then((t) => t?.setVisible(true)) + } + }, + }) + + submenuItems.push(mountMenuItem) + } + + if (!remoteConfig?.disabledActions?.includes('browse')) { + const browseMenuItem = await MenuItem.new({ + id: `browse-${remote}`, + text: 'Browse', + action: async () => { + // await openBrowser(`http://localhost:5572/[${remote}:]/`) + await openFullWindow({ + name: 'Browse', + url: + 'browse.html?url=' + + encodeURIComponent(`http://localhost:5572/[${remote}:]/`), + }) + }, + }) + submenuItems.push(browseMenuItem) + } + + if (!remoteConfig?.disabledActions?.includes('remove')) { + const removeMenuItem = await MenuItem.new({ + id: `remove-${remote}`, + text: 'Remove', + action: async () => { + const confirmation = await confirm( + `Are you sure you want to remove ${remote}? This action cannot be reverted.`, + { title: `Removing ${remote}`, kind: 'warning' } + ) + + if (!confirmation) { + return + } + + await deleteRemote(remote) + // await rebuildTrayMenu() + }, + }) + submenuItems.push(removeMenuItem) + } + + const sub = await Submenu.new({ + items: submenuItems, + text: remote, + }) + + menuItems.push(sub) + } } await PredefinedMenuItem.new({ @@ -320,7 +328,6 @@ export async function buildMenu() { }) }, }) - menuItems.push(settingsItem) const quitItem = await MenuItem.new({ @@ -330,40 +337,10 @@ export async function buildMenu() { await exit(0) }, }) - menuItems.push(quitItem) - const testItem = await MenuItem.new({ - id: 'test', - text: 'Test', - action: async () => { - await openWindow({ - name: 'Test', - url: '/test', - width: 400, - height: 400, - }) - }, - }) - menuItems.push(testItem) - - const test2Item = await MenuItem.new({ - id: 'test2', - text: 'Test2', - action: async () => { - await openWindow({ - name: 'Test2', - url: '/test', - width: 400, - height: 400, - }) - }, - }) - menuItems.push(test2Item) - return await Menu.new({ id: 'main-menu', - items: menuItems, }) } diff --git a/lib/rclone/api.ts b/lib/rclone/api.ts index f74159e..c44347c 100644 --- a/lib/rclone/api.ts +++ b/lib/rclone/api.ts @@ -1,12 +1,29 @@ -import { ask } from '@tauri-apps/plugin-dialog' import { fetch } from '@tauri-apps/plugin-http' -import { Command } from '@tauri-apps/plugin-shell' +import { platform } from '@tauri-apps/plugin-os' +import { useStore } from '../store' + +/* UTILS */ +const SUPPORRTED_BACKENDS = ['sftp', 's3', 'b2', 'drive'] + +function getAuthHeader() { + if (platform() === 'macos') { + return + } + + const state = useStore.getState() + + if (!state.rcloneAuthHeader) { + throw new Error('Rclone auth header is not set') + } + + return { Authorization: state.rcloneAuthHeader } +} /* DATA */ - export async function listRemotes() { const r = await fetch('http://localhost:5572/config/listremotes', { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as Promise<{ remotes: string[] }>) // .catch((e) => { // console.log("error", e); @@ -23,6 +40,7 @@ export async function listRemotes() { export async function getRemote(remote: string) { const r = await fetch(`http://localhost:5572/config/get?name=${remote}`, { method: 'POST', + headers: getAuthHeader(), }).then( (res) => res.json() as Promise<{ type: string } & Record> ) @@ -48,6 +66,7 @@ export async function updateRemote( await fetch(`http://localhost:5572/config/update?${options.toString()}`, { method: 'POST', + headers: getAuthHeader(), }) // console.log(JSON.stringify(r, null, 2)) @@ -67,6 +86,7 @@ export async function createRemote( await fetch(`http://localhost:5572/config/create?${options.toString()}`, { method: 'POST', + headers: getAuthHeader(), }) // console.log(JSON.stringify(r, null, 2)) @@ -75,6 +95,7 @@ export async function createRemote( export async function deleteRemote(remote: string) { await fetch(`http://localhost:5572/config/delete?name=${remote}`, { method: 'POST', + headers: getAuthHeader(), }) // console.log(JSON.stringify(r, null, 2)) @@ -83,6 +104,7 @@ export async function deleteRemote(remote: string) { export async function getMountPoints() { const r = await fetch('http://localhost:5572/mount/listmounts', { method: 'POST', + headers: getAuthHeader(), }).then( (res) => res.json() as Promise<{ @@ -99,11 +121,10 @@ export async function getMountPoints() { return r.mountPoints } -const SUPPORRTED_BACKENDS = ['sftp', 's3', 'b2', 'drive'] - export async function getBackends() { const providers = await fetch('http://localhost:5572/config/providers', { method: 'POST', + headers: getAuthHeader(), }) .then((res) => res.json() as Promise) .then((r) => r.providers) @@ -142,6 +163,7 @@ export async function listPath(remote: string, path: string = '', options: ListO const response = await fetch(`http://localhost:5572/operations/list?${params.toString()}`, { method: 'POST', + headers: getAuthHeader(), }).then( (res) => res.json() as Promise<{ @@ -170,12 +192,14 @@ export async function listPath(remote: string, path: string = '', options: ListO export async function listJobs() { const allStats = await fetch('http://localhost:5572/core/stats', { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as any) const transferring = allStats?.transferring const transferredStats = await fetch('http://localhost:5572/core/transferred', { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as any) const transferred = transferredStats?.transferred @@ -195,6 +219,7 @@ export async function listJobs() { for (const jobId of activeJobIds) { const job = await fetch(`http://localhost:5572/core/stats?group=job/${jobId}`, { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as Promise) jobs.active.push({ @@ -223,6 +248,7 @@ export async function listJobs() { for (const jobId of inactiveJobIds) { const job = await fetch(`http://localhost:5572/core/stats?group=job/${jobId}`, { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as Promise) jobs.inactive.push({ @@ -246,11 +272,11 @@ export async function listJobs() { export async function stopJob(jobId: number) { await fetch(`http://localhost:5572/job/stopgroup?group=job/${jobId}`, { method: 'POST', + headers: getAuthHeader(), }) } /* OPERATIONS */ - export async function mountRemote({ remotePath, mountPoint, @@ -276,6 +302,7 @@ export async function mountRemote({ const r = await fetch(`http://localhost:5572/mount/mount?${options.toString()}`, { method: 'POST', + headers: getAuthHeader(), }) .then((res) => res.json() as Promise<{ remotes: string[] } | Promise<{ error: string }>>) .catch((e) => { @@ -290,36 +317,6 @@ export async function mountRemote({ return } -export async function unmountRemote(mountPoint: string, force = false) { - const command = Command.create('umount', [force ? '-f' : '', mountPoint]) - - const output = await command.execute() - - // console.log('output') - // console.log(JSON.stringify(output, null, 2)) - - if (output.code !== 0) { - if (output.stderr.toLowerCase().includes('busy')) { - const answer = await ask('This resource is busy, do you want to force unmount?', { - title: 'Could not unmount', - kind: 'warning', - }) - if (answer) { - return await unmountRemote(mountPoint, true) - } - throw new Error(output.stderr) - } - - if (output.stderr.toLowerCase().includes('not currently mounted')) { - return - } - - throw new Error(output.stderr) - } - - return -} - export async function startCopy({ source, dest, @@ -351,6 +348,7 @@ export async function startCopy({ const r = await fetch(`http://localhost:5572/sync/copy?${params.toString()}`, { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as Promise<{ jobid: string }>) console.log('Copy operation started:', r) @@ -409,6 +407,7 @@ export async function startSync({ const r = await fetch(`http://localhost:5572/sync/sync?${params.toString()}`, { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as Promise<{ jobid: string }>) console.log('Sync operation started:', r) @@ -441,6 +440,7 @@ export async function startSync({ export async function getGlobalFlags() { const r = await fetch('http://localhost:5572/options/get', { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as Promise) return r @@ -449,6 +449,7 @@ export async function getGlobalFlags() { export async function getCopyFlags() { const r = await fetch('http://localhost:5572/options/info', { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as Promise) const mainFlags = r.main @@ -463,6 +464,7 @@ export async function getCopyFlags() { export async function getSyncFlags() { const r = await fetch('http://localhost:5572/options/info', { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as Promise) const mainFlags = r.main @@ -480,6 +482,7 @@ export async function getSyncFlags() { export async function getFilterFlags() { const r = await fetch('http://localhost:5572/options/info', { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as Promise) const filterFlags = r.filter @@ -493,6 +496,7 @@ export async function getFilterFlags() { export async function getVfsFlags() { const r = await fetch('http://localhost:5572/options/info', { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as Promise) const vfsFlags = r.vfs @@ -507,6 +511,7 @@ export async function getVfsFlags() { export async function getMountFlags() { const r = await fetch('http://localhost:5572/options/info', { method: 'POST', + headers: getAuthHeader(), }).then((res) => res.json() as Promise) const mountFlags = r.mount diff --git a/lib/store.ts b/lib/store.ts index e3ad5ad..1938351 100644 --- a/lib/store.ts +++ b/lib/store.ts @@ -28,6 +28,8 @@ interface State { increment: () => void rcloneLoaded: boolean + rcloneAuth: string + rcloneAuthHeader: string mountedRemotes: Record serveList: { pid: number; protocol: string; remote: string }[] @@ -73,6 +75,8 @@ export const useStore = create()( increment: () => set((state) => ({ count: state.count + 1 })), rcloneLoaded: false, + rcloneAuth: '', + rcloneAuthHeader: '', mountedRemotes: {}, serveList: [], diff --git a/main.ts b/main.ts index acba433..4f95d48 100644 --- a/main.ts +++ b/main.ts @@ -2,13 +2,14 @@ import { getCurrentWindow } from '@tauri-apps/api/window' import { confirm, message } from '@tauri-apps/plugin-dialog' import {} from '@tauri-apps/plugin-fs' import { debug, error, info, trace, warn } from '@tauri-apps/plugin-log' +import { platform } from '@tauri-apps/plugin-os' import { exit } from '@tauri-apps/plugin-process' import { listRemotes } from './lib/rclone/api' import { initRclone } from './lib/rclone/init' import { useStore } from './lib/store' import { initLoadingTray, initTray, rebuildTrayMenu } from './lib/tray' -// forward console logs in webviews to the tauri logger, so they show up in the console +// forward console logs in webviews to the tauri logger, so they show up in terminal function forwardConsole( fnName: 'log' | 'debug' | 'info' | 'warn' | 'error', logger: (message: string) => Promise @@ -28,9 +29,6 @@ forwardConsole('info', info) forwardConsole('warn', warn) forwardConsole('error', error) -console.log('main') -console.error('main') - async function startRclone() { try { const remotes = await listRemotes() @@ -52,12 +50,19 @@ async function startRclone() { return await exit(0) } + const sessionPassword = Math.random().toString(36).substring(2, 15) + useStore.setState({ rcloneAuth: sessionPassword }) + useStore.setState({ rcloneAuthHeader: 'Basic ' + btoa(`admin:${sessionPassword}`) }) + const rcloneCommandFn = rclone.system || rclone.internal const command = await rcloneCommandFn([ 'rcd', - '--rc-no-auth', + ...(platform() === 'macos' + ? ['--rc-no-auth'] // webkit doesn't allow for credentials in the url + : ['--rc-user', 'admin', '--rc-pass', sessionPassword]), '--rc-serve', + // defaults // '-rc-addr', // ':5572', ]) @@ -97,8 +102,6 @@ async function startRclone() { useStore.setState({ remotes: remotes }) // console.log('childProcess', JSON.stringify(childProcess)) // prints `pid` - - // console.log('command', JSON.stringify(command)) } getCurrentWindow().listen('tauri://close-requested', async (e) => { diff --git a/public/browse.html b/public/browse.html index 9415b3a..c0dc686 100644 --- a/public/browse.html +++ b/public/browse.html @@ -267,16 +267,25 @@ It's not pretty, but at the same time Adobe's .PSD implementation exists. } // Function to fetch and modify webpage content - async function fetchAndInjectContent(url, isInitialLoad = false) { + async function fetchAndInjectContent(url, pass, isInitialLoad = false) { try { // Only show navigation spinner for non-initial loads if (!isInitialLoad) { toggleNavSpinner(true); } + + let newUrl = url + if (pass) { + newUrl = url.replace('localhost:5572', 'admin:' + pass + '@localhost:5572') + } else { + console.log('no pass') + } + + console.log('newUrl', newUrl) const fetch = window.__TAURI__.http.fetch // Fetch the webpage content - const response = await fetch(url); + const response = await fetch(newUrl); const html = await response.text(); // Create a temporary DOM parser @@ -322,7 +331,7 @@ It's not pretty, but at the same time Adobe's .PSD implementation exists. document.body.appendChild(navSpinner); // Update the URL in the address bar without reloading - window.history.pushState({}, '', `?url=${encodeURIComponent(url)}`); + window.history.pushState({}, '', `?url=${encodeURIComponent(url)}` + (pass ? `&pass=${encodeURIComponent(pass)}` : '')); // Re-add our event handlers setupEventHandlers(); @@ -469,9 +478,10 @@ It's not pretty, but at the same time Adobe's .PSD implementation exists. // Initial setup const urlParams = new URLSearchParams(window.location.search); const url = urlParams.get('url'); + const pass = urlParams.get('pass'); if (url) { // Pass true to indicate this is the initial load - fetchAndInjectContent(url, true); + fetchAndInjectContent(url, pass, true); } else { document.body.innerHTML = '
Error: No URL provided. Use ?url= parameter.
'; }