init
This commit is contained in:
35
src/main/handle/index.ts
Normal file
35
src/main/handle/index.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { ipcMain } from 'electron';
|
||||
import { getAppList, getCacheAssistantConfig, setConfig } from '@/modules/config';
|
||||
import { installApp, uninstallApp } from '../proxy/install';
|
||||
import { relunch } from '../window/relunch';
|
||||
|
||||
export const handle = () => {
|
||||
ipcMain.handle('get-app-list', (event, data) => {
|
||||
// 获取应用路径
|
||||
const appList = getAppList();
|
||||
return appList;
|
||||
});
|
||||
|
||||
ipcMain.handle('install-app', (event, data) => {
|
||||
console.log('install-app', data.user, data.key, data.version);
|
||||
return installApp(data);
|
||||
});
|
||||
|
||||
ipcMain.handle('uninstall-app', (event, data) => {
|
||||
console.log('uninstall-app', data.user, data.key, data.version);
|
||||
return uninstallApp(data);
|
||||
});
|
||||
|
||||
ipcMain.handle('save-app-config', (event, data) => {
|
||||
console.log('save-app-config', data);
|
||||
if (!data) {
|
||||
return getCacheAssistantConfig();
|
||||
}
|
||||
const config = getCacheAssistantConfig();
|
||||
return setConfig({ ...config, ...data });
|
||||
});
|
||||
|
||||
ipcMain.handle('relunch', () => {
|
||||
relunch();
|
||||
});
|
||||
};
|
||||
47
src/main/index.ts
Normal file
47
src/main/index.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { app, BrowserWindow, ipcMain, session } 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';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
let mainWindow: BrowserWindow | null;
|
||||
|
||||
async function createWindow() {
|
||||
const _session = createSession();
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'), // 如果有 preload 脚本
|
||||
session: _session,
|
||||
},
|
||||
});
|
||||
loadMenu();
|
||||
await checkShowPage(mainWindow);
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null;
|
||||
});
|
||||
}
|
||||
|
||||
app.on('ready', createWindow);
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
if (mainWindow === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
handle();
|
||||
6
src/main/logger.ts
Normal file
6
src/main/logger.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import log from 'electron-log';
|
||||
// $Home/Library/Logs/ads-desktop-electron/main.log
|
||||
log.initialize({ preload: true });
|
||||
export { log };
|
||||
|
||||
export const getLogPath = () => log.transports.file.getFile().path;
|
||||
117
src/main/menu/index.ts
Normal file
117
src/main/menu/index.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { createEnterPage } from '../window/page/enter';
|
||||
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';
|
||||
export const loadMenu = () => {
|
||||
const template = [
|
||||
{
|
||||
label: app.name,
|
||||
submenu: [
|
||||
{
|
||||
label: '关于',
|
||||
role: 'about',
|
||||
},
|
||||
{
|
||||
label: '退出',
|
||||
click: () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
} else {
|
||||
app.exit();
|
||||
}
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: '检查更新',
|
||||
// click: () => {
|
||||
// autoUpdater.checkForUpdatesAndNotify();
|
||||
// },
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '编辑',
|
||||
submenu: [
|
||||
{ label: '复制', accelerator: 'CmdOrCtrl+C', selector: 'copy:' },
|
||||
{ label: '粘贴', accelerator: 'CmdOrCtrl+V', selector: 'paste:' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '查看',
|
||||
role: 'view',
|
||||
submenu: [
|
||||
{
|
||||
label: '刷新',
|
||||
role: 'reload',
|
||||
},
|
||||
{
|
||||
label: '强制刷新',
|
||||
role: 'forcereload',
|
||||
},
|
||||
{
|
||||
label: '重启',
|
||||
click: () => {
|
||||
relunch();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '打开开发者工具',
|
||||
click: () => {
|
||||
const mainWindow = BrowserWindow.getFocusedWindow();
|
||||
if (mainWindow) {
|
||||
openDevTools(mainWindow);
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '帮助',
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{
|
||||
label: '文档',
|
||||
click: async () => {
|
||||
const { shell } = require('electron');
|
||||
// shell.openExternal('http://adstudio.nisar.ai/docs/');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '打开日志',
|
||||
click: async () => {
|
||||
const { shell } = require('electron');
|
||||
log.transports.file.fileName;
|
||||
shell.openExternal('file://' + path.join(getLogPath()));
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '打开配置',
|
||||
click: async () => {
|
||||
createEnterPage();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '打开应用市场',
|
||||
click: async () => {
|
||||
createAppPackagesPage();
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
// @ts-ignore
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(menu);
|
||||
};
|
||||
|
||||
export const openDevTools = (mainWindow: BrowserWindow) => {
|
||||
let window = mainWindow.getBrowserView() ? mainWindow.getBrowserView() : mainWindow;
|
||||
if (window.webContents.isDevToolsOpened()) {
|
||||
window.webContents.closeDevTools();
|
||||
} else {
|
||||
window.webContents.openDevTools();
|
||||
}
|
||||
};
|
||||
19
src/main/preload.js
Normal file
19
src/main/preload.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
const windowChannels = ['relunch'];
|
||||
const validChannels = ['get-app-list', 'install-app', 'uninstall-app', 'save-app-config', ...windowChannels];
|
||||
|
||||
contextBridge.exposeInMainWorld('electron', {
|
||||
ipcRenderer: {
|
||||
on(channel, func) {
|
||||
if (validChannels.includes(channel)) {
|
||||
ipcRenderer.on(channel, func);
|
||||
}
|
||||
},
|
||||
invoke(channel, ...args) {
|
||||
if (validChannels.includes(channel)) {
|
||||
return ipcRenderer.invoke(channel, ...args);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
12
src/main/proxy/api-proxy.ts
Normal file
12
src/main/proxy/api-proxy.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export const apiProxyList = [
|
||||
{
|
||||
path: '/v1',
|
||||
},
|
||||
{
|
||||
path: '/v2',
|
||||
},
|
||||
{
|
||||
path: '/api',
|
||||
},
|
||||
];
|
||||
|
||||
0
src/main/proxy/index.ts
Normal file
0
src/main/proxy/index.ts
Normal file
141
src/main/proxy/install.ts
Normal file
141
src/main/proxy/install.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { appDir, kevisualUrl, addAppConfig, getAppConfig, setAppConfig, getCacheAssistantConfig, setConfig } from '../../modules/config';
|
||||
|
||||
export const demoData = {
|
||||
id: '471ee96f-d7d8-4da1-b84f-4a34f4732f16',
|
||||
title: 'tiptap',
|
||||
description: '',
|
||||
data: {
|
||||
files: [
|
||||
{
|
||||
name: 'README.md',
|
||||
path: 'user/tiptap/0.0.1/README.md',
|
||||
},
|
||||
{
|
||||
name: 'app.css',
|
||||
path: 'user/tiptap/0.0.1/app.css',
|
||||
},
|
||||
{
|
||||
name: 'app.js',
|
||||
path: 'user/tiptap/0.0.1/app.js',
|
||||
},
|
||||
{
|
||||
name: 'create-BxEwtceK.js',
|
||||
path: 'user/tiptap/0.0.1/create-BxEwtceK.js',
|
||||
},
|
||||
{
|
||||
name: 'index.CrTXFMOJ.js',
|
||||
path: 'user/tiptap/0.0.1/index.CrTXFMOJ.js',
|
||||
},
|
||||
{
|
||||
name: 'index.html',
|
||||
path: 'user/tiptap/0.0.1/index.html',
|
||||
},
|
||||
],
|
||||
},
|
||||
version: '0.0.1',
|
||||
domain: '',
|
||||
appType: '',
|
||||
key: 'tiptap',
|
||||
type: '',
|
||||
uid: '2bebe6a0-3c64-4a64-89f9-cc47fd082a07',
|
||||
pid: null,
|
||||
proxy: false,
|
||||
user: 'user',
|
||||
status: 'running',
|
||||
createdAt: '2024-12-14T15:39:30.684Z',
|
||||
updatedAt: '2024-12-14T15:39:55.714Z',
|
||||
deletedAt: null,
|
||||
};
|
||||
|
||||
type DownloadTask = {
|
||||
downloadPath: string;
|
||||
downloadUrl: string;
|
||||
user: string;
|
||||
key: string;
|
||||
version: string;
|
||||
};
|
||||
export const installApp = async (app: any) => {
|
||||
// const _app = demoData;
|
||||
const _app = app;
|
||||
try {
|
||||
let files = _app.data.files || [];
|
||||
const version = _app.version;
|
||||
const user = _app.user;
|
||||
const key = _app.key;
|
||||
|
||||
const downFiles = files.map((file: any) => {
|
||||
const noVersionPath = file.path.replace(`/${version}`, '');
|
||||
return {
|
||||
...file,
|
||||
downloadPath: path.join(appDir, noVersionPath),
|
||||
downloadUrl: `${kevisualUrl}/${noVersionPath}`,
|
||||
};
|
||||
});
|
||||
const downloadTasks: DownloadTask[] = downFiles as any;
|
||||
for (const file of downloadTasks) {
|
||||
const downloadPath = file.downloadPath;
|
||||
const downloadUrl = file.downloadUrl;
|
||||
const dir = path.dirname(downloadPath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
const res = await fetch(downloadUrl);
|
||||
const blob = await res.blob();
|
||||
fs.writeFileSync(downloadPath, Buffer.from(await blob.arrayBuffer()));
|
||||
}
|
||||
let indexHtml = files.find((file: any) => file.name === 'index.html');
|
||||
if (!indexHtml) {
|
||||
files.push({
|
||||
name: 'index.html',
|
||||
path: `${user}/${key}/index.html`,
|
||||
});
|
||||
fs.writeFileSync(path.join(appDir, `${user}/${key}/index.html`), JSON.stringify(app, null, 2));
|
||||
}
|
||||
_app.data.files = files;
|
||||
addAppConfig(_app);
|
||||
return {
|
||||
code: 200,
|
||||
data: _app,
|
||||
message: 'Install app success',
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return {
|
||||
code: 500,
|
||||
message: 'Install app failed',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const uninstallApp = async (app: any) => {
|
||||
try {
|
||||
const { user, key } = app;
|
||||
const appConfig = getAppConfig();
|
||||
const index = appConfig.list.findIndex((item: any) => item.user === user && item.key === key);
|
||||
if (index !== -1) {
|
||||
appConfig.list.splice(index, 1);
|
||||
setAppConfig(appConfig);
|
||||
// 删除appDir和文件
|
||||
fs.rmSync(path.join(appDir, user, key), { recursive: true });
|
||||
// 删除proxy
|
||||
const proxyConfig = getCacheAssistantConfig();
|
||||
const proxyIndex = proxyConfig.proxy.findIndex((item: any) => item.user === user && item.key === key);
|
||||
if (proxyIndex !== -1) {
|
||||
proxyConfig.proxy.splice(proxyIndex, 1);
|
||||
setConfig(proxyConfig);
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: 200,
|
||||
message: 'Uninstall app success',
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return {
|
||||
code: 500,
|
||||
message: 'Uninstall app failed',
|
||||
};
|
||||
}
|
||||
};
|
||||
6
src/main/renderer.ts
Normal file
6
src/main/renderer.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
// This file is required by the index.html file and will
|
||||
// be executed in the renderer process for that window.
|
||||
// No Node.js APIs are available in this process unless
|
||||
// nodeIntegration is set to true in webPreferences.
|
||||
// Use preload.js to selectively enable features
|
||||
// needed in the renderer process.
|
||||
57
src/main/session/index.ts
Normal file
57
src/main/session/index.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
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';
|
||||
|
||||
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}]`);
|
||||
});
|
||||
return _session;
|
||||
};
|
||||
19
src/main/window/page/app-packages.ts
Normal file
19
src/main/window/page/app-packages.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { BrowserWindow } from 'electron';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
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 构建后的文件
|
||||
return mainWindow;
|
||||
};
|
||||
19
src/main/window/page/enter.ts
Normal file
19
src/main/window/page/enter.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { BrowserWindow } from 'electron';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
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 构建后的文件
|
||||
return mainWindow;
|
||||
};
|
||||
18
src/main/window/page/index.ts
Normal file
18
src/main/window/page/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { getCacheAssistantConfig, LocalElectronAppUrl } from '@/modules/config';
|
||||
import { createEnterPage } from './enter';
|
||||
import { createAppPackagesPage } from './app-packages';
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
export const checkShowPage = async (window?: BrowserWindow) => {
|
||||
const assistantConfig = getCacheAssistantConfig();
|
||||
const { pageApi, proxy } = assistantConfig;
|
||||
if (!pageApi) {
|
||||
createEnterPage(window);
|
||||
return;
|
||||
}
|
||||
if (!proxy || proxy.length === 0) {
|
||||
createAppPackagesPage(window);
|
||||
return;
|
||||
}
|
||||
return window?.loadURL(LocalElectronAppUrl);
|
||||
};
|
||||
19
src/main/window/page/introduce.ts
Normal file
19
src/main/window/page/introduce.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { BrowserWindow } from 'electron';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
export const createIntroducePage = (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/index.html')); //
|
||||
return mainWindow;
|
||||
};
|
||||
6
src/main/window/relunch.ts
Normal file
6
src/main/window/relunch.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { app } from 'electron';
|
||||
|
||||
export const relunch = () => {
|
||||
app.relaunch({});
|
||||
app.quit();
|
||||
};
|
||||
11
src/main/window/window-manager.ts
Normal file
11
src/main/window/window-manager.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { BrowserWindow } from 'electron';
|
||||
|
||||
export class WindowsManager {
|
||||
static window: BrowserWindow;
|
||||
static setWindow = (window: BrowserWindow) => {
|
||||
WindowsManager.window = window;
|
||||
};
|
||||
static getWindow = () => {
|
||||
return WindowsManager.window;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user