fix: guard tauri calls with kernel check (#5619)
This commit is contained in:
parent
7deaa136f4
commit
fdbec04703
5 changed files with 123 additions and 37 deletions
11
packages/hoppscotch-common/src/components.d.ts
vendored
11
packages/hoppscotch-common/src/components.d.ts
vendored
|
|
@ -64,10 +64,8 @@ declare module 'vue' {
|
||||||
CollectionsDocumentationSectionsCurlView: typeof import('./components/collections/documentation/sections/CurlView.vue')['default']
|
CollectionsDocumentationSectionsCurlView: typeof import('./components/collections/documentation/sections/CurlView.vue')['default']
|
||||||
CollectionsDocumentationSectionsHeaders: typeof import('./components/collections/documentation/sections/Headers.vue')['default']
|
CollectionsDocumentationSectionsHeaders: typeof import('./components/collections/documentation/sections/Headers.vue')['default']
|
||||||
CollectionsDocumentationSectionsParameters: typeof import('./components/collections/documentation/sections/Parameters.vue')['default']
|
CollectionsDocumentationSectionsParameters: typeof import('./components/collections/documentation/sections/Parameters.vue')['default']
|
||||||
CollectionsDocumentationSectionsPreRequestScript: typeof import('./components/collections/documentation/sections/PreRequestScript.vue')['default']
|
|
||||||
CollectionsDocumentationSectionsRequestBody: typeof import('./components/collections/documentation/sections/RequestBody.vue')['default']
|
CollectionsDocumentationSectionsRequestBody: typeof import('./components/collections/documentation/sections/RequestBody.vue')['default']
|
||||||
CollectionsDocumentationSectionsResponse: typeof import('./components/collections/documentation/sections/Response.vue')['default']
|
CollectionsDocumentationSectionsResponse: typeof import('./components/collections/documentation/sections/Response.vue')['default']
|
||||||
CollectionsDocumentationSectionsTestScript: typeof import('./components/collections/documentation/sections/TestScript.vue')['default']
|
|
||||||
CollectionsDocumentationSectionsVariables: typeof import('./components/collections/documentation/sections/Variables.vue')['default']
|
CollectionsDocumentationSectionsVariables: typeof import('./components/collections/documentation/sections/Variables.vue')['default']
|
||||||
CollectionsEdit: typeof import('./components/collections/Edit.vue')['default']
|
CollectionsEdit: typeof import('./components/collections/Edit.vue')['default']
|
||||||
CollectionsEditFolder: typeof import('./components/collections/EditFolder.vue')['default']
|
CollectionsEditFolder: typeof import('./components/collections/EditFolder.vue')['default']
|
||||||
|
|
@ -155,7 +153,6 @@ declare module 'vue' {
|
||||||
HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox']
|
HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox']
|
||||||
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
|
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
|
||||||
HoppSmartFileChip: typeof import('@hoppscotch/ui')['HoppSmartFileChip']
|
HoppSmartFileChip: typeof import('@hoppscotch/ui')['HoppSmartFileChip']
|
||||||
HoppSmartIcon: typeof import('@hoppscotch/ui')['HoppSmartIcon']
|
|
||||||
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']
|
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']
|
||||||
HoppSmartIntersection: typeof import('@hoppscotch/ui')['HoppSmartIntersection']
|
HoppSmartIntersection: typeof import('@hoppscotch/ui')['HoppSmartIntersection']
|
||||||
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']
|
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']
|
||||||
|
|
@ -234,12 +231,10 @@ declare module 'vue' {
|
||||||
IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default']
|
IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default']
|
||||||
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
|
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
|
||||||
IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default']
|
IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default']
|
||||||
|
IconLucideBrush: typeof import('~icons/lucide/brush')['default']
|
||||||
IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default']
|
IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default']
|
||||||
IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default']
|
|
||||||
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
|
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
|
||||||
IconLucideCircleCheck: typeof import('~icons/lucide/circle-check')['default']
|
IconLucideCircleCheck: typeof import('~icons/lucide/circle-check')['default']
|
||||||
IconLucideCode2: typeof import('~icons/lucide/code2')['default']
|
|
||||||
IconLucideEyeOff: typeof import('~icons/lucide/eye-off')['default']
|
|
||||||
IconLucideFileQuestion: typeof import('~icons/lucide/file-question')['default']
|
IconLucideFileQuestion: typeof import('~icons/lucide/file-question')['default']
|
||||||
IconLucideFileText: typeof import('~icons/lucide/file-text')['default']
|
IconLucideFileText: typeof import('~icons/lucide/file-text')['default']
|
||||||
IconLucideFolder: typeof import('~icons/lucide/folder')['default']
|
IconLucideFolder: typeof import('~icons/lucide/folder')['default']
|
||||||
|
|
@ -249,13 +244,12 @@ declare module 'vue' {
|
||||||
IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
|
IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
|
||||||
IconLucideInfo: typeof import('~icons/lucide/info')['default']
|
IconLucideInfo: typeof import('~icons/lucide/info')['default']
|
||||||
IconLucideLayers: typeof import('~icons/lucide/layers')['default']
|
IconLucideLayers: typeof import('~icons/lucide/layers')['default']
|
||||||
IconLucideLightbulb: typeof import('~icons/lucide/lightbulb')['default']
|
|
||||||
IconLucideListEnd: typeof import('~icons/lucide/list-end')['default']
|
IconLucideListEnd: typeof import('~icons/lucide/list-end')['default']
|
||||||
IconLucideLoader: typeof import('~icons/lucide/loader')['default']
|
|
||||||
IconLucideLoader2: typeof import('~icons/lucide/loader2')['default']
|
IconLucideLoader2: typeof import('~icons/lucide/loader2')['default']
|
||||||
IconLucideLock: typeof import('~icons/lucide/lock')['default']
|
IconLucideLock: typeof import('~icons/lucide/lock')['default']
|
||||||
IconLucideMinus: typeof import('~icons/lucide/minus')['default']
|
IconLucideMinus: typeof import('~icons/lucide/minus')['default']
|
||||||
IconLucidePlusCircle: typeof import('~icons/lucide/plus-circle')['default']
|
IconLucidePlusCircle: typeof import('~icons/lucide/plus-circle')['default']
|
||||||
|
IconLucideRss: typeof import('~icons/lucide/rss')['default']
|
||||||
IconLucideSearch: typeof import('~icons/lucide/search')['default']
|
IconLucideSearch: typeof import('~icons/lucide/search')['default']
|
||||||
IconLucideTerminal: typeof import('~icons/lucide/terminal')['default']
|
IconLucideTerminal: typeof import('~icons/lucide/terminal')['default']
|
||||||
IconLucideTriangleAlert: typeof import('~icons/lucide/triangle-alert')['default']
|
IconLucideTriangleAlert: typeof import('~icons/lucide/triangle-alert')['default']
|
||||||
|
|
@ -290,7 +284,6 @@ declare module 'vue' {
|
||||||
LensesRenderersXMLLensRenderer: typeof import('./components/lenses/renderers/XMLLensRenderer.vue')['default']
|
LensesRenderersXMLLensRenderer: typeof import('./components/lenses/renderers/XMLLensRenderer.vue')['default']
|
||||||
LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default']
|
LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default']
|
||||||
MockServerConfigureMockServerModal: typeof import('./components/mockServer/ConfigureMockServerModal.vue')['default']
|
MockServerConfigureMockServerModal: typeof import('./components/mockServer/ConfigureMockServerModal.vue')['default']
|
||||||
MockServerCreateMockServer: typeof import('./components/mockServer/CreateMockServer.vue')['default']
|
|
||||||
MockServerCreateNewMockServerModal: typeof import('./components/mockServer/CreateNewMockServerModal.vue')['default']
|
MockServerCreateNewMockServerModal: typeof import('./components/mockServer/CreateNewMockServerModal.vue')['default']
|
||||||
MockServerEditMockServer: typeof import('./components/mockServer/EditMockServer.vue')['default']
|
MockServerEditMockServer: typeof import('./components/mockServer/EditMockServer.vue')['default']
|
||||||
MockServerLogSection: typeof import('./components/mockServer/LogSection.vue')['default']
|
MockServerLogSection: typeof import('./components/mockServer/LogSection.vue')['default']
|
||||||
|
|
|
||||||
|
|
@ -6,39 +6,84 @@ import type {
|
||||||
} from "@hoppscotch/kernel"
|
} from "@hoppscotch/kernel"
|
||||||
import * as E from "fp-ts/Either"
|
import * as E from "fp-ts/Either"
|
||||||
import { getModule } from "."
|
import { getModule } from "."
|
||||||
import { invoke } from "@tauri-apps/api/core"
|
import { getKernelMode } from "@hoppscotch/kernel"
|
||||||
import { join } from "@tauri-apps/api/path"
|
|
||||||
|
|
||||||
const STORE_PATH = `${window.location.host}.hoppscotch.store`
|
const STORE_PATH = `${window.location.host}.hoppscotch.store`
|
||||||
|
|
||||||
|
// These are only defined functions if in desktop mode.
|
||||||
|
// For more context, take a look at how `hoppscotch-kernel/.../store/v1/` works
|
||||||
|
// and how the `web` mode store kernel ignores the first file directory input.
|
||||||
|
let invoke:
|
||||||
|
| (<T>(cmd: string, args?: Record<string, unknown>) => Promise<T>)
|
||||||
|
| undefined
|
||||||
|
let join: ((...paths: string[]) => Promise<string>) | undefined
|
||||||
|
|
||||||
|
// Single init promise to avoid multiple imports and race conditions
|
||||||
|
let initPromise: Promise<void> | undefined
|
||||||
|
|
||||||
|
const isInitd = async () => {
|
||||||
|
if (getKernelMode() !== "desktop") return
|
||||||
|
|
||||||
|
if (!initPromise) {
|
||||||
|
initPromise = Promise.all([
|
||||||
|
import("@tauri-apps/api/core").then((module) => {
|
||||||
|
invoke = module.invoke
|
||||||
|
}),
|
||||||
|
import("@tauri-apps/api/path").then((module) => {
|
||||||
|
join = module.join
|
||||||
|
}),
|
||||||
|
]).then(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
await initPromise
|
||||||
|
}
|
||||||
|
|
||||||
export const getConfigDir = async (): Promise<string> => {
|
export const getConfigDir = async (): Promise<string> => {
|
||||||
|
await isInitd()
|
||||||
|
if (!invoke) throw new Error("getConfigDir is only available in desktop mode")
|
||||||
return await invoke<string>("get_config_dir")
|
return await invoke<string>("get_config_dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getBackupDir = async (): Promise<string> => {
|
export const getBackupDir = async (): Promise<string> => {
|
||||||
|
await isInitd()
|
||||||
|
if (!invoke) throw new Error("getBackupDir is only available in desktop mode")
|
||||||
return await invoke<string>("get_backup_dir")
|
return await invoke<string>("get_backup_dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getLatestDir = async (): Promise<string> => {
|
export const getLatestDir = async (): Promise<string> => {
|
||||||
|
await isInitd()
|
||||||
|
if (!invoke) throw new Error("getLatestDir is only available in desktop mode")
|
||||||
return await invoke<string>("get_latest_dir")
|
return await invoke<string>("get_latest_dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getStoreDir = async (): Promise<string> => {
|
export const getStoreDir = async (): Promise<string> => {
|
||||||
|
await isInitd()
|
||||||
|
if (!invoke) throw new Error("getStoreDir is only available in desktop mode")
|
||||||
return await invoke<string>("get_store_dir")
|
return await invoke<string>("get_store_dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getInstanceDir = async (): Promise<string> => {
|
export const getInstanceDir = async (): Promise<string> => {
|
||||||
|
await isInitd()
|
||||||
|
if (!invoke)
|
||||||
|
throw new Error("getInstanceDir is only available in desktop mode")
|
||||||
return await invoke<string>("get_instance_dir")
|
return await invoke<string>("get_instance_dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStorePath = async (): Promise<string> => {
|
const getStorePath = async (): Promise<string> => {
|
||||||
try {
|
if (getKernelMode() === "desktop") {
|
||||||
const storeDir = await getStoreDir()
|
await isInitd()
|
||||||
return join(storeDir, STORE_PATH)
|
if (join) {
|
||||||
} catch (error) {
|
try {
|
||||||
console.error("Failed to get store directory:", error)
|
const storeDir = await getStoreDir()
|
||||||
return "hoppscotch-unified.store"
|
return await join(storeDir, STORE_PATH)
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to get store directory:", error)
|
||||||
|
return STORE_PATH
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return STORE_PATH
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Store = (() => {
|
export const Store = (() => {
|
||||||
|
|
|
||||||
|
|
@ -44,25 +44,28 @@ import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"
|
||||||
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
|
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
|
||||||
import { PublishedDocs } from "~/helpers/backend/graphql"
|
import { PublishedDocs } from "~/helpers/backend/graphql"
|
||||||
import { getKernelMode } from "@hoppscotch/kernel"
|
import { getKernelMode } from "@hoppscotch/kernel"
|
||||||
import { useService } from "dioc/vue"
|
import { platform } from "~/platform"
|
||||||
import { InstanceSwitcherService } from "~/services/instance-switcher.service"
|
|
||||||
import { useReadonlyStream } from "~/composables/stream"
|
import { useReadonlyStream } from "~/composables/stream"
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
const kernelMode = getKernelMode()
|
const kernelMode = getKernelMode()
|
||||||
const instanceSwitcherService =
|
|
||||||
kernelMode === "desktop" ? useService(InstanceSwitcherService) : null
|
const instancePlatform = platform.instance
|
||||||
|
|
||||||
const currentState =
|
const currentState =
|
||||||
kernelMode === "desktop" && instanceSwitcherService
|
kernelMode === "desktop" &&
|
||||||
|
instancePlatform?.instanceSwitchingEnabled &&
|
||||||
|
instancePlatform.getConnectionStateStream
|
||||||
? useReadonlyStream(
|
? useReadonlyStream(
|
||||||
instanceSwitcherService.getStateStream(),
|
instancePlatform.getConnectionStateStream(),
|
||||||
instanceSwitcherService.getCurrentState().value
|
instancePlatform.getCurrentConnectionState?.() ?? {
|
||||||
|
status: "disconnected" as const,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
: ref({
|
: ref({
|
||||||
status: "disconnected",
|
status: "disconnected" as const,
|
||||||
instance: { displayName: "Hoppscotch" },
|
instance: { displayName: "Hoppscotch" },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,20 +54,20 @@
|
||||||
<div class="flex gap-4 items-center justify-center mt-6">
|
<div class="flex gap-4 items-center justify-center mt-6">
|
||||||
<label class="flex items-center space-x-2 cursor-pointer">
|
<label class="flex items-center space-x-2 cursor-pointer">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
|
||||||
v-model="portableSettings.disableUpdateNotifications"
|
v-model="portableSettings.disableUpdateNotifications"
|
||||||
@change="onUpdateNotificationsChange"
|
type="checkbox"
|
||||||
class="form-checkbox h-4 w-4 text-accent"
|
class="form-checkbox h-4 w-4 text-accent"
|
||||||
|
@change="onUpdateNotificationsChange"
|
||||||
/>
|
/>
|
||||||
<span class="text-sm">Don't notify about updates</span>
|
<span class="text-sm">Don't notify about updates</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="flex items-center space-x-2 cursor-pointer">
|
<label class="flex items-center space-x-2 cursor-pointer">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
|
||||||
v-model="portableSettings.autoSkipWelcome"
|
v-model="portableSettings.autoSkipWelcome"
|
||||||
@change="onAutoSkipChange"
|
type="checkbox"
|
||||||
class="form-checkbox h-4 w-4 text-accent"
|
class="form-checkbox h-4 w-4 text-accent"
|
||||||
|
@change="onAutoSkipChange"
|
||||||
/>
|
/>
|
||||||
<span class="text-sm">Don't show this again</span>
|
<span class="text-sm">Don't show this again</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
|
||||||
|
|
@ -6,39 +6,84 @@ import type {
|
||||||
} from "@hoppscotch/kernel"
|
} from "@hoppscotch/kernel"
|
||||||
import * as E from "fp-ts/Either"
|
import * as E from "fp-ts/Either"
|
||||||
import { getModule } from "."
|
import { getModule } from "."
|
||||||
import { invoke } from "@tauri-apps/api/core"
|
import { getKernelMode } from "@hoppscotch/kernel"
|
||||||
import { join } from "@tauri-apps/api/path"
|
|
||||||
|
|
||||||
const STORE_PATH = "hoppscotch-unified.store"
|
const STORE_PATH = "hoppscotch-unified.store"
|
||||||
|
|
||||||
|
// These are only defined functions if in desktop mode.
|
||||||
|
// For more context, take a look at how `hoppscotch-kernel/.../store/v1/` works
|
||||||
|
// and how the `web` mode store kernel ignores the first file directory input.
|
||||||
|
let invoke:
|
||||||
|
| (<T>(cmd: string, args?: Record<string, unknown>) => Promise<T>)
|
||||||
|
| undefined
|
||||||
|
let join: ((...paths: string[]) => Promise<string>) | undefined
|
||||||
|
|
||||||
|
// Single init promise to avoid multiple imports and race conditions
|
||||||
|
let initPromise: Promise<void> | undefined
|
||||||
|
|
||||||
|
const isInitd = async () => {
|
||||||
|
if (getKernelMode() !== "desktop") return
|
||||||
|
|
||||||
|
if (!initPromise) {
|
||||||
|
initPromise = Promise.all([
|
||||||
|
import("@tauri-apps/api/core").then((module) => {
|
||||||
|
invoke = module.invoke
|
||||||
|
}),
|
||||||
|
import("@tauri-apps/api/path").then((module) => {
|
||||||
|
join = module.join
|
||||||
|
}),
|
||||||
|
]).then(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
await initPromise
|
||||||
|
}
|
||||||
|
|
||||||
export const getConfigDir = async (): Promise<string> => {
|
export const getConfigDir = async (): Promise<string> => {
|
||||||
|
await isInitd()
|
||||||
|
if (!invoke) throw new Error("getConfigDir is only available in desktop mode")
|
||||||
return await invoke<string>("get_config_dir")
|
return await invoke<string>("get_config_dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getBackupDir = async (): Promise<string> => {
|
export const getBackupDir = async (): Promise<string> => {
|
||||||
|
await isInitd()
|
||||||
|
if (!invoke) throw new Error("getBackupDir is only available in desktop mode")
|
||||||
return await invoke<string>("get_backup_dir")
|
return await invoke<string>("get_backup_dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getLatestDir = async (): Promise<string> => {
|
export const getLatestDir = async (): Promise<string> => {
|
||||||
|
await isInitd()
|
||||||
|
if (!invoke) throw new Error("getLatestDir is only available in desktop mode")
|
||||||
return await invoke<string>("get_latest_dir")
|
return await invoke<string>("get_latest_dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getStoreDir = async (): Promise<string> => {
|
export const getStoreDir = async (): Promise<string> => {
|
||||||
|
await isInitd()
|
||||||
|
if (!invoke) throw new Error("getStoreDir is only available in desktop mode")
|
||||||
return await invoke<string>("get_store_dir")
|
return await invoke<string>("get_store_dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getInstanceDir = async (): Promise<string> => {
|
export const getInstanceDir = async (): Promise<string> => {
|
||||||
|
await isInitd()
|
||||||
|
if (!invoke)
|
||||||
|
throw new Error("getInstanceDir is only available in desktop mode")
|
||||||
return await invoke<string>("get_instance_dir")
|
return await invoke<string>("get_instance_dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStorePath = async (): Promise<string> => {
|
const getStorePath = async (): Promise<string> => {
|
||||||
try {
|
if (getKernelMode() === "desktop") {
|
||||||
const instanceDir = await getInstanceDir()
|
await isInitd()
|
||||||
return await join(instanceDir, STORE_PATH)
|
if (join) {
|
||||||
} catch (error) {
|
try {
|
||||||
console.error("Failed to get instance directory:", error)
|
const instanceDir = await getInstanceDir()
|
||||||
return "hoppscotch-unified.store"
|
return await join(instanceDir, STORE_PATH)
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to get instance directory:", error)
|
||||||
|
return STORE_PATH
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return STORE_PATH
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Store = (() => {
|
export const Store = (() => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue