fix: guard tauri calls with kernel check (#5619)

This commit is contained in:
Shreyas 2025-11-26 11:34:47 +05:30 committed by GitHub
parent 7deaa136f4
commit fdbec04703
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 123 additions and 37 deletions

View file

@ -64,10 +64,8 @@ declare module 'vue' {
CollectionsDocumentationSectionsCurlView: typeof import('./components/collections/documentation/sections/CurlView.vue')['default']
CollectionsDocumentationSectionsHeaders: typeof import('./components/collections/documentation/sections/Headers.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']
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']
CollectionsEdit: typeof import('./components/collections/Edit.vue')['default']
CollectionsEditFolder: typeof import('./components/collections/EditFolder.vue')['default']
@ -155,7 +153,6 @@ declare module 'vue' {
HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox']
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
HoppSmartFileChip: typeof import('@hoppscotch/ui')['HoppSmartFileChip']
HoppSmartIcon: typeof import('@hoppscotch/ui')['HoppSmartIcon']
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']
HoppSmartIntersection: typeof import('@hoppscotch/ui')['HoppSmartIntersection']
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']
@ -234,12 +231,10 @@ declare module 'vue' {
IconLucideAlertTriangle: typeof import('~icons/lucide/alert-triangle')['default']
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['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']
IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default']
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['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']
IconLucideFileText: typeof import('~icons/lucide/file-text')['default']
IconLucideFolder: typeof import('~icons/lucide/folder')['default']
@ -249,13 +244,12 @@ declare module 'vue' {
IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
IconLucideInfo: typeof import('~icons/lucide/info')['default']
IconLucideLayers: typeof import('~icons/lucide/layers')['default']
IconLucideLightbulb: typeof import('~icons/lucide/lightbulb')['default']
IconLucideListEnd: typeof import('~icons/lucide/list-end')['default']
IconLucideLoader: typeof import('~icons/lucide/loader')['default']
IconLucideLoader2: typeof import('~icons/lucide/loader2')['default']
IconLucideLock: typeof import('~icons/lucide/lock')['default']
IconLucideMinus: typeof import('~icons/lucide/minus')['default']
IconLucidePlusCircle: typeof import('~icons/lucide/plus-circle')['default']
IconLucideRss: typeof import('~icons/lucide/rss')['default']
IconLucideSearch: typeof import('~icons/lucide/search')['default']
IconLucideTerminal: typeof import('~icons/lucide/terminal')['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']
LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.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']
MockServerEditMockServer: typeof import('./components/mockServer/EditMockServer.vue')['default']
MockServerLogSection: typeof import('./components/mockServer/LogSection.vue')['default']

View file

@ -6,39 +6,84 @@ import type {
} from "@hoppscotch/kernel"
import * as E from "fp-ts/Either"
import { getModule } from "."
import { invoke } from "@tauri-apps/api/core"
import { join } from "@tauri-apps/api/path"
import { getKernelMode } from "@hoppscotch/kernel"
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> => {
await isInitd()
if (!invoke) throw new Error("getConfigDir is only available in desktop mode")
return await invoke<string>("get_config_dir")
}
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")
}
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")
}
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")
}
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")
}
const getStorePath = async (): Promise<string> => {
try {
const storeDir = await getStoreDir()
return join(storeDir, STORE_PATH)
} catch (error) {
console.error("Failed to get store directory:", error)
return "hoppscotch-unified.store"
if (getKernelMode() === "desktop") {
await isInitd()
if (join) {
try {
const storeDir = await getStoreDir()
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 = (() => {

View file

@ -44,25 +44,28 @@ import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data"
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
import { PublishedDocs } from "~/helpers/backend/graphql"
import { getKernelMode } from "@hoppscotch/kernel"
import { useService } from "dioc/vue"
import { InstanceSwitcherService } from "~/services/instance-switcher.service"
import { platform } from "~/platform"
import { useReadonlyStream } from "~/composables/stream"
const route = useRoute()
const t = useI18n()
const kernelMode = getKernelMode()
const instanceSwitcherService =
kernelMode === "desktop" ? useService(InstanceSwitcherService) : null
const instancePlatform = platform.instance
const currentState =
kernelMode === "desktop" && instanceSwitcherService
kernelMode === "desktop" &&
instancePlatform?.instanceSwitchingEnabled &&
instancePlatform.getConnectionStateStream
? useReadonlyStream(
instanceSwitcherService.getStateStream(),
instanceSwitcherService.getCurrentState().value
instancePlatform.getConnectionStateStream(),
instancePlatform.getCurrentConnectionState?.() ?? {
status: "disconnected" as const,
}
)
: ref({
status: "disconnected",
status: "disconnected" as const,
instance: { displayName: "Hoppscotch" },
})

View file

@ -54,20 +54,20 @@
<div class="flex gap-4 items-center justify-center mt-6">
<label class="flex items-center space-x-2 cursor-pointer">
<input
type="checkbox"
v-model="portableSettings.disableUpdateNotifications"
@change="onUpdateNotificationsChange"
type="checkbox"
class="form-checkbox h-4 w-4 text-accent"
@change="onUpdateNotificationsChange"
/>
<span class="text-sm">Don't notify about updates</span>
</label>
<label class="flex items-center space-x-2 cursor-pointer">
<input
type="checkbox"
v-model="portableSettings.autoSkipWelcome"
@change="onAutoSkipChange"
type="checkbox"
class="form-checkbox h-4 w-4 text-accent"
@change="onAutoSkipChange"
/>
<span class="text-sm">Don't show this again</span>
</label>

View file

@ -6,39 +6,84 @@ import type {
} from "@hoppscotch/kernel"
import * as E from "fp-ts/Either"
import { getModule } from "."
import { invoke } from "@tauri-apps/api/core"
import { join } from "@tauri-apps/api/path"
import { getKernelMode } from "@hoppscotch/kernel"
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> => {
await isInitd()
if (!invoke) throw new Error("getConfigDir is only available in desktop mode")
return await invoke<string>("get_config_dir")
}
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")
}
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")
}
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")
}
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")
}
const getStorePath = async (): Promise<string> => {
try {
const instanceDir = await getInstanceDir()
return await join(instanceDir, STORE_PATH)
} catch (error) {
console.error("Failed to get instance directory:", error)
return "hoppscotch-unified.store"
if (getKernelMode() === "desktop") {
await isInitd()
if (join) {
try {
const instanceDir = await getInstanceDir()
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 = (() => {