refactor: deprecate invokers - auto proxying apis - strict types (#924)
* refactor: deprecate invokers * refactor: define routes and auto proxying routes * refactor: declare types for APIs, avoid making dynamic calls to any functions from the web * chore: deprecate route handling from preload script * refactor: deprecate unused apis
This commit is contained in:
parent
df977143ec
commit
c4d8defe94
100
core/src/api/index.ts
Normal file
100
core/src/api/index.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* App Route APIs
|
||||||
|
* @description Enum of all the routes exposed by the app
|
||||||
|
*/
|
||||||
|
export enum AppRoute {
|
||||||
|
setNativeThemeLight = 'setNativeThemeLight',
|
||||||
|
setNativeThemeDark = 'setNativeThemeDark',
|
||||||
|
setNativeThemeSystem = 'setNativeThemeSystem',
|
||||||
|
appDataPath = 'appDataPath',
|
||||||
|
appVersion = 'appVersion',
|
||||||
|
getResourcePath = 'getResourcePath',
|
||||||
|
openExternalUrl = 'openExternalUrl',
|
||||||
|
openAppDirectory = 'openAppDirectory',
|
||||||
|
openFileExplore = 'openFileExplorer',
|
||||||
|
relaunch = 'relaunch',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AppEvent {
|
||||||
|
onAppUpdateDownloadUpdate = 'onAppUpdateDownloadUpdate',
|
||||||
|
onAppUpdateDownloadError = 'onAppUpdateDownloadError',
|
||||||
|
onAppUpdateDownloadSuccess = 'onAppUpdateDownloadSuccess',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DownloadRoute {
|
||||||
|
abortDownload = 'abortDownload',
|
||||||
|
downloadFile = 'downloadFile',
|
||||||
|
pauseDownload = 'pauseDownload',
|
||||||
|
resumeDownload = 'resumeDownload',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DownloadEvent {
|
||||||
|
onFileDownloadUpdate = 'onFileDownloadUpdate',
|
||||||
|
onFileDownloadError = 'onFileDownloadError',
|
||||||
|
onFileDownloadSuccess = 'onFileDownloadSuccess',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ExtensionRoute {
|
||||||
|
baseExtensions = 'baseExtensions',
|
||||||
|
getActiveExtensions = 'getActiveExtensions',
|
||||||
|
installExtension = 'installExtension',
|
||||||
|
invokeExtensionFunc = 'invokeExtensionFunc',
|
||||||
|
updateExtension = 'updateExtension',
|
||||||
|
uninstallExtension = 'uninstallExtension',
|
||||||
|
}
|
||||||
|
export enum FileSystemRoute {
|
||||||
|
appendFile = 'appendFile',
|
||||||
|
copyFile = 'copyFile',
|
||||||
|
deleteFile = 'deleteFile',
|
||||||
|
exists = 'exists',
|
||||||
|
getResourcePath = 'getResourcePath',
|
||||||
|
getUserSpace = 'getUserSpace',
|
||||||
|
isDirectory = 'isDirectory',
|
||||||
|
listFiles = 'listFiles',
|
||||||
|
mkdir = 'mkdir',
|
||||||
|
readFile = 'readFile',
|
||||||
|
readLineByLine = 'readLineByLine',
|
||||||
|
rmdir = 'rmdir',
|
||||||
|
writeFile = 'writeFile',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiFunction = (...args: any[]) => any
|
||||||
|
|
||||||
|
export type AppRouteFunctions = {
|
||||||
|
[K in AppRoute]: ApiFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AppEventFunctions = {
|
||||||
|
[K in AppEvent]: ApiFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DownloadRouteFunctions = {
|
||||||
|
[K in DownloadRoute]: ApiFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DownloadEventFunctions = {
|
||||||
|
[K in DownloadEvent]: ApiFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExtensionRouteFunctions = {
|
||||||
|
[K in ExtensionRoute]: ApiFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FileSystemRouteFunctions = {
|
||||||
|
[K in FileSystemRoute]: ApiFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
export type APIFunctions = AppRouteFunctions &
|
||||||
|
AppEventFunctions &
|
||||||
|
DownloadRouteFunctions &
|
||||||
|
DownloadEventFunctions &
|
||||||
|
ExtensionRouteFunctions &
|
||||||
|
FileSystemRouteFunctions
|
||||||
|
|
||||||
|
export const APIRoutes = [
|
||||||
|
...Object.values(AppRoute),
|
||||||
|
...Object.values(DownloadRoute),
|
||||||
|
...Object.values(ExtensionRoute),
|
||||||
|
...Object.values(FileSystemRoute),
|
||||||
|
]
|
||||||
|
export const APIEvents = [...Object.values(AppEvent), ...Object.values(DownloadEvent)]
|
||||||
@ -30,13 +30,6 @@ const downloadFile: (url: string, fileName: string) => Promise<any> = (url, file
|
|||||||
const abortDownload: (fileName: string) => Promise<any> = (fileName) =>
|
const abortDownload: (fileName: string) => Promise<any> = (fileName) =>
|
||||||
global.core.api?.abortDownload(fileName)
|
global.core.api?.abortDownload(fileName)
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the path to the app data directory using the `coreAPI` object.
|
|
||||||
* If the `coreAPI` object is not available, the function returns `undefined`.
|
|
||||||
* @returns A Promise that resolves with the path to the app data directory, or `undefined` if the `coreAPI` object is not available.
|
|
||||||
*/
|
|
||||||
const appDataPath: () => Promise<any> = () => global.core.api?.appDataPath()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the user space path.
|
* Gets the user space path.
|
||||||
* @returns {Promise<any>} A Promise that resolves with the user space path.
|
* @returns {Promise<any>} A Promise that resolves with the user space path.
|
||||||
@ -70,7 +63,6 @@ export {
|
|||||||
executeOnMain,
|
executeOnMain,
|
||||||
downloadFile,
|
downloadFile,
|
||||||
abortDownload,
|
abortDownload,
|
||||||
appDataPath,
|
|
||||||
getUserSpace,
|
getUserSpace,
|
||||||
openFileExplorer,
|
openFileExplorer,
|
||||||
getResourcePath,
|
getResourcePath,
|
||||||
|
|||||||
@ -2,34 +2,39 @@
|
|||||||
* Export all types.
|
* Export all types.
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
export * from "./types/index";
|
export * from './types/index'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export all routes
|
||||||
|
*/
|
||||||
|
export * from './api'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export Core module
|
* Export Core module
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
export * from "./core";
|
export * from './core'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export Event module.
|
* Export Event module.
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
export * from "./events";
|
export * from './events'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export Filesystem module.
|
* Export Filesystem module.
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
export * from "./fs";
|
export * from './fs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export Extension module.
|
* Export Extension module.
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
export * from "./extension";
|
export * from './extension'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export all base extensions.
|
* Export all base extensions.
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
export * from "./extensions/index";
|
export * from './extensions/index'
|
||||||
|
|||||||
@ -1,18 +1,35 @@
|
|||||||
import { app, ipcMain, shell } from 'electron'
|
import { app, ipcMain, shell, nativeTheme } from 'electron'
|
||||||
import { ModuleManager } from './../managers/module'
|
import { ModuleManager } from './../managers/module'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { ExtensionManager } from './../managers/extension'
|
import { ExtensionManager } from './../managers/extension'
|
||||||
import { WindowManager } from './../managers/window'
|
import { WindowManager } from './../managers/window'
|
||||||
import { userSpacePath } from './../utils/path'
|
import { userSpacePath } from './../utils/path'
|
||||||
|
import { AppRoute } from '@janhq/core'
|
||||||
|
import { getResourcePath } from './../utils/path'
|
||||||
|
|
||||||
export function handleAppIPCs() {
|
export function handleAppIPCs() {
|
||||||
/**
|
/**
|
||||||
* Retrieves the path to the app data directory using the `coreAPI` object.
|
* Handles the "setNativeThemeLight" IPC message by setting the native theme source to "light".
|
||||||
* If the `coreAPI` object is not available, the function returns `undefined`.
|
* This will change the appearance of the app to the light theme.
|
||||||
* @returns A Promise that resolves with the path to the app data directory, or `undefined` if the `coreAPI` object is not available.
|
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('appDataPath', async (_event) => {
|
ipcMain.handle(AppRoute.setNativeThemeLight, () => {
|
||||||
return app.getPath('userData')
|
nativeTheme.themeSource = 'light'
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the "setNativeThemeDark" IPC message by setting the native theme source to "dark".
|
||||||
|
* This will change the appearance of the app to the dark theme.
|
||||||
|
*/
|
||||||
|
ipcMain.handle(AppRoute.setNativeThemeDark, () => {
|
||||||
|
nativeTheme.themeSource = 'dark'
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the "setNativeThemeSystem" IPC message by setting the native theme source to "system".
|
||||||
|
* This will change the appearance of the app to match the system's current theme.
|
||||||
|
*/
|
||||||
|
ipcMain.handle(AppRoute.setNativeThemeSystem, () => {
|
||||||
|
nativeTheme.themeSource = 'system'
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,7 +37,7 @@ export function handleAppIPCs() {
|
|||||||
* @param _event - The IPC event object.
|
* @param _event - The IPC event object.
|
||||||
* @returns The version of the app.
|
* @returns The version of the app.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('appVersion', async (_event) => {
|
ipcMain.handle(AppRoute.appVersion, async (_event) => {
|
||||||
return app.getVersion()
|
return app.getVersion()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -29,16 +46,20 @@ export function handleAppIPCs() {
|
|||||||
* The `shell.openPath` method is used to open the directory in the user's default file explorer.
|
* The `shell.openPath` method is used to open the directory in the user's default file explorer.
|
||||||
* @param _event - The IPC event object.
|
* @param _event - The IPC event object.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('openAppDirectory', async (_event) => {
|
ipcMain.handle(AppRoute.openAppDirectory, async (_event) => {
|
||||||
shell.openPath(userSpacePath)
|
shell.openPath(userSpacePath)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.handle(AppRoute.getResourcePath, async (_event) => {
|
||||||
|
return getResourcePath()
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a URL in the user's default browser.
|
* Opens a URL in the user's default browser.
|
||||||
* @param _event - The IPC event object.
|
* @param _event - The IPC event object.
|
||||||
* @param url - The URL to open.
|
* @param url - The URL to open.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('openExternalUrl', async (_event, url) => {
|
ipcMain.handle(AppRoute.openExternalUrl, async (_event, url) => {
|
||||||
shell.openExternal(url)
|
shell.openExternal(url)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -47,7 +68,7 @@ export function handleAppIPCs() {
|
|||||||
* @param _event - The IPC event object.
|
* @param _event - The IPC event object.
|
||||||
* @param url - The URL to reload.
|
* @param url - The URL to reload.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('relaunch', async (_event, url) => {
|
ipcMain.handle(AppRoute.relaunch, async (_event, url) => {
|
||||||
ModuleManager.instance.clearImportedModules()
|
ModuleManager.instance.clearImportedModules()
|
||||||
|
|
||||||
if (app.isPackaged) {
|
if (app.isPackaged) {
|
||||||
@ -56,9 +77,7 @@ export function handleAppIPCs() {
|
|||||||
} else {
|
} else {
|
||||||
for (const modulePath in ModuleManager.instance.requiredModules) {
|
for (const modulePath in ModuleManager.instance.requiredModules) {
|
||||||
delete require.cache[
|
delete require.cache[
|
||||||
require.resolve(
|
require.resolve(join(userSpacePath, 'extensions', modulePath))
|
||||||
join(userSpacePath, 'extensions', modulePath)
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
ExtensionManager.instance.setupExtensions()
|
ExtensionManager.instance.setupExtensions()
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { resolve, join } from 'path'
|
|||||||
import { WindowManager } from './../managers/window'
|
import { WindowManager } from './../managers/window'
|
||||||
import request from 'request'
|
import request from 'request'
|
||||||
import { createWriteStream } from 'fs'
|
import { createWriteStream } from 'fs'
|
||||||
import { getResourcePath } from './../utils/path'
|
import { DownloadEvent, DownloadRoute } from '@janhq/core'
|
||||||
const progress = require('request-progress')
|
const progress = require('request-progress')
|
||||||
|
|
||||||
export function handleDownloaderIPCs() {
|
export function handleDownloaderIPCs() {
|
||||||
@ -13,7 +13,7 @@ export function handleDownloaderIPCs() {
|
|||||||
* @param _event - The IPC event object.
|
* @param _event - The IPC event object.
|
||||||
* @param fileName - The name of the file being downloaded.
|
* @param fileName - The name of the file being downloaded.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('pauseDownload', async (_event, fileName) => {
|
ipcMain.handle(DownloadRoute.pauseDownload, async (_event, fileName) => {
|
||||||
DownloadManager.instance.networkRequests[fileName]?.pause()
|
DownloadManager.instance.networkRequests[fileName]?.pause()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ export function handleDownloaderIPCs() {
|
|||||||
* @param _event - The IPC event object.
|
* @param _event - The IPC event object.
|
||||||
* @param fileName - The name of the file being downloaded.
|
* @param fileName - The name of the file being downloaded.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('resumeDownload', async (_event, fileName) => {
|
ipcMain.handle(DownloadRoute.resumeDownload, async (_event, fileName) => {
|
||||||
DownloadManager.instance.networkRequests[fileName]?.resume()
|
DownloadManager.instance.networkRequests[fileName]?.resume()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -32,23 +32,19 @@ export function handleDownloaderIPCs() {
|
|||||||
* @param _event - The IPC event object.
|
* @param _event - The IPC event object.
|
||||||
* @param fileName - The name of the file being downloaded.
|
* @param fileName - The name of the file being downloaded.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('abortDownload', async (_event, fileName) => {
|
ipcMain.handle(DownloadRoute.abortDownload, async (_event, fileName) => {
|
||||||
const rq = DownloadManager.instance.networkRequests[fileName]
|
const rq = DownloadManager.instance.networkRequests[fileName]
|
||||||
DownloadManager.instance.networkRequests[fileName] = undefined
|
DownloadManager.instance.networkRequests[fileName] = undefined
|
||||||
rq?.abort()
|
rq?.abort()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.handle('getResourcePath', async (_event) => {
|
|
||||||
return getResourcePath()
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloads a file from a given URL.
|
* Downloads a file from a given URL.
|
||||||
* @param _event - The IPC event object.
|
* @param _event - The IPC event object.
|
||||||
* @param url - The URL to download the file from.
|
* @param url - The URL to download the file from.
|
||||||
* @param fileName - The name to give the downloaded file.
|
* @param fileName - The name to give the downloaded file.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('downloadFile', async (_event, url, fileName) => {
|
ipcMain.handle(DownloadRoute.downloadFile, async (_event, url, fileName) => {
|
||||||
const userDataPath = join(app.getPath('home'), 'jan')
|
const userDataPath = join(app.getPath('home'), 'jan')
|
||||||
const destination = resolve(userDataPath, fileName)
|
const destination = resolve(userDataPath, fileName)
|
||||||
const rq = request(url)
|
const rq = request(url)
|
||||||
@ -56,7 +52,7 @@ export function handleDownloaderIPCs() {
|
|||||||
progress(rq, {})
|
progress(rq, {})
|
||||||
.on('progress', function (state: any) {
|
.on('progress', function (state: any) {
|
||||||
WindowManager?.instance.currentWindow?.webContents.send(
|
WindowManager?.instance.currentWindow?.webContents.send(
|
||||||
'FILE_DOWNLOAD_UPDATE',
|
DownloadEvent.onFileDownloadUpdate,
|
||||||
{
|
{
|
||||||
...state,
|
...state,
|
||||||
fileName,
|
fileName,
|
||||||
@ -65,7 +61,7 @@ export function handleDownloaderIPCs() {
|
|||||||
})
|
})
|
||||||
.on('error', function (err: Error) {
|
.on('error', function (err: Error) {
|
||||||
WindowManager?.instance.currentWindow?.webContents.send(
|
WindowManager?.instance.currentWindow?.webContents.send(
|
||||||
'FILE_DOWNLOAD_ERROR',
|
DownloadEvent.onFileDownloadError,
|
||||||
{
|
{
|
||||||
fileName,
|
fileName,
|
||||||
err,
|
err,
|
||||||
@ -75,7 +71,7 @@ export function handleDownloaderIPCs() {
|
|||||||
.on('end', function () {
|
.on('end', function () {
|
||||||
if (DownloadManager.instance.networkRequests[fileName]) {
|
if (DownloadManager.instance.networkRequests[fileName]) {
|
||||||
WindowManager?.instance.currentWindow?.webContents.send(
|
WindowManager?.instance.currentWindow?.webContents.send(
|
||||||
'FILE_DOWNLOAD_COMPLETE',
|
DownloadEvent.onFileDownloadSuccess,
|
||||||
{
|
{
|
||||||
fileName,
|
fileName,
|
||||||
}
|
}
|
||||||
@ -83,7 +79,7 @@ export function handleDownloaderIPCs() {
|
|||||||
DownloadManager.instance.setRequest(fileName, undefined)
|
DownloadManager.instance.setRequest(fileName, undefined)
|
||||||
} else {
|
} else {
|
||||||
WindowManager?.instance.currentWindow?.webContents.send(
|
WindowManager?.instance.currentWindow?.webContents.send(
|
||||||
'FILE_DOWNLOAD_ERROR',
|
DownloadEvent.onFileDownloadError,
|
||||||
{
|
{
|
||||||
fileName,
|
fileName,
|
||||||
err: 'Download cancelled',
|
err: 'Download cancelled',
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { getExtension } from './../extension/store'
|
|||||||
import { removeExtension } from './../extension/store'
|
import { removeExtension } from './../extension/store'
|
||||||
import Extension from './../extension/extension'
|
import Extension from './../extension/extension'
|
||||||
import { getResourcePath, userSpacePath } from './../utils/path'
|
import { getResourcePath, userSpacePath } from './../utils/path'
|
||||||
|
import { ExtensionRoute } from '@janhq/core'
|
||||||
|
|
||||||
export function handleExtensionIPCs() {
|
export function handleExtensionIPCs() {
|
||||||
/**MARK: General handlers */
|
/**MARK: General handlers */
|
||||||
@ -23,7 +24,7 @@ export function handleExtensionIPCs() {
|
|||||||
* @returns The result of the invoked function.
|
* @returns The result of the invoked function.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
'extension:invokeExtensionFunc',
|
ExtensionRoute.invokeExtensionFunc,
|
||||||
async (_event, modulePath, method, ...args) => {
|
async (_event, modulePath, method, ...args) => {
|
||||||
const module = require(
|
const module = require(
|
||||||
/* webpackIgnore: true */ join(userSpacePath, 'extensions', modulePath)
|
/* webpackIgnore: true */ join(userSpacePath, 'extensions', modulePath)
|
||||||
@ -44,81 +45,59 @@ export function handleExtensionIPCs() {
|
|||||||
* @param _event - The IPC event object.
|
* @param _event - The IPC event object.
|
||||||
* @returns An array of paths to the base extensions.
|
* @returns An array of paths to the base extensions.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('extension:baseExtensions', async (_event) => {
|
ipcMain.handle(ExtensionRoute.baseExtensions, async (_event) => {
|
||||||
const baseExtensionPath = join(getResourcePath(), 'pre-install')
|
const baseExtensionPath = join(getResourcePath(), 'pre-install')
|
||||||
return readdirSync(baseExtensionPath)
|
return readdirSync(baseExtensionPath)
|
||||||
.filter((file) => extname(file) === '.tgz')
|
.filter((file) => extname(file) === '.tgz')
|
||||||
.map((file) => join(baseExtensionPath, file))
|
.map((file) => join(baseExtensionPath, file))
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the path to the user's extension directory.
|
|
||||||
* @param _event - The IPC event extension.
|
|
||||||
* @returns The path to the user's extension directory.
|
|
||||||
*/
|
|
||||||
ipcMain.handle('extension:extensionPath', async (_event) => {
|
|
||||||
return join(userSpacePath, 'extensions')
|
|
||||||
})
|
|
||||||
|
|
||||||
/**MARK: Extension Manager handlers */
|
/**MARK: Extension Manager handlers */
|
||||||
ipcMain.handle('extension:install', async (e, extensions) => {
|
ipcMain.handle(ExtensionRoute.installExtension, async (e, extensions) => {
|
||||||
// Install and activate all provided extensions
|
// Install and activate all provided extensions
|
||||||
const installed = await installExtensions(extensions)
|
const installed = await installExtensions(extensions)
|
||||||
return JSON.parse(JSON.stringify(installed))
|
return JSON.parse(JSON.stringify(installed))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Register IPC route to uninstall a extension
|
// Register IPC route to uninstall a extension
|
||||||
ipcMain.handle('extension:uninstall', async (e, extensions, reload) => {
|
ipcMain.handle(
|
||||||
// Uninstall all provided extensions
|
ExtensionRoute.uninstallExtension,
|
||||||
for (const ext of extensions) {
|
async (e, extensions, reload) => {
|
||||||
const extension = getExtension(ext)
|
// Uninstall all provided extensions
|
||||||
await extension.uninstall()
|
for (const ext of extensions) {
|
||||||
if (extension.name) removeExtension(extension.name)
|
const extension = getExtension(ext)
|
||||||
}
|
await extension.uninstall()
|
||||||
|
if (extension.name) removeExtension(extension.name)
|
||||||
|
}
|
||||||
|
|
||||||
// Reload all renderer pages if needed
|
// Reload all renderer pages if needed
|
||||||
reload && webContents.getAllWebContents().forEach((wc) => wc.reload())
|
reload && webContents.getAllWebContents().forEach((wc) => wc.reload())
|
||||||
return true
|
return true
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Register IPC route to update a extension
|
// Register IPC route to update a extension
|
||||||
ipcMain.handle('extension:update', async (e, extensions, reload) => {
|
ipcMain.handle(
|
||||||
// Update all provided extensions
|
ExtensionRoute.updateExtension,
|
||||||
const updated: Extension[] = []
|
async (e, extensions, reload) => {
|
||||||
for (const ext of extensions) {
|
// Update all provided extensions
|
||||||
const extension = getExtension(ext)
|
const updated: Extension[] = []
|
||||||
const res = await extension.update()
|
for (const ext of extensions) {
|
||||||
if (res) updated.push(extension)
|
const extension = getExtension(ext)
|
||||||
|
const res = await extension.update()
|
||||||
|
if (res) updated.push(extension)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload all renderer pages if needed
|
||||||
|
if (updated.length && reload)
|
||||||
|
webContents.getAllWebContents().forEach((wc) => wc.reload())
|
||||||
|
|
||||||
|
return JSON.parse(JSON.stringify(updated))
|
||||||
}
|
}
|
||||||
|
)
|
||||||
// Reload all renderer pages if needed
|
|
||||||
if (updated.length && reload)
|
|
||||||
webContents.getAllWebContents().forEach((wc) => wc.reload())
|
|
||||||
|
|
||||||
return JSON.parse(JSON.stringify(updated))
|
|
||||||
})
|
|
||||||
|
|
||||||
// Register IPC route to check if updates are available for a extension
|
|
||||||
ipcMain.handle('extension:updatesAvailable', (e, names) => {
|
|
||||||
const extensions = names
|
|
||||||
? names.map((name: string) => getExtension(name))
|
|
||||||
: getAllExtensions()
|
|
||||||
|
|
||||||
const updates: Record<string, Extension> = {}
|
|
||||||
for (const extension of extensions) {
|
|
||||||
updates[extension.name] = extension.isUpdateAvailable()
|
|
||||||
}
|
|
||||||
return updates
|
|
||||||
})
|
|
||||||
|
|
||||||
// Register IPC route to get the list of active extensions
|
// Register IPC route to get the list of active extensions
|
||||||
ipcMain.handle('extension:getActiveExtensions', () => {
|
ipcMain.handle(ExtensionRoute.getActiveExtensions, () => {
|
||||||
return JSON.parse(JSON.stringify(getActiveExtensions()))
|
return JSON.parse(JSON.stringify(getActiveExtensions()))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Register IPC route to toggle the active state of a extension
|
|
||||||
ipcMain.handle('extension:toggleExtensionActive', (e, plg, active) => {
|
|
||||||
const extension = getExtension(plg)
|
|
||||||
return JSON.parse(JSON.stringify(extension.setActive(active)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import fse from 'fs-extra'
|
|||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import readline from 'readline'
|
import readline from 'readline'
|
||||||
import { userSpacePath } from './../utils/path'
|
import { userSpacePath } from './../utils/path'
|
||||||
|
import { FileSystemRoute } from '@janhq/core'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles file system operations.
|
* Handles file system operations.
|
||||||
@ -15,7 +16,7 @@ export function handleFsIPCs() {
|
|||||||
* @returns A promise that resolves with the path to the user data directory.
|
* @returns A promise that resolves with the path to the user data directory.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
'getUserSpace',
|
FileSystemRoute.getUserSpace,
|
||||||
(): Promise<string> => Promise.resolve(userSpacePath)
|
(): Promise<string> => Promise.resolve(userSpacePath)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,12 +26,15 @@ export function handleFsIPCs() {
|
|||||||
* @param path - The path to check.
|
* @param path - The path to check.
|
||||||
* @returns A promise that resolves with a boolean indicating whether the path is a directory.
|
* @returns A promise that resolves with a boolean indicating whether the path is a directory.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('isDirectory', (_event, path: string): Promise<boolean> => {
|
ipcMain.handle(
|
||||||
const fullPath = join(userSpacePath, path)
|
FileSystemRoute.isDirectory,
|
||||||
return Promise.resolve(
|
(_event, path: string): Promise<boolean> => {
|
||||||
fs.existsSync(fullPath) && fs.lstatSync(fullPath).isDirectory()
|
const fullPath = join(userSpacePath, path)
|
||||||
)
|
return Promise.resolve(
|
||||||
})
|
fs.existsSync(fullPath) && fs.lstatSync(fullPath).isDirectory()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a file from the user data directory.
|
* Reads a file from the user data directory.
|
||||||
@ -38,17 +42,20 @@ export function handleFsIPCs() {
|
|||||||
* @param path - The path of the file to read.
|
* @param path - The path of the file to read.
|
||||||
* @returns A promise that resolves with the contents of the file.
|
* @returns A promise that resolves with the contents of the file.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('readFile', async (event, path: string): Promise<string> => {
|
ipcMain.handle(
|
||||||
return new Promise((resolve, reject) => {
|
FileSystemRoute.readFile,
|
||||||
fs.readFile(join(userSpacePath, path), 'utf8', (err, data) => {
|
async (event, path: string): Promise<string> => {
|
||||||
if (err) {
|
return new Promise((resolve, reject) => {
|
||||||
reject(err)
|
fs.readFile(join(userSpacePath, path), 'utf8', (err, data) => {
|
||||||
} else {
|
if (err) {
|
||||||
resolve(data)
|
reject(err)
|
||||||
}
|
} else {
|
||||||
|
resolve(data)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a file exists in the user data directory.
|
* Checks whether a file exists in the user data directory.
|
||||||
@ -56,7 +63,7 @@ export function handleFsIPCs() {
|
|||||||
* @param path - The path of the file to check.
|
* @param path - The path of the file to check.
|
||||||
* @returns A promise that resolves with a boolean indicating whether the file exists.
|
* @returns A promise that resolves with a boolean indicating whether the file exists.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('exists', async (_event, path: string) => {
|
ipcMain.handle(FileSystemRoute.exists, async (_event, path: string) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const fullPath = join(userSpacePath, path)
|
const fullPath = join(userSpacePath, path)
|
||||||
fs.existsSync(fullPath) ? resolve(true) : resolve(false)
|
fs.existsSync(fullPath) ? resolve(true) : resolve(false)
|
||||||
@ -71,7 +78,7 @@ export function handleFsIPCs() {
|
|||||||
* @returns A promise that resolves when the file has been written.
|
* @returns A promise that resolves when the file has been written.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
'writeFile',
|
FileSystemRoute.writeFile,
|
||||||
async (event, path: string, data: string): Promise<void> => {
|
async (event, path: string, data: string): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await fs.writeFileSync(join(userSpacePath, path), data, 'utf8')
|
await fs.writeFileSync(join(userSpacePath, path), data, 'utf8')
|
||||||
@ -87,13 +94,16 @@ export function handleFsIPCs() {
|
|||||||
* @param path - The path of the directory to create.
|
* @param path - The path of the directory to create.
|
||||||
* @returns A promise that resolves when the directory has been created.
|
* @returns A promise that resolves when the directory has been created.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('mkdir', async (event, path: string): Promise<void> => {
|
ipcMain.handle(
|
||||||
try {
|
FileSystemRoute.mkdir,
|
||||||
fs.mkdirSync(join(userSpacePath, path), { recursive: true })
|
async (event, path: string): Promise<void> => {
|
||||||
} catch (err) {
|
try {
|
||||||
console.error(`mkdir ${path} result: ${err}`)
|
fs.mkdirSync(join(userSpacePath, path), { recursive: true })
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`mkdir ${path} result: ${err}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a directory in the user data directory.
|
* Removes a directory in the user data directory.
|
||||||
@ -101,13 +111,16 @@ export function handleFsIPCs() {
|
|||||||
* @param path - The path of the directory to remove.
|
* @param path - The path of the directory to remove.
|
||||||
* @returns A promise that resolves when the directory is removed successfully.
|
* @returns A promise that resolves when the directory is removed successfully.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('rmdir', async (event, path: string): Promise<void> => {
|
ipcMain.handle(
|
||||||
try {
|
FileSystemRoute.rmdir,
|
||||||
await fs.rmSync(join(userSpacePath, path), { recursive: true })
|
async (event, path: string): Promise<void> => {
|
||||||
} catch (err) {
|
try {
|
||||||
console.error(`rmdir ${path} result: ${err}`)
|
await fs.rmSync(join(userSpacePath, path), { recursive: true })
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`rmdir ${path} result: ${err}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists the files in a directory in the user data directory.
|
* Lists the files in a directory in the user data directory.
|
||||||
@ -116,7 +129,7 @@ export function handleFsIPCs() {
|
|||||||
* @returns A promise that resolves with an array of file names.
|
* @returns A promise that resolves with an array of file names.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
'listFiles',
|
FileSystemRoute.listFiles,
|
||||||
async (event, path: string): Promise<string[]> => {
|
async (event, path: string): Promise<string[]> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.readdir(join(userSpacePath, path), (err, files) => {
|
fs.readdir(join(userSpacePath, path), (err, files) => {
|
||||||
@ -136,7 +149,7 @@ export function handleFsIPCs() {
|
|||||||
* @param filePath - The path to the file to delete.
|
* @param filePath - The path to the file to delete.
|
||||||
* @returns A string indicating the result of the operation.
|
* @returns A string indicating the result of the operation.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('deleteFile', async (_event, filePath) => {
|
ipcMain.handle(FileSystemRoute.deleteFile, async (_event, filePath) => {
|
||||||
try {
|
try {
|
||||||
await fs.unlinkSync(join(userSpacePath, filePath))
|
await fs.unlinkSync(join(userSpacePath, filePath))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -151,19 +164,25 @@ export function handleFsIPCs() {
|
|||||||
* @param data - The data to append to the file.
|
* @param data - The data to append to the file.
|
||||||
* @returns A promise that resolves when the file has been written.
|
* @returns A promise that resolves when the file has been written.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('appendFile', async (_event, path: string, data: string) => {
|
ipcMain.handle(
|
||||||
try {
|
FileSystemRoute.appendFile,
|
||||||
await fs.appendFileSync(join(userSpacePath, path), data, 'utf8')
|
async (_event, path: string, data: string) => {
|
||||||
} catch (err) {
|
try {
|
||||||
console.error(`appendFile ${path} result: ${err}`)
|
await fs.appendFileSync(join(userSpacePath, path), data, 'utf8')
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`appendFile ${path} result: ${err}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
ipcMain.handle('copyFile', async (_event, src: string, dest: string) => {
|
ipcMain.handle(
|
||||||
console.debug(`Copying file from ${src} to ${dest}`)
|
FileSystemRoute.copyFile,
|
||||||
|
async (_event, src: string, dest: string) => {
|
||||||
|
console.debug(`Copying file from ${src} to ${dest}`)
|
||||||
|
|
||||||
return fse.copySync(src, dest, { overwrite: false })
|
return fse.copySync(src, dest, { overwrite: false })
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a file line by line.
|
* Reads a file line by line.
|
||||||
@ -171,25 +190,28 @@ export function handleFsIPCs() {
|
|||||||
* @param path - The path of the file to read.
|
* @param path - The path of the file to read.
|
||||||
* @returns A promise that resolves with the contents of the file.
|
* @returns A promise that resolves with the contents of the file.
|
||||||
*/
|
*/
|
||||||
ipcMain.handle('readLineByLine', async (_event, path: string) => {
|
ipcMain.handle(
|
||||||
const fullPath = join(userSpacePath, path)
|
FileSystemRoute.readLineByLine,
|
||||||
|
async (_event, path: string) => {
|
||||||
|
const fullPath = join(userSpacePath, path)
|
||||||
|
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
try {
|
try {
|
||||||
const readInterface = readline.createInterface({
|
const readInterface = readline.createInterface({
|
||||||
input: fs.createReadStream(fullPath),
|
input: fs.createReadStream(fullPath),
|
||||||
})
|
|
||||||
const lines: any = []
|
|
||||||
readInterface
|
|
||||||
.on('line', function (line) {
|
|
||||||
lines.push(line)
|
|
||||||
})
|
})
|
||||||
.on('close', function () {
|
const lines: any = []
|
||||||
res(lines)
|
readInterface
|
||||||
})
|
.on('line', function (line) {
|
||||||
} catch (err) {
|
lines.push(line)
|
||||||
rej(err)
|
})
|
||||||
}
|
.on('close', function () {
|
||||||
})
|
res(lines)
|
||||||
})
|
})
|
||||||
|
} catch (err) {
|
||||||
|
rej(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
import { ipcMain, nativeTheme } from "electron";
|
|
||||||
|
|
||||||
export function handleThemesIPCs() {
|
|
||||||
/**
|
|
||||||
* Handles the "setNativeThemeLight" IPC message by setting the native theme source to "light".
|
|
||||||
* This will change the appearance of the app to the light theme.
|
|
||||||
*/
|
|
||||||
ipcMain.handle("setNativeThemeLight", () => {
|
|
||||||
nativeTheme.themeSource = "light";
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the "setNativeThemeDark" IPC message by setting the native theme source to "dark".
|
|
||||||
* This will change the appearance of the app to the dark theme.
|
|
||||||
*/
|
|
||||||
ipcMain.handle("setNativeThemeDark", () => {
|
|
||||||
nativeTheme.themeSource = "dark";
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the "setNativeThemeSystem" IPC message by setting the native theme source to "system".
|
|
||||||
* This will change the appearance of the app to match the system's current theme.
|
|
||||||
*/
|
|
||||||
ipcMain.handle("setNativeThemeSystem", () => {
|
|
||||||
nativeTheme.themeSource = "system";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,57 +1,58 @@
|
|||||||
import { app, dialog } from "electron";
|
import { app, dialog } from 'electron'
|
||||||
import { WindowManager } from "./../managers/window";
|
import { WindowManager } from './../managers/window'
|
||||||
import { autoUpdater } from "electron-updater";
|
import { autoUpdater } from 'electron-updater'
|
||||||
|
import { AppEvent } from '@janhq/core'
|
||||||
|
|
||||||
export function handleAppUpdates() {
|
export function handleAppUpdates() {
|
||||||
/* Should not check for update during development */
|
/* Should not check for update during development */
|
||||||
if (!app.isPackaged) {
|
if (!app.isPackaged) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
/* New Update Available */
|
/* New Update Available */
|
||||||
autoUpdater.on("update-available", async (_info: any) => {
|
autoUpdater.on('update-available', async (_info: any) => {
|
||||||
const action = await dialog.showMessageBox({
|
const action = await dialog.showMessageBox({
|
||||||
message: `Update available. Do you want to download the latest update?`,
|
message: `Update available. Do you want to download the latest update?`,
|
||||||
buttons: ["Download", "Later"],
|
buttons: ['Download', 'Later'],
|
||||||
});
|
})
|
||||||
if (action.response === 0) await autoUpdater.downloadUpdate();
|
if (action.response === 0) await autoUpdater.downloadUpdate()
|
||||||
});
|
})
|
||||||
|
|
||||||
/* App Update Completion Message */
|
/* App Update Completion Message */
|
||||||
autoUpdater.on("update-downloaded", async (_info: any) => {
|
autoUpdater.on('update-downloaded', async (_info: any) => {
|
||||||
WindowManager.instance.currentWindow?.webContents.send(
|
WindowManager.instance.currentWindow?.webContents.send(
|
||||||
"APP_UPDATE_COMPLETE",
|
AppEvent.onAppUpdateDownloadSuccess,
|
||||||
{}
|
{}
|
||||||
);
|
)
|
||||||
const action = await dialog.showMessageBox({
|
const action = await dialog.showMessageBox({
|
||||||
message: `Update downloaded. Please restart the application to apply the updates.`,
|
message: `Update downloaded. Please restart the application to apply the updates.`,
|
||||||
buttons: ["Restart", "Later"],
|
buttons: ['Restart', 'Later'],
|
||||||
});
|
})
|
||||||
if (action.response === 0) {
|
if (action.response === 0) {
|
||||||
autoUpdater.quitAndInstall();
|
autoUpdater.quitAndInstall()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
/* App Update Error */
|
/* App Update Error */
|
||||||
autoUpdater.on("error", (info: any) => {
|
autoUpdater.on('error', (info: any) => {
|
||||||
WindowManager.instance.currentWindow?.webContents.send(
|
WindowManager.instance.currentWindow?.webContents.send(
|
||||||
"APP_UPDATE_ERROR",
|
AppEvent.onAppUpdateDownloadError,
|
||||||
{}
|
{}
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
|
|
||||||
/* App Update Progress */
|
/* App Update Progress */
|
||||||
autoUpdater.on("download-progress", (progress: any) => {
|
autoUpdater.on('download-progress', (progress: any) => {
|
||||||
console.debug("app update progress: ", progress.percent);
|
console.debug('app update progress: ', progress.percent)
|
||||||
WindowManager.instance.currentWindow?.webContents.send(
|
WindowManager.instance.currentWindow?.webContents.send(
|
||||||
"APP_UPDATE_PROGRESS",
|
AppEvent.onAppUpdateDownloadUpdate,
|
||||||
{
|
{
|
||||||
percent: progress.percent,
|
percent: progress.percent,
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
autoUpdater.autoDownload = false;
|
autoUpdater.autoDownload = false
|
||||||
autoUpdater.autoInstallOnAppQuit = true;
|
autoUpdater.autoInstallOnAppQuit = true
|
||||||
if (process.env.CI !== "e2e") {
|
if (process.env.CI !== 'e2e') {
|
||||||
autoUpdater.checkForUpdates();
|
autoUpdater.checkForUpdates()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,62 +0,0 @@
|
|||||||
import { shell } from 'electron'
|
|
||||||
|
|
||||||
const { ipcRenderer } = require('electron')
|
|
||||||
|
|
||||||
export function appInvokers() {
|
|
||||||
const interfaces = {
|
|
||||||
/**
|
|
||||||
* Sets the native theme to light.
|
|
||||||
*/
|
|
||||||
setNativeThemeLight: () => ipcRenderer.invoke('setNativeThemeLight'),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the native theme to dark.
|
|
||||||
*/
|
|
||||||
setNativeThemeDark: () => ipcRenderer.invoke('setNativeThemeDark'),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the native theme to system default.
|
|
||||||
*/
|
|
||||||
setNativeThemeSystem: () => ipcRenderer.invoke('setNativeThemeSystem'),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the application data path.
|
|
||||||
* @returns {Promise<string>} A promise that resolves to the application data path.
|
|
||||||
*/
|
|
||||||
appDataPath: () => ipcRenderer.invoke('appDataPath'),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the application version.
|
|
||||||
* @returns {Promise<string>} A promise that resolves to the application version.
|
|
||||||
*/
|
|
||||||
appVersion: () => ipcRenderer.invoke('appVersion'),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens an external URL.
|
|
||||||
* @param {string} url - The URL to open.
|
|
||||||
* @returns {Promise<void>} A promise that resolves when the URL has been opened.
|
|
||||||
*/
|
|
||||||
openExternalUrl: (url: string) =>
|
|
||||||
ipcRenderer.invoke('openExternalUrl', url),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Relaunches the application.
|
|
||||||
* @returns {Promise<void>} A promise that resolves when the application has been relaunched.
|
|
||||||
*/
|
|
||||||
relaunch: () => ipcRenderer.invoke('relaunch'),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the application directory.
|
|
||||||
* @returns {Promise<void>} A promise that resolves when the application directory has been opened.
|
|
||||||
*/
|
|
||||||
openAppDirectory: () => ipcRenderer.invoke('openAppDirectory'),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the file explorer at a specific path.
|
|
||||||
* @param {string} path - The path to open in the file explorer.
|
|
||||||
*/
|
|
||||||
openFileExplorer: (path: string) => shell.openPath(path),
|
|
||||||
}
|
|
||||||
|
|
||||||
return interfaces
|
|
||||||
}
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
const { ipcRenderer } = require('electron')
|
|
||||||
|
|
||||||
export function downloadInvokers() {
|
|
||||||
const interfaces = {
|
|
||||||
/**
|
|
||||||
* Opens the file explorer at a specific path.
|
|
||||||
* @param {string} path - The path to open in the file explorer.
|
|
||||||
*/
|
|
||||||
downloadFile: (url: string, path: string) =>
|
|
||||||
ipcRenderer.invoke('downloadFile', url, path),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses the download of a file.
|
|
||||||
* @param {string} fileName - The name of the file whose download should be paused.
|
|
||||||
*/
|
|
||||||
pauseDownload: (fileName: string) =>
|
|
||||||
ipcRenderer.invoke('pauseDownload', fileName),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses the download of a file.
|
|
||||||
* @param {string} fileName - The name of the file whose download should be paused.
|
|
||||||
*/
|
|
||||||
resumeDownload: (fileName: string) =>
|
|
||||||
ipcRenderer.invoke('resumeDownload', fileName),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses the download of a file.
|
|
||||||
* @param {string} fileName - The name of the file whose download should be paused.
|
|
||||||
*/
|
|
||||||
abortDownload: (fileName: string) =>
|
|
||||||
ipcRenderer.invoke('abortDownload', fileName),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pauses the download of a file.
|
|
||||||
* @param {string} fileName - The name of the file whose download should be paused.
|
|
||||||
*/
|
|
||||||
onFileDownloadUpdate: (callback: any) =>
|
|
||||||
ipcRenderer.on('FILE_DOWNLOAD_UPDATE', callback),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listens for errors on file downloads.
|
|
||||||
* @param {Function} callback - The function to call when there is an error.
|
|
||||||
*/
|
|
||||||
onFileDownloadError: (callback: any) =>
|
|
||||||
ipcRenderer.on('FILE_DOWNLOAD_ERROR', callback),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listens for the successful completion of file downloads.
|
|
||||||
* @param {Function} callback - The function to call when a download is complete.
|
|
||||||
*/
|
|
||||||
onFileDownloadSuccess: (callback: any) =>
|
|
||||||
ipcRenderer.on('FILE_DOWNLOAD_COMPLETE', callback),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listens for updates on app update downloads.
|
|
||||||
* @param {Function} callback - The function to call when there is an update.
|
|
||||||
*/
|
|
||||||
onAppUpdateDownloadUpdate: (callback: any) =>
|
|
||||||
ipcRenderer.on('APP_UPDATE_PROGRESS', callback),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listens for errors on app update downloads.
|
|
||||||
* @param {Function} callback - The function to call when there is an error.
|
|
||||||
*/
|
|
||||||
onAppUpdateDownloadError: (callback: any) =>
|
|
||||||
ipcRenderer.on('APP_UPDATE_ERROR', callback),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listens for the successful completion of app update downloads.
|
|
||||||
* @param {Function} callback - The function to call when an update download is complete.
|
|
||||||
*/
|
|
||||||
onAppUpdateDownloadSuccess: (callback: any) =>
|
|
||||||
ipcRenderer.on('APP_UPDATE_COMPLETE', callback),
|
|
||||||
}
|
|
||||||
|
|
||||||
return interfaces
|
|
||||||
}
|
|
||||||
@ -1,78 +0,0 @@
|
|||||||
const { ipcRenderer } = require('electron')
|
|
||||||
|
|
||||||
export function extensionInvokers() {
|
|
||||||
const interfaces = {
|
|
||||||
/**
|
|
||||||
* Installs the given extensions.
|
|
||||||
* @param {any[]} extensions - The extensions to install.
|
|
||||||
*/
|
|
||||||
install(extensions: any[]) {
|
|
||||||
return ipcRenderer.invoke('extension:install', extensions)
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Uninstalls the given extensions.
|
|
||||||
* @param {any[]} extensions - The extensions to uninstall.
|
|
||||||
* @param {boolean} reload - Whether to reload after uninstalling.
|
|
||||||
*/
|
|
||||||
uninstall(extensions: any[], reload: boolean) {
|
|
||||||
return ipcRenderer.invoke('extension:uninstall', extensions, reload)
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Retrieves the active extensions.
|
|
||||||
*/
|
|
||||||
getActive() {
|
|
||||||
return ipcRenderer.invoke('extension:getActiveExtensions')
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Updates the given extensions.
|
|
||||||
* @param {any[]} extensions - The extensions to update.
|
|
||||||
* @param {boolean} reload - Whether to reload after updating.
|
|
||||||
*/
|
|
||||||
update(extensions: any[], reload: boolean) {
|
|
||||||
return ipcRenderer.invoke('extension:update', extensions, reload)
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Checks if updates are available for the given extension.
|
|
||||||
* @param {any} extension - The extension to check for updates.
|
|
||||||
*/
|
|
||||||
updatesAvailable(extension: any) {
|
|
||||||
return ipcRenderer.invoke('extension:updatesAvailable', extension)
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Toggles the active state of the given extension.
|
|
||||||
* @param {any} extension - The extension to toggle.
|
|
||||||
* @param {boolean} active - The new active state.
|
|
||||||
*/
|
|
||||||
toggleActive(extension: any, active: boolean) {
|
|
||||||
return ipcRenderer.invoke(
|
|
||||||
'extension:toggleExtensionActive',
|
|
||||||
extension,
|
|
||||||
active
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invokes a function of the given extension.
|
|
||||||
* @param {any} extension - The extension whose function should be invoked.
|
|
||||||
* @param {any} method - The function to invoke.
|
|
||||||
* @param {any[]} args - The arguments to pass to the function.
|
|
||||||
*/
|
|
||||||
invokeExtensionFunc: (extension: any, method: any, ...args: any[]) =>
|
|
||||||
ipcRenderer.invoke(
|
|
||||||
'extension:invokeExtensionFunc',
|
|
||||||
extension,
|
|
||||||
method,
|
|
||||||
...args
|
|
||||||
),
|
|
||||||
/**
|
|
||||||
* Retrieves the base extensions.
|
|
||||||
*/
|
|
||||||
baseExtensions: () => ipcRenderer.invoke('extension:baseExtensions'),
|
|
||||||
/**
|
|
||||||
* Retrieves the extension path.
|
|
||||||
*/
|
|
||||||
extensionPath: () => ipcRenderer.invoke('extension:extensionPath'),
|
|
||||||
}
|
|
||||||
|
|
||||||
return interfaces
|
|
||||||
}
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
const { ipcRenderer } = require('electron')
|
|
||||||
|
|
||||||
export function fsInvokers() {
|
|
||||||
const interfaces = {
|
|
||||||
/**
|
|
||||||
* Deletes a file at the specified path.
|
|
||||||
* @param {string} filePath - The path of the file to delete.
|
|
||||||
*/
|
|
||||||
deleteFile: (filePath: string) =>
|
|
||||||
ipcRenderer.invoke('deleteFile', filePath),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the path points to a directory.
|
|
||||||
* @param {string} filePath - The path to check.
|
|
||||||
*/
|
|
||||||
isDirectory: (filePath: string) =>
|
|
||||||
ipcRenderer.invoke('isDirectory', filePath),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the user's space.
|
|
||||||
*/
|
|
||||||
getUserSpace: () => ipcRenderer.invoke('getUserSpace'),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a file at the specified path.
|
|
||||||
* @param {string} path - The path of the file to read.
|
|
||||||
*/
|
|
||||||
readFile: (path: string) => ipcRenderer.invoke('readFile', path),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a file at the specified path.
|
|
||||||
* @param {string} path - The path of the file to read.
|
|
||||||
*/
|
|
||||||
exists: (path: string) => ipcRenderer.invoke('exists', path),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes data to a file at the specified path.
|
|
||||||
* @param {string} path - The path of the file to write to.
|
|
||||||
* @param {string} data - The data to write.
|
|
||||||
*/
|
|
||||||
writeFile: (path: string, data: string) =>
|
|
||||||
ipcRenderer.invoke('writeFile', path, data),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lists the files in a directory at the specified path.
|
|
||||||
* @param {string} path - The path of the directory to list files from.
|
|
||||||
*/
|
|
||||||
listFiles: (path: string) => ipcRenderer.invoke('listFiles', path),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends data to a file at the specified path.
|
|
||||||
* @param {string} path - The path of the file to append to.
|
|
||||||
* @param {string} data - The data to append.
|
|
||||||
*/
|
|
||||||
appendFile: (path: string, data: string) =>
|
|
||||||
ipcRenderer.invoke('appendFile', path, data),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads a file line by line at the specified path.
|
|
||||||
* @param {string} path - The path of the file to read.
|
|
||||||
*/
|
|
||||||
readLineByLine: (path: string) =>
|
|
||||||
ipcRenderer.invoke('readLineByLine', path),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a directory at the specified path.
|
|
||||||
* @param {string} path - The path where the directory should be created.
|
|
||||||
*/
|
|
||||||
mkdir: (path: string) => ipcRenderer.invoke('mkdir', path),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a directory at the specified path.
|
|
||||||
* @param {string} path - The path of the directory to remove.
|
|
||||||
*/
|
|
||||||
rmdir: (path: string) => ipcRenderer.invoke('rmdir', path),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies a file from the source path to the destination path.
|
|
||||||
* @param {string} src - The source path of the file to copy.
|
|
||||||
* @param {string} dest - The destination path where the file should be copied.
|
|
||||||
*/
|
|
||||||
copyFile: (src: string, dest: string) => ipcRenderer.invoke('copyFile', src, dest),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the resource path.
|
|
||||||
* @returns {Promise<string>} A promise that resolves to the resource path.
|
|
||||||
*/
|
|
||||||
getResourcePath: () => ipcRenderer.invoke('getResourcePath'),
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return interfaces
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { app, BrowserWindow } from 'electron'
|
import { app, BrowserWindow } from 'electron'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { setupMenu } from './utils/menu'
|
import { setupMenu } from './utils/menu'
|
||||||
import { createUserSpace, getResourcePath } from './utils/path'
|
import { createUserSpace } from './utils/path'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Managers
|
* Managers
|
||||||
@ -14,7 +14,6 @@ import { ExtensionManager } from './managers/extension'
|
|||||||
* IPC Handlers
|
* IPC Handlers
|
||||||
**/
|
**/
|
||||||
import { handleDownloaderIPCs } from './handlers/download'
|
import { handleDownloaderIPCs } from './handlers/download'
|
||||||
import { handleThemesIPCs } from './handlers/theme'
|
|
||||||
import { handleExtensionIPCs } from './handlers/extension'
|
import { handleExtensionIPCs } from './handlers/extension'
|
||||||
import { handleAppIPCs } from './handlers/app'
|
import { handleAppIPCs } from './handlers/app'
|
||||||
import { handleAppUpdates } from './handlers/update'
|
import { handleAppUpdates } from './handlers/update'
|
||||||
@ -79,7 +78,6 @@ function createMainWindow() {
|
|||||||
function handleIPCs() {
|
function handleIPCs() {
|
||||||
handleFsIPCs()
|
handleFsIPCs()
|
||||||
handleDownloaderIPCs()
|
handleDownloaderIPCs()
|
||||||
handleThemesIPCs()
|
|
||||||
handleExtensionIPCs()
|
handleExtensionIPCs()
|
||||||
handleAppIPCs()
|
handleAppIPCs()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,6 +67,7 @@
|
|||||||
"build:publish:linux": "tsc -p . && electron-builder -p onTagOrDraft -l deb"
|
"build:publish:linux": "tsc -p . && electron-builder -p onTagOrDraft -l deb"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@janhq/core": "link:./core",
|
||||||
"@npmcli/arborist": "^7.1.0",
|
"@npmcli/arborist": "^7.1.0",
|
||||||
"@types/request": "^2.48.12",
|
"@types/request": "^2.48.12",
|
||||||
"@uiball/loaders": "^1.3.0",
|
"@uiball/loaders": "^1.3.0",
|
||||||
|
|||||||
@ -3,19 +3,27 @@
|
|||||||
* @module preload
|
* @module preload
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO: Refactor this file for less dependencies and more modularity
|
import { APIEvents, APIRoutes } from '@janhq/core'
|
||||||
// TODO: Most of the APIs should be done using RestAPIs from extensions
|
import { contextBridge, ipcRenderer } from 'electron'
|
||||||
|
|
||||||
import { fsInvokers } from './invokers/fs'
|
const interfaces: { [key: string]: (...args: any[]) => any } = {}
|
||||||
import { appInvokers } from './invokers/app'
|
|
||||||
import { downloadInvokers } from './invokers/download'
|
|
||||||
import { extensionInvokers } from './invokers/extension'
|
|
||||||
|
|
||||||
const { contextBridge } = require('electron')
|
// Loop over each route in APIRoutes
|
||||||
|
APIRoutes.forEach((method) => {
|
||||||
contextBridge.exposeInMainWorld('electronAPI', {
|
// For each method, create a function on the interfaces object
|
||||||
...extensionInvokers(),
|
// This function invokes the method on the ipcRenderer with any provided arguments
|
||||||
...downloadInvokers(),
|
interfaces[method] = (...args: any[]) => ipcRenderer.invoke(method, ...args)
|
||||||
...fsInvokers(),
|
})
|
||||||
...appInvokers(),
|
|
||||||
|
// Loop over each method in APIEvents
|
||||||
|
APIEvents.forEach((method) => {
|
||||||
|
// For each method, create a function on the interfaces object
|
||||||
|
// This function sets up an event listener on the ipcRenderer for the method
|
||||||
|
// The handler for the event is provided as an argument to the function
|
||||||
|
interfaces[method] = (handler: any) => ipcRenderer.on(method, handler)
|
||||||
|
})
|
||||||
|
// Expose the 'interfaces' object in the main world under the name 'electronAPI'
|
||||||
|
// This allows the renderer process to access these methods directly
|
||||||
|
contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
|
...interfaces,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -118,7 +118,7 @@ export default function EventHandler({ children }: { children: ReactNode }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (window.core.events) {
|
if (window.core?.events) {
|
||||||
events.on(EventName.OnMessageResponse, handleNewMessageResponse)
|
events.on(EventName.OnMessageResponse, handleNewMessageResponse)
|
||||||
events.on(EventName.OnMessageUpdate, handleMessageResponseUpdate)
|
events.on(EventName.OnMessageUpdate, handleMessageResponseUpdate)
|
||||||
events.on(EventName.OnModelReady, handleModelReady)
|
events.on(EventName.OnModelReady, handleModelReady)
|
||||||
|
|||||||
@ -53,7 +53,7 @@ const Providers = (props: PropsWithChildren) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (setupCore) {
|
if (setupCore) {
|
||||||
// Electron
|
// Electron
|
||||||
if (window && window.core.api) {
|
if (window && window.core?.api) {
|
||||||
setupExtensions()
|
setupExtensions()
|
||||||
} else {
|
} else {
|
||||||
// Host
|
// Host
|
||||||
|
|||||||
@ -58,7 +58,7 @@ export class ExtensionManager {
|
|||||||
* @returns An array of extensions.
|
* @returns An array of extensions.
|
||||||
*/
|
*/
|
||||||
async getActive(): Promise<Extension[]> {
|
async getActive(): Promise<Extension[]> {
|
||||||
const res = await window.core.api?.getActive()
|
const res = await window.core?.api?.getActiveExtensions()
|
||||||
if (!res || !Array.isArray(res)) return []
|
if (!res || !Array.isArray(res)) return []
|
||||||
|
|
||||||
const extensions: Extension[] = res.map(
|
const extensions: Extension[] = res.map(
|
||||||
@ -119,7 +119,7 @@ export class ExtensionManager {
|
|||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const res = await window.core.api?.install(extensions)
|
const res = await window.core?.api?.installExtension(extensions)
|
||||||
if (res.cancelled) return false
|
if (res.cancelled) return false
|
||||||
return res.map(async (ext: any) => {
|
return res.map(async (ext: any) => {
|
||||||
const extension = new Extension(ext.name, ext.url, ext.active)
|
const extension = new Extension(ext.name, ext.url, ext.active)
|
||||||
@ -138,7 +138,7 @@ export class ExtensionManager {
|
|||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return window.core.api?.uninstall(extensions, reload)
|
return window.core?.api?.uninstallExtension(extensions, reload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import {
|
|||||||
threadsAtom,
|
threadsAtom,
|
||||||
setActiveThreadIdAtom,
|
setActiveThreadIdAtom,
|
||||||
threadStatesAtom,
|
threadStatesAtom,
|
||||||
activeThreadAtom,
|
|
||||||
updateThreadAtom,
|
updateThreadAtom,
|
||||||
} from '@/helpers/atoms/Conversation.atom'
|
} from '@/helpers/atoms/Conversation.atom'
|
||||||
|
|
||||||
@ -67,7 +66,7 @@ export const useCreateNewThread = () => {
|
|||||||
top_p: 0,
|
top_p: 0,
|
||||||
stream: false,
|
stream: false,
|
||||||
},
|
},
|
||||||
engine: undefined
|
engine: undefined,
|
||||||
},
|
},
|
||||||
instructions: assistant.instructions,
|
instructions: assistant.instructions,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ export function useGetAppVersion() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const getAppVersion = () => {
|
const getAppVersion = () => {
|
||||||
window.core.api?.appVersion().then((version: string | undefined) => {
|
window.core?.api?.appVersion().then((version: string | undefined) => {
|
||||||
setVersion(version ?? '')
|
setVersion(version ?? '')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,7 +67,7 @@ const ExtensionCatalog = () => {
|
|||||||
// Send the filename of the to be installed extension
|
// Send the filename of the to be installed extension
|
||||||
// to the main process for installation
|
// to the main process for installation
|
||||||
const installed = await extensionManager.install([extensionFile])
|
const installed = await extensionManager.install([extensionFile])
|
||||||
if (installed) window.core.api?.relaunch()
|
if (installed) window.core?.api?.relaunch()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,7 +80,7 @@ const ExtensionCatalog = () => {
|
|||||||
// Send the filename of the to be uninstalled extension
|
// Send the filename of the to be uninstalled extension
|
||||||
// to the main process for removal
|
// to the main process for removal
|
||||||
const res = await extensionManager.uninstall([name])
|
const res = await extensionManager.uninstall([name])
|
||||||
if (res) window.core.api?.relaunch()
|
if (res) window.core?.api?.relaunch()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
8
web/types/index.d.ts
vendored
8
web/types/index.d.ts
vendored
@ -1,11 +1,17 @@
|
|||||||
|
import { APIFunctions } from '@janhq/core'
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
export {}
|
export {}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
declare const PLUGIN_CATALOG: string
|
declare const PLUGIN_CATALOG: string
|
||||||
declare const VERSION: string
|
declare const VERSION: string
|
||||||
|
interface Core {
|
||||||
|
api: APIFunctions
|
||||||
|
events: EventEmitter
|
||||||
|
}
|
||||||
interface Window {
|
interface Window {
|
||||||
core?: any | undefined
|
core?: Core | undefined
|
||||||
electronAPI?: any | undefined
|
electronAPI?: any | undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user