diff --git a/core/src/api/index.ts b/core/src/api/index.ts index a232c4090..0adc8b7e2 100644 --- a/core/src/api/index.ts +++ b/core/src/api/index.ts @@ -12,6 +12,7 @@ export enum AppRoute { updateAppConfiguration = 'updateAppConfiguration', relaunch = 'relaunch', joinPath = 'joinPath', + isSubdirectory = 'isSubdirectory', baseName = 'baseName', startServer = 'startServer', stopServer = 'stopServer', diff --git a/core/src/core.ts b/core/src/core.ts index aa545e10e..24053e55c 100644 --- a/core/src/core.ts +++ b/core/src/core.ts @@ -22,7 +22,11 @@ const executeOnMain: (extension: string, method: string, ...args: any[]) => Prom * @param {object} network - Optional object to specify proxy/whether to ignore SSL certificates. * @returns {Promise} A promise that resolves when the file is downloaded. */ -const downloadFile: (url: string, fileName: string, network?: { proxy?: string, ignoreSSL?: boolean }) => Promise = (url, fileName, network) => { +const downloadFile: ( + url: string, + fileName: string, + network?: { proxy?: string; ignoreSSL?: boolean } +) => Promise = (url, fileName, network) => { return global.core?.api?.downloadFile(url, fileName, network) } @@ -87,6 +91,17 @@ const getResourcePath: () => Promise = () => global.core.api?.getResourc const log: (message: string, fileName?: string) => void = (message, fileName) => global.core.api?.log(message, fileName) +/** + * Check whether the path is a subdirectory of another path. + * + * @param from - The path to check. + * @param to - The path to check against. + * + * @returns {Promise} - A promise that resolves with a boolean indicating whether the path is a subdirectory. + */ +const isSubdirectory: (from: string, to: string) => Promise = (from: string, to: string) => + global.core.api?.isSubdirectory(from, to) + /** * Register extension point function type definition */ @@ -94,7 +109,7 @@ export type RegisterExtensionPoint = ( extensionName: string, extensionId: string, method: Function, - priority?: number, + priority?: number ) => void /** @@ -111,5 +126,6 @@ export { openExternalUrl, baseName, log, + isSubdirectory, FileStat, } diff --git a/electron/handlers/app.ts b/electron/handlers/app.ts index bdb70047a..c1f431ef3 100644 --- a/electron/handlers/app.ts +++ b/electron/handlers/app.ts @@ -1,5 +1,5 @@ import { app, ipcMain, dialog, shell } from 'electron' -import { join, basename } from 'path' +import { join, basename, relative as getRelative, isAbsolute } from 'path' import { WindowManager } from './../managers/window' import { getResourcePath } from './../utils/path' import { AppRoute, AppConfiguration } from '@janhq/core' @@ -50,6 +50,27 @@ export function handleAppIPCs() { join(...paths) ) + /** + * Checks if the given path is a subdirectory of the given directory. + * + * @param _event - The IPC event object. + * @param from - The path to check. + * @param to - The directory to check against. + * + * @returns {Promise} - A promise that resolves with the result. + */ + ipcMain.handle( + AppRoute.isSubdirectory, + async (_event, from: string, to: string) => { + const relative = getRelative(from, to) + const isSubdir = + relative && !relative.startsWith('..') && !isAbsolute(relative) + + if (isSubdir === '') return false + else return isSubdir + } + ) + /** * Retrieve basename from given path, respect to the current OS. */ diff --git a/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx b/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx index d80e34f3c..84646e735 100644 --- a/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx +++ b/web/screens/Settings/Advanced/DataFolder/ModalErrorSetDestGlobal.tsx @@ -10,15 +10,12 @@ import { ModalClose, Button, } from '@janhq/uikit' -import { atom, useAtom, useAtomValue } from 'jotai' - -import { errorAtom } from '.' +import { atom, useAtom } from 'jotai' export const showChangeFolderErrorAtom = atom(false) const ModalErrorSetDestGlobal = () => { const [show, setShow] = useAtom(showChangeFolderErrorAtom) - const error = useAtomValue(errorAtom) return ( @@ -30,7 +27,6 @@ const ModalErrorSetDestGlobal = () => { Oops! Something went wrong. Jan data folder remains the same. Please try again.

-

{error}

setShow(false)}> diff --git a/web/screens/Settings/Advanced/DataFolder/index.tsx b/web/screens/Settings/Advanced/DataFolder/index.tsx index 1704cd964..5abd5390b 100644 --- a/web/screens/Settings/Advanced/DataFolder/index.tsx +++ b/web/screens/Settings/Advanced/DataFolder/index.tsx @@ -1,8 +1,8 @@ import { Fragment, useCallback, useEffect, useState } from 'react' -import { fs, AppConfiguration } from '@janhq/core' +import { fs, AppConfiguration, isSubdirectory } from '@janhq/core' import { Button, Input } from '@janhq/uikit' -import { atom, useSetAtom } from 'jotai' +import { useSetAtom } from 'jotai' import { PencilIcon, FolderOpenIcon } from 'lucide-react' import Loader from '@/containers/Loader' @@ -18,8 +18,6 @@ import ModalErrorSetDestGlobal, { import ModalSameDirectory, { showSamePathModalAtom } from './ModalSameDirectory' -export const errorAtom = atom('') - const DataFolder = () => { const [janDataFolderPath, setJanDataFolderPath] = useState('') const [showLoader, setShowLoader] = useState(false) @@ -27,7 +25,6 @@ const DataFolder = () => { const setShowSameDirectory = useSetAtom(showSamePathModalAtom) const setShowChangeFolderError = useSetAtom(showChangeFolderErrorAtom) const [destinationPath, setDestinationPath] = useState(undefined) - const setError = useSetAtom(errorAtom) useEffect(() => { window.core?.api @@ -46,6 +43,15 @@ const DataFolder = () => { return } + const appConfiguration: AppConfiguration = + await window.core?.api?.getAppConfigurations() + const currentJanDataFolder = appConfiguration.data_folder + + if (await isSubdirectory(currentJanDataFolder, destFolder)) { + setShowSameDirectory(true) + return + } + setDestinationPath(destFolder) setShowDirectoryConfirm(true) }, [janDataFolderPath, setShowSameDirectory, setShowDirectoryConfirm]) @@ -68,14 +74,12 @@ const DataFolder = () => { setShowLoader(false) }, 1200) await window.core?.api?.relaunch() - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e: any) { + } catch (e) { console.error(`Error: ${e}`) - setError(e.message) setShowLoader(false) setShowChangeFolderError(true) } - }, [destinationPath, setError, setShowChangeFolderError]) + }, [destinationPath, setShowChangeFolderError]) return (