demo: js code

This commit is contained in:
gaoshuaixing
2025-01-09 19:06:07 +08:00
parent 1fa022d8d5
commit 7a1ac30fa6
51 changed files with 2 additions and 1834 deletions

View File

@@ -32,7 +32,8 @@ module.exports = {
args: ['run', 'build'],
},
electron: {
type: 'typescript',
type: 'javascript',
bundleType: 'copy'
},
win64: {
cmd: 'electron-builder',

View File

@@ -1,64 +0,0 @@
import path from 'path';
import { getBaseDir } from 'ee-core/ps';
import { type AppConfig } from 'ee-core/config';
const config: () => AppConfig = () => {
return {
openDevTools: false,
singleLock: true,
windowsOption: {
title: 'electron-egg',
width: 980,
height: 650,
minWidth: 400,
minHeight: 300,
webPreferences: {
contextIsolation: false,
nodeIntegration: true,
},
frame: true,
show: true,
icon: path.join(getBaseDir(), 'public', 'images', 'logo-32.png'),
},
logger: {
level: 'INFO',
outputJSON: false,
appLogName: 'ee.log',
coreLogName: 'ee-core.log',
errorLogName: 'ee-error.log',
},
remote: {
enable: false,
url: 'http://electron-egg.kaka996.com/',
},
socketServer: {
enable: false,
port: 7070,
path: "/socket.io/",
connectTimeout: 45000,
pingTimeout: 30000,
pingInterval: 25000,
maxHttpBufferSize: 1e8,
transports: ["polling", "websocket"],
cors: {
origin: true,
},
channel: 'c1',
},
httpServer: {
enable: false,
https: {
enable: false,
key: '/public/ssl/localhost+1.key',
cert: '/public/ssl/localhost+1.pem',
},
host: '127.0.0.1',
port: 7071,
},
mainServer: {
indexPath: '/public/dist/index.html',
}
};
};
export default config;

View File

@@ -1,14 +0,0 @@
import { type AppConfig } from 'ee-core/config';
const config: () => AppConfig = () => {
return {
openDevTools: {
mode: 'bottom'
},
jobs: {
messageLog: true
}
};
};
export default config;

View File

@@ -1,9 +0,0 @@
import { type AppConfig } from 'ee-core/config';
const config: () => AppConfig = () => {
return {
openDevTools: false,
};
};
export default config;

View File

@@ -1,63 +0,0 @@
import { crossService } from '../service/cross';
/**
* Cross
* @class
*/
class CrossController {
/**
* View process service information
*/
info() {
crossService.info();
return 'hello electron-egg';
}
/**
* Get service url
*/
async getUrl(args: { name: string }): Promise<string> {
const { name } = args;
const serverUrl = crossService.getUrl(name);
return serverUrl;
}
/**
* kill service
* By default (modifiable), killing the process will exit the electron application.
*/
async killServer(args: { type: string; name: string }): Promise<void> {
const { type, name } = args;
crossService.killServer(type, name);
return;
}
/**
* create service
*/
async createServer(args: { program: string }): Promise<void> {
const { program } = args;
if (program == 'go') {
crossService.createGoServer();
} else if (program == 'java') {
crossService.createJavaServer();
} else if (program == 'python') {
crossService.createPythonServer();
}
return;
}
/**
* Access the api for the cross service
*/
async requestApi(args: { name: string; urlPath: string; params: any }): Promise<any> {
const { name, urlPath, params} = args;
const data = await crossService.requestApi(name, urlPath, params);
return data;
}
}
CrossController.toString = () => '[class CrossController]';
export default CrossController;

View File

@@ -1,63 +0,0 @@
import { dialog } from 'electron';
import { getMainWindow } from 'ee-core/electron';
/**
* effect - demo
* @class
*/
class EffectController {
/**
* select file
*/
selectFile(): string | null {
const filePaths = dialog.showOpenDialogSync({
properties: ['openFile']
});
if (!filePaths) {
return null
}
return filePaths[0];
}
/**
* login window
*/
loginWindow(args: { width?: number; height?: number }): void {
const { width, height } = args;
const win = getMainWindow();
const size = {
width: width || 400,
height: height || 300
}
win.setSize(size.width, size.height);
win.setResizable(true);
win.center();
win.show();
win.focus();
}
/**
* restore window
*/
restoreWindow(args: { width?: number; height?: number }): void {
const { width, height } = args;
const win = getMainWindow();
const size = {
width: width || 980,
height: height || 650
}
win.setSize(size.width, size.height);
win.setResizable(true);
win.center();
win.show();
win.focus();
}
}
EffectController.toString = () => '[class EffectController]';
export default EffectController;

View File

@@ -1,16 +0,0 @@
/**
* example
* @class
*/
class ExampleController {
/**
* test
*/
async test(): Promise<string> {
return 'hello electron-egg';
}
}
ExampleController.toString = () => '[class ExampleController]';
export default ExampleController;

View File

@@ -1,285 +0,0 @@
import dayjs from 'dayjs';
import path from 'path';
import fs from 'fs';
import { exec } from 'child_process';
import { app as electronApp, shell } from 'electron';
import { getExtraResourcesDir } from 'ee-core/ps';
import { logger } from 'ee-core/log';
import { getConfig } from 'ee-core/config';
import { frameworkService } from '../service/framework';
import { sqlitedbService } from '../service/database/sqlitedb';
import { autoUpdaterService } from '../service/os/auto_updater';
/**
* framework - demo
* @class
*/
class FrameworkController {
/**
* 所有方法接收两个参数
* @param args 前端传的参数
* @param event - ipc通信时才有值。详情见控制器文档
*/
/**
* sqlite数据库操作
*/
async sqlitedbOperation(args: {
action: string;
info?: any;
delete_name?: string;
update_name?: string;
update_age?: number;
search_age?: number;
data_dir?: string;
}): Promise<{
action: string;
result: any;
all_list: any;
code: number;
}> {
const { action, info, delete_name, update_name, update_age, search_age, data_dir } = args;
const data = {
action,
result: null as any,
all_list: []as any,
code: 0
};
try {
// test
sqlitedbService.getDataDir();
} catch (err) {
console.log(err);
data.code = -1;
return data;
}
switch (action) {
case 'add' :
data.result = await sqlitedbService.addTestDataSqlite(info);;
break;
case 'del' :
data.result = await sqlitedbService.delTestDataSqlite(delete_name);;
break;
case 'update' :
data.result = await sqlitedbService.updateTestDataSqlite(update_name, update_age);
break;
case 'get' :
data.result = await sqlitedbService.getTestDataSqlite(search_age);
break;
case 'getDataDir' :
data.result = await sqlitedbService.getDataDir();
break;
case 'setDataDir' :
data.result = await sqlitedbService.setCustomDataDir(data_dir || "");
break;
}
data.all_list = await sqlitedbService.getAllTestDataSqlite();
return data;
}
/**
* 调用其它程序exe、bash等可执行程序
*
*/
openSoftware(args: { softName: string }): boolean {
const { softName } = args;
const softwarePath = path.join(getExtraResourcesDir(), softName);
logger.info('[openSoftware] softwarePath:', softwarePath);
// 检查程序是否存在
if (!fs.existsSync(softwarePath)) {
return false;
}
// 命令行字符串 并 执行, start 命令后面的路径要加双引号
const cmdStr = `start "${softwarePath}"`;
exec(cmdStr);
// 方法二
// 使用cross模块
return true;
}
/**
* 检测http服务是否开启
*/
async checkHttpServer(): Promise<{ enable: boolean; server: string }> {
const httpConfig = getConfig().httpServer;
if (!httpConfig || !('enable' in httpConfig) || !('protocol' in httpConfig) || !('host' in httpConfig) || !('port' in httpConfig)) {
return { enable: false, server: '' };
}
const { enable, protocol, host, port } = httpConfig;
const url = `${protocol}://${host}:${port}`;
console.log('[checkHttpServer] url:', url);
const data = {
enable: enable,
server: url
}
return data;
}
/**
* 一个 http 请求
* args 是 前端传的参数
* ctx 是 koa 的 ctx 对象
*/
async doHttpRequest(args: any, ctx: any): Promise<boolean> {
const httpInfo = {
args,
method: ctx.request.method,
query: ctx.request.query,
body: ctx.request.body
}
logger.info('httpInfo:', httpInfo);
const { id } = args;
if (!id) {
return false;
}
const dir = electronApp.getPath(id);
shell.openPath(dir);
return true;
}
/**
* 一个socket io请求访问此方法
*/
async doSocketRequest(args: any): Promise<boolean> {
const { id } = args;
if (!id) {
return false;
}
const dir = electronApp.getPath(id);
shell.openPath(dir);
return true;
}
/**
* 异步消息类型
*/
async ipcInvokeMsg(args: string): Promise<string> {
let timeNow = dayjs().format('YYYY-MM-DD HH:mm:ss');
const data = args + ' - ' + timeNow;
return data;
}
/**
* 同步消息类型
*/
async ipcSendSyncMsg(args: string): Promise<string> {
let timeNow = dayjs().format('YYYY-MM-DD HH:mm:ss');
const data = args + ' - ' + timeNow;
return data;
}
/**
* 双向异步通信
*/
ipcSendMsg(args: { type: string; content: any }, event: any): any {
const { type, content } = args;
const data = frameworkService.bothWayMessage(type, content, event);
return data;
}
/**
* 任务
*/
someJob(args: { jobId: string; action: string }, event: any): any {
const { jobId, action} = args;
let result = null as any;
switch (action) {
case 'create':
result = frameworkService.doJob(jobId, action, event);
break;
case 'close':
frameworkService.doJob(jobId, action, event);
break;
case 'pause':
frameworkService.doJob(jobId, action, event);
break;
case 'resume':
frameworkService.doJob(jobId, action, event);
break;
default:
}
let data = {
jobId,
action,
result
}
return data;
}
/**
* 创建任务池
*/
async createPool(args: { number: number }, event: any): Promise<void> {
let num = args.number;
frameworkService.doCreatePool(num, event);
// test monitor
frameworkService.monitorJob();
return;
}
/**
* 通过进程池执行任务
*/
someJobByPool(args: { jobId: string; action: string }, event: any): any {
const { jobId, action } = args;
let result = null as any;
switch (action) {
case 'run':
result = frameworkService.doJobByPool(jobId, action, event);
break;
default:
}
let data = {
jobId,
action,
result
}
return data;
}
/**
* 检查是否有新版本
*/
checkForUpdater() {
autoUpdaterService.checkUpdate();
return;
}
/**
* 下载新版本
*/
downloadApp() {
autoUpdaterService.download();
return;
}
/**
* 测试接口
*/
hello(args: any): void {
logger.info('hello ', args);
}
}
FrameworkController.toString = () => '[class FrameworkController]';
export default FrameworkController;

View File

@@ -1,168 +0,0 @@
import fs from 'fs';
import path from 'path';
import { app as electronApp, dialog, shell } from 'electron';
import { windowService } from '../service/os/window';
import exp from 'constants';
/**
* example
* @class
*/
class OsController {
/**
* All methods receive two parameters
* @param args Parameters transmitted by the frontend
* @param event - Event are only available during IPC communication. For details, please refer to the controller documentation
*/
/**
* Message prompt dialog box
*/
messageShow(): string {
dialog.showMessageBoxSync({
type: 'info', // "none", "info", "error", "question" 或者 "warning"
title: 'Custom Title',
message: 'Customize message content',
detail: 'Other additional information'
})
return 'Opened the message box';
}
/**
* Message prompt and confirmation dialog box
*/
messageShowConfirm(): string {
const res = dialog.showMessageBoxSync({
type: 'info',
title: 'Custom Title',
message: 'Customize message content',
detail: 'Other additional information',
cancelId: 1, // Index of buttons used to cancel dialog boxes
defaultId: 0, // Set default selected button
buttons: ['confirm', 'cancel'],
})
let data = (res === 0) ? 'click the confirm button' : 'click the cancel button';
return data;
}
/**
* Select Directory
*/
selectFolder() {
const filePaths = dialog.showOpenDialogSync({
properties: ['openDirectory', 'createDirectory']
});
if (!filePaths) {
return ""
}
return filePaths[0];
}
/**
* open directory
*/
openDirectory(args: { id: any }): boolean {
const { id } = args;
if (!id) {
return false;
}
let dir = '';
if (path.isAbsolute(id)) {
dir = id;
} else {
dir = electronApp.getPath(id);
}
shell.openPath(dir);
return true;
}
/**
* Select Picture
*/
selectPic(): string | null {
const filePaths = dialog.showOpenDialogSync({
title: 'select pic',
properties: ['openFile'],
filters: [
{ name: 'Images', extensions: ['jpg', 'png', 'gif'] },
]
});
if (!filePaths) {
return null
}
try {
const data = fs.readFileSync(filePaths[0]);
const pic = 'data:image/jpeg;base64,' + data.toString('base64');
return pic;
} catch (err) {
console.error(err);
return null;
}
}
/**
* Open a new window
*/
createWindow(args: any): any {
const wcid = windowService.createWindow(args);
return wcid;
}
/**
* Get Window contents id
*/
getWCid(args: any): any {
const wcid = windowService.getWCid(args);
return wcid;
}
/**
* Realize communication between two windows through the transfer of the main process
*/
window1ToWindow2(args: any): void {
windowService.communicate(args);
return;
}
/**
* Realize communication between two windows through the transfer of the main process
*/
window2ToWindow1(args: any): void {
windowService.communicate(args);
return;
}
/**
* Create system notifications
*/
sendNotification(args: { title?: string; subtitle?: string; body?: string; silent?: boolean }, event: any): boolean {
const { title, subtitle, body, silent} = args;
const options: any = {};
if (title) {
options.title = title;
}
if (subtitle) {
options.subtitle = subtitle;
}
if (body) {
options.body = body;
}
if (silent !== undefined) {
options.silent = silent;
}
windowService.createNotification(options, event);
return true
}
}
OsController.toString = () => '[class OsController]';
export default OsController;

View File

@@ -1,10 +0,0 @@
import { logger } from 'ee-core/log';
/**
* Welcome function
*/
function welcome(): void {
logger.info('[child-process] [jobs/example/hello] welcome !');
}
export { welcome };

View File

@@ -1,91 +0,0 @@
import { logger } from 'ee-core/log';
import { isChildJob, exit } from 'ee-core/ps';
import { childMessage } from 'ee-core/message';
import { welcome } from './hello';
import { UserService } from '../../service/job/user';
/**
* example - TimerJob
* @class
*/
class TimerJob {
params: any;
timer: NodeJS.Timeout | undefined;
timeoutTimer: NodeJS.Timeout | undefined;
number: number;
countdown: number;
constructor(params: any) {
this.params = params;
this.timer = undefined;
this.timeoutTimer = undefined;
this.number = 0;
this.countdown = 10; // 倒计时
}
/**
* handle() method is necessary and will be automatically called
*/
async handle(): Promise<void> {
logger.info("[child-process] TimerJob params: ", this.params);
const { jobId } = this.params;
// Use service in child process
// 1. Ensure that the service does not have Electron's API or dependencies, as Electron does not support them
const userService = new UserService();
userService.hello('job');
// Execute the task
this.doTimer(jobId);
}
/**
* Pause the job
*/
async pause(jobId: string): Promise<void> {
logger.info("[child-process] Pause timerJob, jobId: ", jobId);
clearInterval(this.timer);
clearInterval(this.timeoutTimer);
}
/**
* Resume the job
*/
async resume(jobId: string, pid: number): Promise<void> {
logger.info("[child-process] Resume timerJob, jobId: ", jobId, ", pid: ", pid);
this.doTimer(jobId);
}
/**
* Run the task
*/
async doTimer(jobId) {
// Timer to simulate the task
const eventName = 'job-timer-progress-' + jobId;
this.timer = setInterval(() => {
welcome();
childMessage.send(eventName, {jobId, number: this.number, end: false});
this.number++;
this.countdown--;
}, 1000);
// Use setTimeout to simulate the task duration
this.timeoutTimer = setTimeout(() => {
// Stop the timer to simulate the task
clearInterval(this.timer);
// Task completed, reset the front-end display
childMessage.send(eventName, {jobId, number:0, pid:0, end: true});
// If it is a childJob task, call exit() to exit the process, otherwise it will stay in memory
// If it is a childPoolJob task, stay in memory and wait for the next business
if (isChildJob()) {
exit();
}
}, this.countdown * 1000)
}
}
TimerJob.toString = () => '[class TimerJob]';
export default TimerJob;

View File

@@ -1,19 +0,0 @@
import { ElectronEgg } from 'ee-core';
import { Lifecycle } from './preload/lifecycle';
import { preload } from './preload';
// New app
const app = new ElectronEgg();
// Register lifecycle
const life = new Lifecycle();
app.register("ready", life.ready);
app.register("electron-app-ready", life.electronAppReady);
app.register("window-ready", life.windowReady);
app.register("before-close", life.beforeClose);
// Register preload
app.register("preload", preload);
// Run
app.run();

View File

@@ -1,16 +0,0 @@
/*
* 如果启用了上下文隔离渲染进程无法使用electron的api
* 可通过contextBridge 导出api给渲染进程使用
*/
import { type IpcRenderer, contextBridge, ipcRenderer } from 'electron';
// 确保contextBridge.exposeInMainWorld的参数类型正确这里进行简单的类型定义示例
type ElectronApi = {
ipcRenderer: IpcRenderer;
};
const ele: ElectronApi = {
ipcRenderer,
};
contextBridge.exposeInMainWorld('electron', ele);

View File

@@ -1,12 +0,0 @@
/**
* Preload module, this file will be loaded when the program starts.
*/
function preload(): void {
// Example feature module, optional to use and modify
console.log('preload/index.js');
}
/**
* Entry point of the preload module
*/
export { preload };

View File

@@ -1,47 +0,0 @@
import { type AppConfig, getConfig } from 'ee-core/config';
import { getMainWindow } from 'ee-core/electron';
class Lifecycle {
/**
* Core app has been loaded
*/
async ready(): Promise<void> {
console.log('[lifecycle] ready');
}
/**
* Electron app is ready
*/
async electronAppReady(): Promise<void> {
console.log('[lifecycle] electron-app-ready');
}
/**
* Main window has been loaded
*/
async windowReady(): Promise<void> {
console.log('[lifecycle] window-ready');
// Delay loading, no white screen
const config:AppConfig = getConfig();
const { windowsOption } = config;
if (windowsOption?.show === false) {
const win = getMainWindow();
win.once('ready-to-show', () => {
win.show();
win.focus();
});
}
}
/**
* Before app close
*/
async beforeClose(): Promise<void> {
console.log('[lifecycle] before-close');
}
}
// 设置类的toString方法虽然在TypeScript中不常见
Lifecycle.toString = () => '[class Lifecycle]';
export { Lifecycle };

View File

@@ -1,144 +0,0 @@
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

@@ -1,52 +0,0 @@
import { type Database, SqliteStorage } from 'ee-core/storage';
import { getDataDir } from 'ee-core/ps';
import path from 'path';
/**
* sqlite数据存储
* @class
*/
class BasedbService {
dbname: string;
db!: Database;
storage!: SqliteStorage;
constructor(options: { dbname: string }) {
const { dbname } = options;
this.dbname = dbname;
this.init();
}
/*
* 初始化
*/
private init(): void {
// 定义数据文件
const dbFile = path.join(getDataDir(), "db", this.dbname);
const sqliteOptions = {
timeout: 6000,
verbose: console.log
}
this.storage = new SqliteStorage(dbFile, sqliteOptions);
this.db = this.storage.db;
}
/*
* change data dir (sqlite)
*/
changeDataDir(dir: string): void {
// the absolute path of the db file
const dbFile = path.join(dir, this.dbname);
const sqliteOptions = {
timeout: 6000,
verbose: console.log
}
this.storage = new SqliteStorage(dbFile, sqliteOptions);
this.db = this.storage.db;
}
}
BasedbService.toString = () => '[class BasedbService]';
export {
BasedbService,
}

View File

@@ -1,110 +0,0 @@
import { BasedbService } from './basedb';
/**
* sqlite数据存储
* @class
*/
class SqlitedbService extends BasedbService {
userTableName: string;
constructor () {
const options = {
dbname: 'sqlite-demo.db',
}
super(options);
this.userTableName = 'user';
this.initTable();
}
/*
* 初始化表
*/
private initTable(): void {
// 检查表是否存在
const masterStmt = this.db.prepare('SELECT * FROM sqlite_master WHERE type=? AND name = ?');
let tableExists = masterStmt.get('table', this.userTableName);
if (!tableExists) {
// 创建表
const create_user_table_sql =
`CREATE TABLE ${this.userTableName}
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name CHAR(50) NOT NULL,
age INT
);`
this.db.exec(create_user_table_sql);
}
}
/*
* 增 Test data (sqlite)
*/
async addTestDataSqlite(data: { name: string; age: number }) {
const insert = this.db.prepare(`INSERT INTO ${this.userTableName} (name, age) VALUES (@name, @age)`);
insert.run(data);
return true;
}
/*
* 删 Test data (sqlite)
*/
async delTestDataSqlite(name: string = ''): Promise<boolean> {
const delUser = this.db.prepare(`DELETE FROM ${this.userTableName} WHERE name = ?`);
delUser.run(name);
return true;
}
/*
* 改 Test data (sqlite)
*/
async updateTestDataSqlite(name: string = '', age: number = 0): Promise<boolean> {
const updateUser = this.db.prepare(`UPDATE ${this.userTableName} SET age = ? WHERE name = ?`);
updateUser.run(age, name);
return true;
}
/*
* 查 Test data (sqlite)
*/
async getTestDataSqlite(age: number = 0): Promise<any[]> {
const selectUser = this.db.prepare(`SELECT * FROM ${this.userTableName} WHERE age = @age`);
const users = selectUser.all({age: age});
return users;
}
/*
* all Test data (sqlite)
*/
async getAllTestDataSqlite(): Promise<any[]> {
const selectAllUser = this.db.prepare(`SELECT * FROM ${this.userTableName} `);
const allUser = selectAllUser.all();
return allUser;
}
/*
* get data dir (sqlite)
*/
async getDataDir(): Promise<string> {
const dir = this.storage.getDbDir();
return dir;
}
/*
* set custom data dir (sqlite)
*/
async setCustomDataDir(dir: string): Promise<void> {
if (dir.length == 0) {
return;
}
this.changeDataDir(dir);
this.initTable();
return;
}
}
SqlitedbService.toString = () => '[class SqlitedbService]';
const sqlitedbService = new SqlitedbService();
export {
SqlitedbService,
sqlitedbService
};

View File

@@ -1,23 +0,0 @@
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

@@ -1,21 +0,0 @@
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

@@ -1,166 +0,0 @@
import { logger } from 'ee-core/log';
import { ChildJob, ChildPoolJob } from 'ee-core/jobs';
// framework service
class FrameworkService {
myTimer: number;
myJob: ChildJob;
myJobPool: ChildPoolJob;
taskForJob: { [key: string]: any };
constructor() {
// 在构造函数中初始化一些变量
this.myTimer = 0;
this.myJob = new ChildJob();
this.myJobPool = new ChildPoolJob();
this.taskForJob = {};
}
/**
* test
*/
async test(args: any): Promise<{ status: string; params: any }> {
let obj = {
status:'ok',
params: args
}
logger.info('FrameworkService obj:', obj);
return obj;
}
/**
* ipc通信(双向)
*/
bothWayMessage(type: string, content: any, event: any): string {
// 前端ipc频道 channel
const channel = 'controller.framework.ipcSendMsg';
if (type == 'start') {
// 每隔1秒向前端页面发送消息
// 用定时器模拟
this.myTimer = setInterval((e: any, c: string, msg: string) => {
let timeNow = Date.now();
let data = msg + ':' + timeNow;
e.reply(`${c}`, data)
}, 1000, event, channel, content)
return '开始了'
} else if (type == 'end') {
clearInterval(this.myTimer);
return '停止了'
} else {
return 'ohther'
}
}
/**
* 执行任务
*/
doJob(jobId: string, action: string, event: any): any {
const res = {
pid: 0,
};
let oneTask: any;
const channel = 'controller.framework.timerJobProgress';
if (action == 'create') {
// 执行任务及监听进度
let eventName = 'job-timer-progress-' + jobId;
const timerTask = this.myJob.exec('./jobs/example/timer', {jobId});
timerTask.emitter.on(eventName, (data: any) => {
logger.info('[main-process] timerTask, from TimerJob data:', data);
// 发送数据到渲染进程
event.sender.send(`${channel}`, data)
})
// 执行任务及监听进度 异步
// myjob.execPromise('./jobs/example/timer', {jobId}).then(task => {
// task.emitter.on(eventName, (data) => {
// Log.info('[main-process] timerTask, from TimerJob data:', data);
// // 发送数据到渲染进程
// event.sender.send(`${channel}`, data)
// })
// });
res.pid = timerTask.pid;
this.taskForJob[jobId] = timerTask;
}
if (action == 'close') {
oneTask = this.taskForJob[jobId];
oneTask.kill();
event.sender.send(`${channel}`, {jobId, number:0, pid:0});
}
if (action == 'pause') {
oneTask = this.taskForJob[jobId];
oneTask.callFunc('./jobs/example/timer', 'pause', jobId);
}
if (action == 'resume') {
oneTask = this.taskForJob[jobId];
oneTask.callFunc('./jobs/example/timer', 'resume', jobId, oneTask.pid);
}
return res;
}
/**
* 创建pool
*/
doCreatePool(num: number, event: any): void {
const channel = 'controller.framework.createPoolNotice';
this.myJobPool.create(num).then((pids: any[]) => {
event.reply(`${channel}`, pids);
});
}
/**
* 通过进程池执行任务
*/
doJobByPool(jobId: string, action: string, event: any): any {
let res = {
pid: 0,
};
const channel = 'controller.framework.timerJobProgress';
if (action == 'run') {
// 异步-执行任务及监听进度
this.myJobPool.runPromise('./jobs/example/timer', {jobId}).then((task: any) => {
// 监听器名称唯一,否则会出现重复监听。
// 任务完成时,需要移除监听器,防止内存泄漏
let eventName = 'job-timer-progress-' + jobId;
task.emitter.on(eventName, (data: any) => {
logger.info('[main-process] [ChildPoolJob] timerTask, from TimerJob data:', data);
// 发送数据到渲染进程
event.sender.send(`${channel}`, data)
// 如果收到任务完成的消息,移除监听器
if (data.end) {
task.emitter.removeAllListeners(eventName);
}
});
res.pid = task.pid;
});
}
return res;
}
/**
* 获取正在运行的 job 进程
*/
monitorJob(): void {
setInterval(() => {
let jobPids = this.myJob.getPids();
let jobPoolPids = this.myJobPool.getPids();
logger.info(`[main-process] [monitorJob] jobPids: ${jobPids}, jobPoolPids: ${jobPoolPids}`);
}, 5000)
}
}
FrameworkService.toString = () => '[class FrameworkService]';
const frameworkService = new FrameworkService();
export {
FrameworkService,
frameworkService
}

View File

@@ -1,19 +0,0 @@
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

@@ -1,177 +0,0 @@
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

@@ -1,31 +0,0 @@
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

@@ -1,83 +0,0 @@
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: 'EE程序',
icon: '/public/images/tray.png',
}
}
/**
* Create the tray icon
*/
create () {
// todo In development mode, code hot reloading can cause tray icon artifacts
if (isDev()) return;
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

@@ -1,130 +0,0 @@
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';
/**
* 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.protocol && isFileProtocol(mainServer.protocol)) {
addr = mainServer.protocol + path.join(getBaseDir(), mainServer.indexPath);
}
}
contentUrl = addr + content;
}
console.log('contentUrl: ', 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
}