add: base module

This commit is contained in:
2025-03-10 16:29:47 +08:00
parent 8b59a8e21a
commit 3a583a3619
40 changed files with 2698 additions and 218 deletions

3
src/main/app.ts Normal file
View File

@@ -0,0 +1,3 @@
import { useContextKey } from '@kevisual/use-config/context';
export { log, getLogPath } from './logger.ts';

View File

@@ -1,7 +1,7 @@
import { ipcMain } from 'electron';
import { getAppList, getCacheAssistantConfig, setConfig } from '@/modules/config';
import { installApp, uninstallApp } from '../proxy/install';
import { relunch } from '../window/relunch';
import { getAppList, getCacheAssistantConfig, setConfig } from '@/modules/config/index.ts';
import { installApp, uninstallApp } from '../proxy/install.ts';
import { relunch } from '../window/relunch.ts';
export const handle = () => {
ipcMain.handle('get-app-list', (event, data) => {

View File

@@ -1,18 +1,23 @@
import { app, BrowserWindow, ipcMain, session } from 'electron';
import { app, BrowserWindow } from 'electron';
import * as path from 'path';
import { fileURLToPath } from 'url';
import { LocalElectronAppUrl } from '../modules/config';
import { createSession } from './session';
import { handle } from './handle';
import { loadMenu } from './menu';
import { checkShowPage } from './window/page';
import { createIntroducePage } from './window/page/introduce';
import { createSession } from './session/index.ts';
import { handle } from './handle/index.ts';
import { loadMenu } from './menu/index.ts';
import { getLogPath, log } from './app.ts';
import { checkShowPage } from './window/page/index.ts';
import { closeProcess, createProcess } from './process/index.ts';
import { getElectronResourcePath } from './system/env.ts';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
let mainWindow: BrowserWindow | null;
async function createWindow() {
const resourcePath = getElectronResourcePath();
log.info('resourcePath', resourcePath);
log.info('createWindow');
log.info('path', getLogPath());
const _session = createSession();
mainWindow = new BrowserWindow({
width: 800,
@@ -20,6 +25,7 @@ async function createWindow() {
webPreferences: {
preload: path.join(__dirname, 'preload.js'), // 如果有 preload 脚本
session: _session,
webSecurity: false,
},
});
loadMenu();
@@ -30,12 +36,16 @@ async function createWindow() {
});
}
app.on('ready', createWindow);
app.on('ready', async () => {
await createProcess();
createWindow();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
closeProcess();
});
app.on('activate', () => {

View File

@@ -1,10 +1,11 @@
import { createEnterPage } from '../window/page/enter';
import { createEnterPage } from '../window/page/enter.ts';
import { BrowserWindow, Menu, app } from 'electron';
import path from 'path';
import { getLogPath, log } from '../logger';
import { createAppPackagesPage } from '../window/page/app-packages';
import { relunch } from '../window/relunch';
import { getLogPath, log } from '../logger.ts';
import { createAppPackagesPage } from '../window/page/app-packages.ts';
import { relunch } from '../window/relunch.ts';
import { checkShowPage } from '../window/page/index.ts';
export const loadMenu = () => {
const template = [
{
@@ -32,11 +33,40 @@ export const loadMenu = () => {
// },
],
},
{
label: '打开应用',
submenu: [
{
label: '首页',
click: () => {
// 获取当前window
const mainWindow = BrowserWindow.getFocusedWindow();
if (mainWindow) {
checkShowPage(mainWindow);
}
},
},
{
label: '打开配置',
click: async () => {
createEnterPage();
},
},
{
label: '打开应用市场',
click: async () => {
createAppPackagesPage();
},
},
],
},
{
label: '编辑',
submenu: [
{ label: '复制', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
{ label: '粘贴', accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
{ label: '撤销', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' },
{ label: '全选', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' },
],
},
{
@@ -72,13 +102,6 @@ export const loadMenu = () => {
label: '帮助',
role: 'help',
submenu: [
{
label: '文档',
click: async () => {
const { shell } = require('electron');
// shell.openExternal('http://adstudio.nisar.ai/docs/');
},
},
{
label: '打开日志',
click: async () => {
@@ -87,18 +110,6 @@ export const loadMenu = () => {
shell.openExternal('file://' + path.join(getLogPath()));
},
},
{
label: '打开配置',
click: async () => {
createEnterPage();
},
},
{
label: '打开应用市场',
click: async () => {
createAppPackagesPage();
},
},
],
},
];

82
src/main/process/index.ts Normal file
View File

@@ -0,0 +1,82 @@
// import { AssistantProcess } from '@kevisual/assistant-module/assistant-process';
import path from 'path';
import { fork } from 'child_process';
import { log } from '../app.ts';
import { isMac, isDev, getElectronResourcePath } from '../system/env.ts';
import { setProcessPid, getProcessPid, removeProcessPid } from '../../modules/config/process-pid.ts';
export const getAssistantCenterPath = () => {
const resourcePath = getElectronResourcePath();
if (isDev) {
return path.join(resourcePath, '../dist');
}
if (isMac()) {
return path.join(resourcePath, 'dist');
}
return path.join(resourcePath, 'dist');
};
// export const assistantCenterPath = path.join(__dirname, '../dist');
export const assistantCenterPath = getAssistantCenterPath();
export const assistantProcessPath = path.join(assistantCenterPath, 'dist/app.mjs');
// export const assistantProcess = new AssistantProcess(assistantPath);
export const processConfig = {
assistantCenterPath,
assistantProcessPath,
port: 51015,
process: null,
};
export const getOrigin = () => {
return `https://localhost:${processConfig.port}`;
};
export const createProcess = async () => {
log.info('createProcess', assistantProcessPath, 'cwd', assistantCenterPath);
const pid = getProcessPid();
if (pid) {
removeProcessPid();
await new Promise((resolve) => setTimeout(resolve, 1000));
}
return new Promise((resolve, reject) => {
// const signal = new AbortSignal();
try {
const assistantProcess = fork(assistantProcessPath, {
cwd: assistantCenterPath,
// signal,
stdio: 'inherit',
env: {
...process.env,
// KEVISUAL_URL: 'https://kevisual.xiongxiao.me',
KEVISUAL_URL: 'https://kevisual.silkyai.cn',
NODE_ENV_PARENT: 'fork',
},
});
assistantProcess.on('message', (message) => {
log.log('assistantProcess message', typeof message, message);
// if (message.toString().includes(checkString)) {
// resolve(assistantProcess);
// }
if (typeof message === 'object') {
const msg = message as { type: string; data?: { port?: number } };
if (msg.type === 'fork') {
resolve({ process: assistantProcess, port: msg.data?.port || processConfig.port });
}
}
});
assistantProcess.on('error', (error) => {
log.error(error);
});
processConfig.process = assistantProcess;
setProcessPid(assistantProcess.pid);
return assistantProcess;
} catch (error) {
log.error(error);
reject(error);
}
});
};
export const closeProcess = () => {
log.info('closeProcess');
removeProcessPid();
};

View File

@@ -1,6 +1,6 @@
import path from 'path';
import fs from 'fs';
import { appDir, kevisualUrl, addAppConfig, getAppConfig, setAppConfig, getCacheAssistantConfig, setConfig } from '../../modules/config';
import { appDir, kevisualUrl, addAppConfig, getAppConfig, setAppConfig, getCacheAssistantConfig, setConfig } from '../../modules/config/index.ts';
export const demoData = {
id: '471ee96f-d7d8-4da1-b84f-4a34f4732f16',

View File

@@ -1,57 +1,19 @@
import { app, BrowserWindow, ipcMain, session } from 'electron';
import { getCacheAssistantConfig, appDir, LocalElectronAppUrl } from '../../modules/config';
import { net } from 'electron';
import path from 'path';
import * as url from 'url';
import { checkFileExists } from '../../modules/file';
import { apiProxyList } from '../proxy/api-proxy';
import { session } from 'electron';
let _session: Electron.Session;
export const createSession = () => {
if (_session) {
return _session;
}
// 创建一个持久化的会话
_session = session.fromPartition('persist:app');
_session.protocol.handle('https', async (req) => {
const requrl = req.url;
const newReqUrl = new URL(requrl);
const localOrigin = new URL(LocalElectronAppUrl).origin;
if (newReqUrl.origin !== localOrigin) {
// 不拦截
return net.fetch(req.url, { bypassCustomProtocolHandlers: true });
}
const apiProxy = apiProxyList.find((_proxy: any) => newReqUrl.pathname.startsWith(_proxy.path));
if (apiProxy) {
const pageApi = getCacheAssistantConfig().pageApi || '';
if (!pageApi) {
return new Response(`App Page Api Not Set, please set it first`);
}
const newPageUrl = new URL(req.url, pageApi);
return net.fetch(newPageUrl.toString(), { bypassCustomProtocolHandlers: true });
}
const [user, key] = newReqUrl.pathname.split('/').slice(1);
const proxyList = getCacheAssistantConfig().proxy || [];
const proxy = proxyList.find((_proxy: any) => newReqUrl.pathname.startsWith(_proxy.path));
if (proxy) {
try {
const relativePath = path.join(appDir, newReqUrl.pathname);
const indexHtml = path.join(appDir, user, key, 'index.html');
console.log('relativePath', relativePath);
if (checkFileExists(relativePath, true)) {
const res = await net.fetch(url.pathToFileURL(relativePath).toString());
return res;
} else {
const res = await net.fetch(url.pathToFileURL(indexHtml).toString());
return res;
}
} catch (error) {
console.error(error);
}
return new Response('App is Running Error, please reinstall it or refresh the page');
}
return new Response(`App Not Install, please install it first,user/app: [${user}/${key}]`);
// Ignore certificate errors (for development only)
_session.webRequest.onBeforeSendHeaders((details, callback) => {
details.requestHeaders['User-Agent'] = 'silky-assistant';
callback({ cancel: false, requestHeaders: details.requestHeaders });
});
_session.setCertificateVerifyProc((request, callback) => {
callback(0); // 0 means trust the certificate
});
return _session;
};

21
src/main/system/env.ts Normal file
View File

@@ -0,0 +1,21 @@
import { app } from 'electron';
export const isDev = () => {
return process.env.NODE_ENV === 'development';
};
export const isMac = () => {
return process.platform === 'darwin';
};
export const isWin = () => {
return process.platform === 'win32';
};
export const isLinux = () => {
return process.platform === 'linux';
};
export const getElectronResourcePath = () => {
return app.getAppPath();
};

View File

@@ -1,19 +1,10 @@
import { BrowserWindow } from 'electron';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
import { getOrigin } from '@/main/process/index.ts';
import { createWinodw } from './create-window.ts';
export const createAppPackagesPage = (window?: BrowserWindow) => {
const mainWindow =
window ||
new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'), // 如果有 preload 脚本
},
});
mainWindow.loadFile(path.join(__dirname, '../renderer/packages/index.html')); // Vite 构建后的文件
const mainWindow = createWinodw(window);
const url = new URL('/root/assistant-base-app/?link=packages', getOrigin());
mainWindow.loadURL(url.toString());
return mainWindow;
};

View File

@@ -0,0 +1,22 @@
import { BrowserWindow } from 'electron';
import { fileURLToPath } from 'url';
import path from 'path';
import { createSession } from '../../session/index.ts';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export const createWinodw = (window: BrowserWindow, opts?: any) => {
if (window) return window;
const _session = createSession();
return new BrowserWindow({
width: 800,
height: 600,
...opts,
webPreferences: {
preload: path.join(__dirname, 'preload.js'), // 如果有 preload 脚本
session: _session,
webSecurity: false,
...opts?.webPreferences,
},
});
};

View File

@@ -1,19 +1,10 @@
import { BrowserWindow } from 'electron';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
import { getOrigin } from '@/main/process/index.ts';
import { createWinodw } from './create-window.ts';
export const createEnterPage = (window?: BrowserWindow) => {
const mainWindow =
window ||
new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'), // 如果有 preload 脚本
},
});
mainWindow.loadFile(path.join(__dirname, '../renderer/enter/index.html')); // Vite 构建后的文件
const mainWindow = createWinodw(window);
const url = new URL('/root/assistant-base-app/?link=enter', getOrigin());
mainWindow.loadURL(url.toString());
return mainWindow;
};

View File

@@ -1,18 +1,36 @@
import { getCacheAssistantConfig, LocalElectronAppUrl } from '@/modules/config';
import { createEnterPage } from './enter';
import { createAppPackagesPage } from './app-packages';
import { getCacheAssistantConfig } from '@/modules/config/index.ts';
import { createEnterPage } from './enter.ts';
import { createAppPackagesPage } from './app-packages.ts';
import { BrowserWindow } from 'electron';
import { getOrigin } from '@/main/process/index.ts';
import { createWinodw } from './create-window.ts';
export const checkShowPage = async (window?: BrowserWindow) => {
const assistantConfig = getCacheAssistantConfig();
const { pageApi, proxy } = assistantConfig;
const { pageApi, proxy, loadURL } = assistantConfig;
if (!pageApi) {
createEnterPage(window);
return;
return createEnterPage(window);
}
if (!proxy || proxy.length === 0) {
createAppPackagesPage(window);
return;
return createAppPackagesPage(window);
}
return window?.loadURL(LocalElectronAppUrl);
window = createWinodw(window);
let defaultURL = getOrigin() + '/web/note/';
if (loadURL) {
const url = new URL(loadURL, getOrigin());
const urls = url.pathname.split('/');
const [_, user, app] = urls;
let _loadURL = url.toString();
if (!user && !app) {
_loadURL = defaultURL;
}
if (app && urls.length === 3) {
_loadURL = url.toString() + '/';
}
console.log('url loadURL', _loadURL);
window?.loadURL(_loadURL);
return window;
}
window?.loadURL(defaultURL);
return window;
};