This commit is contained in:
贾同学
2025-10-16 20:01:57 +08:00
commit 4768ef2d26
79 changed files with 3358 additions and 0 deletions

144
electron/service/cross.ts Normal file
View File

@@ -0,0 +1,144 @@
import { logger } from 'ee-core/log';
import { getExtraResourcesDir, getLogDir } from 'ee-core/ps';
import path from 'path';
import axios from 'axios';
import { is } from 'ee-core/utils';
import { cross } from 'ee-core/cross';
/**
* cross
* @class
*/
class CrossService {
info(): string {
const pids = cross.getPids();
logger.info('cross pids:', pids);
let num = 1;
pids.forEach(pid => {
let entity = cross.getProc(pid);
logger.info(`server-${num} name:${entity.name}`);
logger.info(`server-${num} config:`, entity.config);
num++;
})
return 'hello electron-egg';
}
getUrl(name: string): string {
const serverUrl = cross.getUrl(name);
return serverUrl;
}
killServer(type: string, name: string): void {
if (type == 'all') {
cross.killAll();
} else {
cross.killByName(name);
}
}
/**
* create go service
* In the default configuration, services can be started with applications.
* Developers can turn off the configuration and create it manually.
*/
async createGoServer(): Promise<void> {
// method 1: Use the default Settings
//const entity = await cross.run(serviceName);
// method 2: Use custom configuration
const serviceName = "go";
const opt = {
name: 'goapp',
cmd: path.join(getExtraResourcesDir(), 'goapp'),
directory: getExtraResourcesDir(),
args: ['--port=7073'],
appExit: true,
}
const entity = await cross.run(serviceName, opt);
logger.info('server name:', entity.name);
logger.info('server config:', entity.config);
logger.info('server url:', entity.getUrl());
}
/**
* create java server
*/
async createJavaServer(): Promise<void> {
const serviceName = "java";
const jarPath = path.join(getExtraResourcesDir(), 'java-app.jar');
const opt = {
name: 'javaapp',
cmd: path.join(getExtraResourcesDir(), 'jre1.8.0_201/bin/javaw.exe'),
directory: getExtraResourcesDir(),
args: ['-jar', '-server', '-Xms512M', '-Xmx512M', '-Xss512k', '-Dspring.profiles.active=prod', `-Dserver.port=18080`, `-Dlogging.file.path=${getLogDir()}`, `${jarPath}`],
appExit: false,
}
if (is.macOS()) {
// Setup Java program
opt.cmd = path.join(getExtraResourcesDir(), 'jre1.8.0_201.jre/Contents/Home/bin/java');
}
if (is.linux()) {
// Setup Java program
}
const entity = await cross.run(serviceName, opt);
logger.info('server name:', entity.name);
logger.info('server config:', entity.config);
logger.info('server url:', cross.getUrl(entity.name));
}
/**
* create python service
* In the default configuration, services can be started with applications.
* Developers can turn off the configuration and create it manually.
*/
async createPythonServer(): Promise<void> {
// method 1: Use the default Settings
//const entity = await cross.run(serviceName);
// method 2: Use custom configuration
const serviceName = "python";
const opt = {
name: 'pyapp',
cmd: path.join(getExtraResourcesDir(), 'py', 'pyapp'),
directory: path.join(getExtraResourcesDir(), 'py'),
args: ['--port=7074'],
windowsExtname: true,
appExit: true,
}
const entity = await cross.run(serviceName, opt);
logger.info('server name:', entity.name);
logger.info('server config:', entity.config);
logger.info('server url:', entity.getUrl());
}
async requestApi(name: string, urlPath: string, params: any): Promise<any> {
const serverUrl = cross.getUrl(name);
const apiHello = serverUrl + urlPath;
console.log('Server Url:', serverUrl);
const response = await axios({
method: 'get',
url: apiHello,
timeout: 1000,
params,
proxy: false,
});
if (response.status == 200) {
const { data } = response;
return data;
}
return null;
}
}
CrossService.toString = () => '[class CrossService]';
const crossService = new CrossService();
export {
CrossService,
crossService
};

View File

@@ -0,0 +1,23 @@
import { logger } from 'ee-core/log';
// effect service
class EffectService {
// hello
async hello(args: any): Promise<{ status: string; params: any }> {
let obj = {
status:'ok',
params: args
}
logger.info('EffectService obj:', obj);
return obj;
}
}
EffectService.toString = () => '[class EffectService]';
const effectService = new EffectService();
export {
EffectService,
effectService
}

View File

@@ -0,0 +1,21 @@
import { logger } from 'ee-core/log';
// example service
class ExampleService {
async test(args: any): Promise<{ status: string; params: any }> {
let obj = {
status:'ok',
params: args
}
logger.info('ExampleService obj:', obj);
return obj;
}
}
ExampleService.toString = () => '[class ExampleService]';
const exampleService = new ExampleService();
export {
ExampleService,
exampleService
};

View File

@@ -0,0 +1,19 @@
import { logger } from 'ee-core/log';
/**
* UserService class
*/
class UserService {
async hello(args: any): Promise<{ status: string; params: any }> {
const obj = {
status: 'ok',
params: args,
};
logger.info('UserService obj:', obj);
return obj;
}
}
UserService.toString = () => '[class UserService]';
export { UserService };

View File

@@ -0,0 +1,177 @@
import { app as electronApp } from 'electron';
import { autoUpdater } from 'electron-updater';
import { is } from 'ee-core/utils';
import { logger } from 'ee-core/log';
import { getMainWindow, setCloseAndQuit } from 'ee-core/electron';
/**
* AutoUpdaterService class for automatic updates
*/
class AutoUpdaterService {
private config: {
windows: boolean;
macOS: boolean;
linux: boolean;
options: any;
};
constructor() {
this.config = {
windows: false,
macOS: false,
linux: false,
options: {
provider: 'generic',
url: 'http://kodo.qiniu.com/'
},
}
}
/**
* Create and configure the auto updater
*/
create(): void {
logger.info('[autoUpdater] load');
const cfg = this.config;
if ((is.windows() && cfg.windows) ||
(is.macOS() && cfg.macOS) ||
(is.linux() && cfg.linux)) {
// continue
} else {
return;
}
const status = {
error: -1,
available: 1,
noAvailable: 2,
downloading: 3,
downloaded: 4,
};
const version = electronApp.getVersion();
logger.info('[autoUpdater] current version: ', version);
// Set the download server address
let server = cfg.options.url;
const lastChar = server.substring(server.length - 1);
server = lastChar === '/' ? server : server + "/";
cfg.options.url = server;
try {
autoUpdater.setFeedURL(cfg.options);
} catch (error) {
logger.error('[autoUpdater] setFeedURL error : ', error);
}
autoUpdater.on('checking-for-update', () => {
// sendStatusToWindow('正在检查更新...');
});
autoUpdater.on('update-available', () => {
const data = {
status: status.available,
desc: '有可用更新',
};
this.sendStatusToWindow(data);
});
autoUpdater.on('update-not-available', () => {
const data = {
status: status.noAvailable,
desc: '没有可用更新',
};
this.sendStatusToWindow(data);
});
autoUpdater.on('error', (err) => {
const data = {
status: status.error,
desc: err,
};
this.sendStatusToWindow(data);
});
autoUpdater.on('download-progress', (progressObj) => {
const percentNumber = progressObj.percent;
const totalSize = this.bytesChange(progressObj.total);
const transferredSize = this.bytesChange(progressObj.transferred);
let text = '已下载 ' + percentNumber + '%';
text = text + ' (' + transferredSize + "/" + totalSize + ')';
const data = {
status: status.downloading,
desc: text,
percentNumber,
totalSize,
transferredSize,
};
logger.info('[addon:autoUpdater] progress: ', text);
this.sendStatusToWindow(data);
});
autoUpdater.on('update-downloaded', () => {
const data = {
status: status.downloaded,
desc: '下载完成',
};
this.sendStatusToWindow(data);
// Allow the window to close
setCloseAndQuit(true);
// Install updates and exit the application
autoUpdater.quitAndInstall();
});
}
/**
* Check for updates
*/
checkUpdate(): void {
autoUpdater.checkForUpdates();
}
/**
* Download updates
*/
download(): void {
autoUpdater.downloadUpdate();
}
/**
* Send status to the frontend
*/
sendStatusToWindow(content: any = {}): void {
const textJson = JSON.stringify(content);
const channel = 'custom/app/updater';
const win = getMainWindow();
win.webContents.send(channel, textJson);
}
/**
* Convert bytes to a more readable format
*/
bytesChange(limit: number): string {
let size = "";
if (limit < 0.1 * 1024) {
size = limit.toFixed(2) + "B";
} else if (limit < 0.1 * 1024 * 1024) {
size = (limit / 1024).toFixed(2) + "KB";
} else if (limit < 0.1 * 1024 * 1024 * 1024) {
size = (limit / (1024 * 1024)).toFixed(2) + "MB";
} else {
size = (limit / (1024 * 1024 * 1024)).toFixed(2) + "GB";
}
let sizeStr = size + "";
let index = sizeStr.indexOf(".");
let dou = sizeStr.substring(index + 1, index + 3);
if (dou === "00") {
return sizeStr.substring(0, index) + sizeStr.substring(index + 3, index + 5);
}
return size;
}
}
AutoUpdaterService.toString = () => '[class AutoUpdaterService]';
const autoUpdaterService = new AutoUpdaterService();
export {
AutoUpdaterService,
autoUpdaterService
};

View File

@@ -0,0 +1,15 @@
const {getMainWindow} = require("ee-core/electron");
class IconService {
update(iconPath) {
const win = getMainWindow();
win.setIcon(iconPath);
}
}
module.exports = {
iconService: new IconService()
};

View File

@@ -0,0 +1,31 @@
import { logger } from 'ee-core/log';
import { app as electronApp } from 'electron';
/**
* SecurityService class for handling security-related operations
*/
class SecurityService {
/**
* Create and configure the security service
*/
create(): void {
logger.info('[security] load');
const runWithDebug = process.argv.find((e) => {
const isHasDebug = e.includes('--inspect') || e.includes('--inspect-brk') || e.includes('--remote-debugging-port');
return isHasDebug;
});
// Do not allow remote debugging
if (runWithDebug) {
logger.error('[error] Remote debugging is not allowed, runWithDebug:', runWithDebug);
electronApp.quit();
}
}
}
SecurityService.toString = () => '[class SecurityService]';
const securityService = new SecurityService();
export {
SecurityService,
securityService
};

View File

@@ -0,0 +1,81 @@
import { Tray, Menu } from 'electron';
import path from 'path';
import { isDev, getBaseDir } from 'ee-core/ps';
import { logger } from 'ee-core/log';
import { app as electronApp } from 'electron';
import { getMainWindow, getCloseAndQuit, setCloseAndQuit } from 'ee-core/electron';
/**
* 托盘
* @class
*/
class TrayService {
tray: Tray | null;
config: {
title: string;
icon: string;
}
constructor() {
this.tray = null;
this.config = {
title: 'electron-egg',
icon: '/public/images/tray.png',
}
}
/**
* Create the tray icon
*/
create () {
logger.info('[tray] load');
const cfg = this.config;
const mainWindow = getMainWindow();
// tray icon
const iconPath = path.join(getBaseDir(), cfg.icon);
// Tray menu items
const trayMenuTemplate = [
{
label: '显示',
click: function () {
mainWindow.show();
}
},
{
label: '退出',
click: function () {
electronApp.quit();
}
}
]
// Set a flag to minimize to tray instead of closing
setCloseAndQuit(false);
mainWindow.on('close', (event: any) => {
if (getCloseAndQuit()) {
return;
}
mainWindow.hide();
event.preventDefault();
});
// Initialize the tray
this.tray = new Tray(iconPath);
this.tray.setToolTip(cfg.title);
const contextMenu = Menu.buildFromTemplate(trayMenuTemplate);
this.tray.setContextMenu(contextMenu);
// Show the main window when the tray icon is clicked
this.tray.on('click', () => {
mainWindow.show()
})
}
}
TrayService.toString = () => '[class TrayService]';
const trayService = new TrayService();
export {
trayService
}

View File

@@ -0,0 +1,131 @@
import path from 'path';
import { BrowserWindow, Notification } from 'electron';
import { getMainWindow } from 'ee-core/electron';
import { isProd, getBaseDir } from 'ee-core/ps';
import { getConfig } from 'ee-core/config';
import { isFileProtocol } from 'ee-core/utils';
import { logger } from 'ee-core/log';
/**
* Window
* @class
*/
class WindowService {
myNotification: Notification | null;
windows: { [key: string]: BrowserWindow };
constructor() {
this.myNotification = null;
this.windows = {}
}
/**
* Create a new window
*/
createWindow(args: { type: string; content: string; windowName: string; windowTitle: string }): number {
const { type, content, windowName, windowTitle } = args;
let contentUrl: string = '';
if (type == 'html') {
contentUrl = path.join('file://', getBaseDir(), content)
} else if (type == 'web') {
contentUrl = content;
} else if (type == 'vue') {
let addr = 'http://localhost:8080'
if (isProd()) {
const { mainServer } = getConfig();
if (mainServer && mainServer.protocol && isFileProtocol(mainServer.protocol)) {
addr = mainServer.protocol + path.join(getBaseDir(), mainServer.indexPath);
}
}
contentUrl = addr + content;
}
logger.info('[createWindow] url: ', contentUrl);
const opt = {
title: windowTitle,
x: 10,
y: 10,
width: 980,
height: 650,
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
},
}
const win = new BrowserWindow(opt);
const winContentsId = win.webContents.id;
win.loadURL(contentUrl);
win.webContents.openDevTools();
this.windows[windowName] = win;
return winContentsId;
}
/**
* Get window contents id
*/
getWCid(args: { windowName: string }): number {
const { windowName } = args;
let win: BrowserWindow;
if (windowName == 'main') {
win = getMainWindow();
} else {
win = this.windows[windowName];
}
return win.webContents.id;
}
/**
* Realize communication between two windows through the transfer of the main process
*/
communicate(args: { receiver: string; content: any }): void {
const { receiver, content } = args;
if (receiver == 'main') {
const win = getMainWindow();
win.webContents.send('controller/os/window2ToWindow1', content);
} else if (receiver == 'window2') {
const win = this.windows[receiver];
win.webContents.send('controller/os/window1ToWindow2', content);
}
}
/**
* createNotification
*/
createNotification(options: any, event: any): void {
const channel = 'controller/os/sendNotification';
this.myNotification = new Notification(options);
if (options.clickEvent) {
this.myNotification.on('click', () => {
let data = {
type: 'click',
msg: '您点击了通知消息'
}
event.reply(`${channel}`, data)
});
}
if (options.closeEvent) {
this.myNotification.on('close', () => {
let data = {
type: 'close',
msg: '您关闭了通知消息'
}
event.reply(`${channel}`, data)
});
}
this.myNotification.show();
}
}
WindowService.toString = () => '[class WindowService]';
const windowService = new WindowService();
export {
WindowService,
windowService
}