diff --git a/electron/main.ts b/electron/main.ts index bf0685c89..3001cd00b 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -5,6 +5,7 @@ import { join } from 'path' import { setupAutoUpdater } from './services/auto-update' import store from './services/config-store' import { setupNetworkService } from './services/network' +import { setupFilesystemStorage } from './services/storage' // If the app is packaged, push logs to the system instead of the console if (app.isPackaged) { @@ -71,6 +72,7 @@ protocol.registerSchemesAsPrivileged([ }, ]) +setupFilesystemStorage() setupNetworkService() app.whenReady().then(async () => { diff --git a/electron/preload.ts b/electron/preload.ts index 08977a09b..a26e42dce 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -14,4 +14,21 @@ contextBridge.exposeInMainWorld('electronAPI', { downloadUpdate: () => ipcRenderer.send('download-update'), installUpdate: () => ipcRenderer.send('install-update'), cancelUpdate: () => ipcRenderer.send('cancel-update'), + setItem: async (key: string, value: Blob, subFolders?: string[]) => { + const arrayBuffer = await value.arrayBuffer() + await ipcRenderer.invoke('setItem', { key, value: new Uint8Array(arrayBuffer), subFolders }) + }, + getItem: async (key: string, subFolders?: string[]) => { + const arrayBuffer = await ipcRenderer.invoke('getItem', { key, subFolders }) + return arrayBuffer ? new Blob([arrayBuffer]) : null + }, + removeItem: async (key: string, subFolders?: string[]) => { + await ipcRenderer.invoke('removeItem', { key, subFolders }) + }, + clear: async (subFolders?: string[]) => { + await ipcRenderer.invoke('clear', { subFolders }) + }, + keys: async (subFolders?: string[]) => { + return await ipcRenderer.invoke('keys', { subFolders }) + }, }) diff --git a/electron/services/storage.ts b/electron/services/storage.ts new file mode 100644 index 000000000..f005494df --- /dev/null +++ b/electron/services/storage.ts @@ -0,0 +1,61 @@ +import { ipcMain } from 'electron' +import { app } from 'electron' +import * as fs from 'fs/promises' +import { dirname, join } from 'path' + +// Create a new storage interface for filesystem +const cockpitFolderPath = join(app.getPath('home'), 'Cockpit') +fs.mkdir(cockpitFolderPath, { recursive: true }) + +export const filesystemStorage = { + async setItem(key: string, value: ArrayBuffer, subFolders?: string[]): Promise { + const buffer = Buffer.from(value) + const filePath = join(cockpitFolderPath, ...(subFolders ?? []), key) + await fs.mkdir(dirname(filePath), { recursive: true }) + await fs.writeFile(filePath, buffer) + }, + async getItem(key: string, subFolders?: string[]): Promise { + const filePath = join(cockpitFolderPath, ...(subFolders ?? []), key) + try { + return await fs.readFile(filePath) + } catch (error) { + if (error.code === 'ENOENT') return null + throw error + } + }, + async removeItem(key: string, subFolders?: string[]): Promise { + const filePath = join(cockpitFolderPath, ...(subFolders ?? []), key) + await fs.unlink(filePath) + }, + async clear(subFolders?: string[]): Promise { + const dirPath = join(cockpitFolderPath, ...(subFolders ?? [])) + await fs.rm(dirPath, { recursive: true }) + }, + async keys(subFolders?: string[]): Promise { + const dirPath = join(cockpitFolderPath, ...(subFolders ?? [])) + try { + return await fs.readdir(dirPath) + } catch (error) { + if (error.code === 'ENOENT') return [] + throw error + } + }, +} + +export const setupFilesystemStorage = (): void => { + ipcMain.handle('setItem', async (_, data) => { + await filesystemStorage.setItem(data.key, data.value, data.subFolders) + }) + ipcMain.handle('getItem', async (_, data) => { + return await filesystemStorage.getItem(data.key, data.subFolders) + }) + ipcMain.handle('removeItem', async (_, data) => { + await filesystemStorage.removeItem(data.key, data.subFolders) + }) + ipcMain.handle('clear', async (_, data) => { + await filesystemStorage.clear(data.subFolders) + }) + ipcMain.handle('keys', async (_, data) => { + return await filesystemStorage.keys(data.subFolders) + }) +} diff --git a/src/libs/cosmos.ts b/src/libs/cosmos.ts index 810bef604..b74b2f9e8 100644 --- a/src/libs/cosmos.ts +++ b/src/libs/cosmos.ts @@ -1,5 +1,6 @@ import { isBrowser } from 'browser-or-node' +import { ElectronStorageDB } from '@/types/general' import { NetworkInfo } from '@/types/network' import { @@ -185,7 +186,7 @@ declare global { /** * Electron API exposed through preload script */ - electronAPI?: { + electronAPI?: ElectronStorageDB & { /** * Get network information from the main process * @returns Promise containing subnet information diff --git a/src/stores/video.ts b/src/stores/video.ts index 34d53ff4b..86308a73b 100644 --- a/src/stores/video.ts +++ b/src/stores/video.ts @@ -21,10 +21,10 @@ import { isEqual, sleep } from '@/libs/utils' import { useMainVehicleStore } from '@/stores/mainVehicle' import { useMissionStore } from '@/stores/mission' import { Alert, AlertLevel } from '@/types/alert' +import { StorageDB } from '@/types/general' import { type DownloadProgressCallback, type FileDescriptor, - type StorageDB, type StreamData, type UnprocessedVideoInfo, type VideoProcessingDetails, diff --git a/src/types/general.ts b/src/types/general.ts index 0ba903c59..f01f8ae57 100644 --- a/src/types/general.ts +++ b/src/types/general.ts @@ -33,3 +33,34 @@ export interface DialogActions { } export type ConfigComponent = DefineComponent, Record, unknown> | null + +export interface StorageDB { + getItem: (key: string) => Promise + setItem: (key: string, value: Blob) => Promise + removeItem: (key: string) => Promise + clear: () => Promise + keys: () => Promise +} + +export interface ElectronStorageDB { + /** + * Set an item in the filesystem storage + */ + setItem: (key: string, value: Blob, subFolders?: string[]) => Promise + /** + * Get an item from the filesystem storage + */ + getItem: (key: string, subFolders?: string[]) => Promise + /** + * Remove an item from the filesystem storage + */ + removeItem: (key: string, subFolders?: string[]) => Promise + /** + * Clear the filesystem storage + */ + clear: (subFolders?: string[]) => Promise + /** + * Get all keys from the filesystem storage + */ + keys: (subFolders?: string[]) => Promise +} diff --git a/src/types/video.ts b/src/types/video.ts index 5de9e0afc..5b58ba90d 100644 --- a/src/types/video.ts +++ b/src/types/video.ts @@ -108,10 +108,6 @@ export interface FileDescriptor { filename: string } -export interface StorageDB { - getItem: (key: string) => Promise -} - export type DownloadProgressCallback = (progress: number, total: number) => Promise export enum VideoExtensionContainer {