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 (