diff --git a/.gitignore b/.gitignore index 9912b76..9cb9e4d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ logs/ run/ .idea/ package-lock.json +data/ diff --git a/app/controller/v1/example.js b/app/controller/v1/example.js index ec9f8d7..5864ce6 100644 --- a/app/controller/v1/example.js +++ b/app/controller/v1/example.js @@ -21,6 +21,20 @@ class ExampleController extends BaseController { this.sendSuccess(data); } + /** + * test2 + */ + test2() { + const { ctx, service } = this; + const body = ctx.request.body; + console.log('test2 params:', body); + const data = { + age: 32 + }; + + this.sendSuccess(data); + } + async openLocalDir() { const self = this; const { ctx, service } = this; diff --git a/app/controller/v1/home.js b/app/controller/v1/home.js index cf4eded..5828faf 100644 --- a/app/controller/v1/home.js +++ b/app/controller/v1/home.js @@ -4,6 +4,9 @@ const BaseController = require('../base'); class HomeController extends BaseController { + /** + * 单页应用入口(vue、react等) + */ async index() { const { ctx } = this; @@ -13,26 +16,6 @@ class HomeController extends BaseController { await ctx.render('index.ejs', data); } - - async hello() { - const { ctx } = this; - - const data = { - title: 'hello' - }; - - this.sendSuccess(data); - } - - async helloPage() { - const { ctx } = this; - - const data = { - title: 'hello' - }; - - await ctx.render('hello.ejs', data); - } } module.exports = HomeController; diff --git a/app/extend/context.js b/app/extend/context.js index 812cc18..09b5274 100644 --- a/app/extend/context.js +++ b/app/extend/context.js @@ -4,16 +4,6 @@ */ 'use strict'; module.exports = { - isMobile() { - const deviceAgent = this.get('user-agent').toLowerCase(); - const agentID = deviceAgent.match(/(iphone|ipod|ipad|android)/); - if (agentID) { - // 手机访问 - return true; - } - // 电脑访问 - return false; - }, success(msg, data, total) { this.body = { success: true, @@ -28,8 +18,5 @@ module.exports = { msg, result: data, }; - }, - async infoPage(msg) { - await this.render('500', { msg }); - }, + } }; diff --git a/app/router/example.js b/app/router/example.js index 4ba9ab7..55decf8 100644 --- a/app/router/example.js +++ b/app/router/example.js @@ -35,6 +35,8 @@ module.exports = app => { router.post('/api/v1/example/selectFileDir', controller.v1.example.selectFileDir); // test some electron api router.post('/api/v1/example/testElectronApi', controller.v1.example.testElectronApi); + // test2 + router.post('/api/v1/example/test2', controller.v1.example.test2); // message show router.post('/api/v1/example/messageShow', controller.v1.example.messageShow); // message show confirm diff --git a/app/schedule/example.js b/app/schedule/example.js new file mode 100644 index 0000000..d673511 --- /dev/null +++ b/app/schedule/example.js @@ -0,0 +1,22 @@ +'use strict'; + +const Subscription = require('egg').Subscription; + +/** + * test + */ + +class Test extends Subscription { + static get schedule() { + return { + interval: '360m', + type: 'worker', + immediate: false, + disable: true, + }; + } + + async subscribe() {} +} + +module.exports = Test; diff --git a/app/service/example.js b/app/service/example.js index cecc66d..18ed139 100644 --- a/app/service/example.js +++ b/app/service/example.js @@ -1,8 +1,17 @@ 'use strict'; -const BaseService = require('./base'); +const Service = require('egg').Service; +const EeSocket = require('ee-core').Socket.EeSocket; +const socketClient = EeSocket.getClient(); + +class ExampleService extends Service { + + async testElectronApi(id = 0) { + const res = await socketClient.call('controller.example.test', {name:"gsx"}, {age:12}); + + return null; + } -class ExampleService extends BaseService { async openLocalDir(dir) { const self = this; @@ -93,12 +102,6 @@ class ExampleService extends BaseService { return result.data; } - async testElectronApi(id = 0) { - await this.ipcCall('example.testElectronApi'); - - return null; - } - async messageShow() { await this.ipcCall('example.messageShow'); diff --git a/app/service/storage.js b/app/service/storage.js index d3f6b19..ce9180f 100644 --- a/app/service/storage.js +++ b/app/service/storage.js @@ -1,73 +1,34 @@ 'use strict'; -const BaseService = require('./base'); -const path = require('path'); +const Storage = require('ee-core').Storage; +const Service = require('egg').Service; const _ = require('lodash'); -const lowdb = require('lowdb'); -const FileSync = require('lowdb/adapters/FileSync'); -const storageKey = require('../const/storageKey'); -const fs = require('fs'); -const os = require('os'); -const utils = require('../utils/utils'); -const pkg = require('../../package.json'); -const storageDb = 'db.json'; -class StorageService extends BaseService { - /* - * instance - */ - instance(file = null) { - if (!file) { - const storageDir = this.getStorageDir(); - if (!fs.existsSync(storageDir)) { - utils.mkdir(storageDir); - utils.chmodPath(storageDir, '777'); - } - file = path.normalize(storageDir + storageDb); - } - const isExist = fs.existsSync(file); - if (!isExist) { - return null; - } - - const adapter = new FileSync(file); - const db = lowdb(adapter); - - return db; - } +class StorageService extends Service { - /* - * getElectronIPCPort - */ - getElectronIPCPort() { - const key = storageKey.ELECTRON_IPC + '.port'; - const port = this.instance() - .get(key) - .value(); - - return port; - } - - /* - * getStorageDir - */ - getStorageDir() { - const userHomeDir = os.userInfo().homedir; - const storageDir = path.normalize(userHomeDir + '/' + pkg.name + '/'); - - return storageDir; + constructor () { + super(); + this.systemDB = Storage.JsonDB.connection('system'); + this.demoDB = Storage.JsonDB.connection('demo'); + this.systemDBKey = { + cache: 'cache' + }; + this.demoDBKey = { + preferences: 'preferences', + test_data: 'test_data' + }; } /* * 增 Test data */ async addTestData(user) { - const key = storageKey.TEST_DATA; - if (!this.instance().has(key).value()) { - this.instance().set(key, []).write(); + const key = this.demoDBKey.test_data; + if (!this.demoDB.has(key).value()) { + this.demoDB.set(key, []).write(); } - const data = this.instance() + const data = this.demoDB .get(key) .push(user) .write(); @@ -79,8 +40,8 @@ class StorageService extends BaseService { * 删 Test data */ async delTestData(name = '') { - const key = storageKey.TEST_DATA; - const data = this.instance() + const key = this.demoDBKey.test_data; + const data = this.demoDB .get(key) .remove({name: name}) .write(); @@ -92,8 +53,8 @@ class StorageService extends BaseService { * 改 Test data */ async updateTestData(name= '', age = 0) { - const key = storageKey.TEST_DATA; - const data = this.instance() + const key = this.demoDBKey.test_data; + const data = this.demoDB .get(key) .find({name: name}) // 修改找到的第一个数据,貌似无法批量修改 todo .assign({age: age}) @@ -106,8 +67,8 @@ class StorageService extends BaseService { * 查 Test data */ async getTestData(age = 0) { - const key = storageKey.TEST_DATA; - let data = this.instance() + const key = this.demoDBKey.test_data; + let data = this.demoDB .get(key) //.find({age: age}) 查找单个 .filter(function(o) { @@ -130,11 +91,11 @@ class StorageService extends BaseService { * all Test data */ async getAllTestData() { - const key = storageKey.TEST_DATA; - if (!this.instance().has(key).value()) { - this.instance().set(key, []).write(); + const key = this.demoDBKey.test_data; + if (!this.demoDB.has(key).value()) { + this.demoDB.set(key, []).write(); } - let data = this.instance() + let data = this.demoDB .get(key) .value(); diff --git a/app/utils/index.js b/app/utils/index.js index e6aa964..663c1fa 100644 --- a/app/utils/index.js +++ b/app/utils/index.js @@ -1,228 +1,7 @@ 'use strict'; -// MD5加密工具 -const crypto = require('crypto'); -// 使用crypto进行MD5加密 -const md5 = crypto.createHash('md5'); - -/* 通用函数集合 */ -// UTC时间格式化成本地时间 -exports.formatUTC = UTCDateString => { - if (!UTCDateString) { - return '-'; - } - // 格式化显示 - function formatFunc(str) { - return str > 9 ? str : '0' + str; - } - // 这步是关键 - const date2 = new Date(UTCDateString); - const year = date2.getFullYear(); - const mon = formatFunc(date2.getMonth() + 1); - const day = formatFunc(date2.getDate()); - const hour = date2.getHours(); - const min = date2.getMinutes(); - const second = date2.getSeconds(); - - const dateStr = - year + '-' + mon + '-' + day + ' ' + hour + ':' + min + ':' + second; - return dateStr; -}; -// 生成8位数的uid -exports.createNewUid = () => { - /* 生成8位随机整数,不能有4位连续数字,不能有4位重复数字 */ - // 4位重复数字 - // let reg1 = /\d{4}/; - const reg1 = /([\d])\1{3}/; - // 4位连续数字 - const reg2 = /(0(?=1)|1(?=2)|2(?=3)|3(?=4)|4(?=5)|5(?=6)|6(?=7)|7(?=8)|8(?=9)|9(?=0)){3}|(?:0(?=9)|9(?=8)|8(?=7)|7(?=6)|6(?=5)|5(?=4)|4(?=3)|3(?=2)|2(?=1)|1(?=0)){3}/; - // const numStr1 = '10122229'; - // const numStr2 = '78902100'; - // console.log(`reg1 is ${reg1.test(numStr1)}`); - // console.log(`reg2 is ${reg2.test(numStr2)}`); - - // 生成uid(随机20次应该能有1次满足条件) - let uid = null; - for (let i = 0; i < 20; i++) { - // 生成随机整数(范围11111111到19878711) - uid = String(parseInt(Math.random() * 8767600) + 11111111); - // console.log(`uid is ${uid}`) - // const uid = "89016530"; - // console.log(`reg1 is ${reg1.test(uid)}`); - // console.log(`reg2 is ${reg2.test(uid)}`); - if (!reg1.test(uid) && !reg2.test(uid)) { - // console.log(`uid is 1111`) - return uid; - } - } -}; - -// 生成密码加密的盐slat(8位字符串) -exports.saltPwd = () => { - let str = ''; - const arr = [ - '0', - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - 'a', - 'b', - 'c', - 'd', - 'e', - 'f', - 'g', - 'h', - 'i', - 'j', - 'k', - 'l', - 'm', - 'n', - 'o', - 'p', - 'q', - 'r', - 's', - 't', - 'u', - 'v', - 'w', - 'x', - 'y', - 'z', - ]; - - let pos = null; - for (let i = 0; i < 8; i++) { - pos = Math.round(Math.random() * (arr.length - 1)); - str += arr[pos]; - } - return str; -}; -// 生成随机字符串(数字(0-9),字母(a-z,A-Z)) -exports.randomWord = (randomFlag, min, max) => { - let str = ''; - let range = min; - const arr = [ - '0', - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - 'a', - 'b', - 'c', - 'd', - 'e', - 'f', - 'g', - 'h', - 'i', - 'j', - 'k', - 'l', - 'm', - 'n', - 'o', - 'p', - 'q', - 'r', - 's', - 't', - 'u', - 'v', - 'w', - 'x', - 'y', - 'z', - 'A', - 'B', - 'C', - 'D', - 'E', - 'F', - 'G', - 'H', - 'I', - 'J', - 'K', - 'L', - 'M', - 'N', - 'O', - 'P', - 'Q', - 'R', - 'S', - 'T', - 'U', - 'V', - 'W', - 'X', - 'Y', - 'Z', - ]; - - // 随机产生 - if (randomFlag) { - range = Math.round(Math.random() * (max - min)) + min; - } - let pos = null; - for (let i = 0; i < range; i++) { - pos = Math.round(Math.random() * (arr.length - 1)); - str += arr[pos]; - } - return str; - // 使用方法 - // 生成3-32位随机串:randomWord(true, 3, 32) - // 生成43位随机串:randomWord(false, 43) -}; // 生成两数之间随机数 exports.randomNums = (min, max) => { return Math.round(Math.random() * (max - min) + min); }; -// 客户端密码加密 -exports.saltPwdMd5 = (password, saltPwd) => { - const md5 = crypto.createHash('md5'); - // 加了盐的客户端密码 - const saltPassword = password + ':' + saltPwd; - // 加盐的密码再用MD5加密 - const saltPasswordMd5 = md5.update(saltPassword).digest('hex'); - return saltPasswordMd5; -}; -exports.createToken = data => { - // console.log(`data is ${JSON.stringify(data)}`); - const { uid, app } = data; - // 当前时间戳 - const created = Math.floor(Date.now() / 1000); - const token = app.jwt.sign({ uid, created }, app.config.jwt.secret, { - expiresIn: '30d', - }); - - return token; -}; -exports.createAdminToken = data => { - // console.log(`data is ${JSON.stringify(data)}`); - const { id, role, app } = data; - // 当前时间戳 - const created = Math.floor(Date.now() / 1000); - - const token = app.jwt.sign({ id, role, created }, app.config.jwt.secret, { - expiresIn: '30d', - }); - - return token; -}; diff --git a/config/config.default.js b/config/config.default.js index 26c62e9..698c537 100644 --- a/config/config.default.js +++ b/config/config.default.js @@ -1,8 +1,8 @@ -/* eslint valid-jsdoc: "off" */ - 'use strict'; + const path = require('path'); -const electronEggConfig = require('../electron/config').get('webEgg'); +const Utils = require('ee-core').Utils; +const eggConfig = Utils.getEggConfig(); /** * @param {Egg.EggAppInfo} appInfo app info @@ -21,23 +21,15 @@ module.exports = appInfo => { config.middleware = []; // add your user config here - const userConfig = { - // myAppName: 'egg', - }; + const userConfig = {}; config.cluster = { listen: { - port: electronEggConfig.port || 7068, - hostname: electronEggConfig.hostname || '0.0.0.0', - // path: '/var/run/egg.sock', + port: eggConfig.port || 7068, + hostname: eggConfig.hostname || '127.0.0.1', }, }; - // jwt插件配置(盐) - config.jwt = { - secret: 'jcgame88', - }; - /* 跨域插件配置-start */ config.security = { xframe: { @@ -50,7 +42,7 @@ module.exports = appInfo => { // 'http://127.0.0.1:8080' ], methodnoallow: { enable: false }, - // 安全配置(很重要) + // 安全配置 csrf: { enable: false, ignoreJSON: true, // 默认为 false,当设置为 true 时,将会放过所有 content-type 为 `application/json` 的请求 @@ -59,17 +51,10 @@ module.exports = appInfo => { // 允许的跨域请求类型(GET,POST) config.cors = { origin: '*', - // allowMethods: 'GET,POST', allowMethods: 'GET,POST,HEAD,PUT,OPTIONS,DELETE,PATCH', }; /* 跨域插件配置-end */ - // 校验插件配置(支持 parameter的所有配置项) - config.validate = { - // convert: false, - // validateRoot: false, - }; - // 获取真实ip config.maxProxyCount = 2; diff --git a/config/config.local.js b/config/config.local.js index 5650d84..22160a0 100644 --- a/config/config.local.js +++ b/config/config.local.js @@ -1,13 +1,10 @@ 'use strict'; -// 本地环境-配置文件 -const storageDir = require('../electron/lib/storage').getStorageDir(); -/* - * 远程调用 - */ -exports.outApi = { - login: 'http://local.com/api/login', -}; +const Utils = require('ee-core').Utils; +const logDir = Utils.getLogDir(); + exports.logger = { - dir: storageDir + 'logs/local', + dir: logDir, }; + + diff --git a/config/config.prod.js b/config/config.prod.js index c5122c4..c9e7b1a 100644 --- a/config/config.prod.js +++ b/config/config.prod.js @@ -1,14 +1,11 @@ 'use strict'; -// 本地环境-配置文件 -const storageDir = require('../electron/lib/storage').getStorageDir(); -/* - * 远程调用 - */ -exports.outApi = { - login: 'http://api.local.com/api/login', -}; +const Utils = require('ee-core').Utils; +const logDir = Utils.getLogDir(); + exports.logger = { - dir: storageDir + 'logs/prod', + dir: logDir, }; + + diff --git a/config/plugin.js b/config/plugin.js index 96047be..2a42e62 100644 --- a/config/plugin.js +++ b/config/plugin.js @@ -4,12 +4,6 @@ *Egg插件 */ -// jwt登录状态验证插件 -exports.jwt = { - enable: true, - package: 'egg-jwt', -}; - // 跨域插件 exports.cors = { enable: true, diff --git a/electron/config/config.default.js b/electron/config/config.default.js new file mode 100644 index 0000000..2a6357f --- /dev/null +++ b/electron/config/config.default.js @@ -0,0 +1,102 @@ +'use strict'; + +const dayjs = require('dayjs'); + +/** + * 默认配置 + */ +module.exports = (appInfo) => { + /** + * built-in config + * @type {Ee.EeAppConfig} + **/ + const config = {}; + + /* 开发模式配置 */ + config.developmentMode = { + default: 'vue', + mode: { + vue: { + hostname: 'localhost', + port: 8080 + }, + react: { + hostname: 'localhost', + port: 3000 + }, + ejs: { + hostname: 'localhost', + port: 7068 // The same as the egg port + } + } + }; + + /* 开发者工具 */ + config.openDevTools = false; + + /* 应用程序顶部菜单 */ + config.openAppMenu = false; + + /* 主窗口 */ + config.windowsOption = { + width: 980, + height: 650, + minWidth: 800, + minHeight: 650, + webPreferences: { + //webSecurity: false, + contextIsolation: false, // 设置此项为false后,才可在渲染进程中使用electron api + nodeIntegration: true, + }, + frame: true, + //titleBarStyle: 'hidden' + }; + + /* ee框架日志 */ + config.logger = { + appLogName: `ee-${dayjs().format('YYYY-MM-DD')}.log`, + errorLogName: `ee-error-${dayjs().format('YYYY-MM-DD')}.log` + } + + /* 远程web地址 (可选) */ + config.remoteUrl = { + enable: false, // 是否启用 + url: 'https://discuz.chat/' // Any web url + }; + + /* egg服务 (可选) */ + config.egg = { + title: 'electron-egg', // 进程的title属性标识(默认你的应用名称-英文) + port: 7068, + hostname: '127.0.0.1', + workers: 1 // 工作进程数据 + }; + + /* 应用自动升级 (可选) */ + config.autoUpdate = { + windows: false, // windows平台 + macOS: false, // macOs 需要签名验证 + linux: false, // linux平台 + options: { + provider: 'generic', // or github, s3, bintray + url: 'http://kodo.qiniu.com/' // resource dir, end with '/' + }, + force: false, // 强制更新(运行软件时,检查新版本并后台下载安装) + }; + + /* 被浏览器唤醒 (可选) */ + config.awakeProtocol = { + protocol: 'electron-egg', // 自定义协议名(默认你的应用名称-英文) + args: [] + }; + + /* 托盘 (可选) */ + config.tray = { + title: 'EE程序', // 托盘显示标题 + icon: '/public/images/tray_logo.png' // 托盘图标 + } + + return { + ...config + }; +} diff --git a/electron/config/config.local.js b/electron/config/config.local.js new file mode 100644 index 0000000..b6ddd23 --- /dev/null +++ b/electron/config/config.local.js @@ -0,0 +1,8 @@ +'use strict'; + +/* + * test + */ +exports.testConfig = { + login: 'http://local.com/api/login', +}; diff --git a/electron/config/config.prod.js b/electron/config/config.prod.js new file mode 100644 index 0000000..89633fe --- /dev/null +++ b/electron/config/config.prod.js @@ -0,0 +1,8 @@ +'use strict'; + +/* + * test + */ +exports.testConfig = { + login: 'http://prod.com/api/login', +}; diff --git a/electron/controller/example.js b/electron/controller/example.js new file mode 100644 index 0000000..084437e --- /dev/null +++ b/electron/controller/example.js @@ -0,0 +1,38 @@ +'use strict'; + +const Controller = require('ee-core').Controller; +const { app } = require('electron'); + +class ExampleController extends Controller { + + /** + * test + */ + async test (args) { + let obj = { + status:'ok' + } + + // 调用egg的某个api + // const result = await this.app.curlEgg('post', '/api/v1/example/test2', {name: 'gsx2'}); + // console.log('fffffffffff: ', result); + //this.app.logger.info('ssssssssssssssssssss'); + + return obj; + } + + /** + * hello + */ + hello (args, event) { + let newMsg = args + " +1"; + let reply = ''; + reply = '收到:' + args + ',返回:' + newMsg; + + // let channel = "example.socketMessageStop"; + // event.reply(`${channel}`, '另外的数据'); + return reply; + } +} + +module.exports = ExampleController; diff --git a/electron/library/autoUpdater.js b/electron/library/autoUpdater.js new file mode 100644 index 0000000..445dda1 --- /dev/null +++ b/electron/library/autoUpdater.js @@ -0,0 +1,148 @@ +'use strict'; + +const {app} = require('electron'); +const updater = require("electron-updater"); +const autoUpdater = updater.autoUpdater; +const path = require('path'); + +/** + * 自动升级模块 + */ +module.exports = { + + /** + * 安装 + */ + install (eeApp) { + console.log('[preload] load AutoUpdater module'); + + const status = { + error: -1, + available: 1, + noAvailable: 2, + downloading: 3, + downloaded: 4, + } + + const updateConfig = eeApp.config.autoUpdate; + const mainWindow = eeApp.electron.mainWindow; + const version = app.getVersion(); + console.log('[preload:autoUpdater] current version: ', version); + + // 设置下载服务器地址 + let server = updateConfig.options.url; + let lastChar = server.substring(server.length - 1); + server = lastChar === '/' ? server : server + "/"; + console.log('[preload:autoUpdater] server: ', server); + updateConfig.options.url = server; + + // 是否后台自动下载 + autoUpdater.autoDownload = updateConfig.force ? true : false; + if (process.env.EE_SERVER_ENV == 'local') { + autoUpdater.updateConfigPath = path.join(__dirname, '../../out/dev-app-update.yml') + } + + try { + autoUpdater.setFeedURL(updateConfig.options); + } catch (error) { + eeApp.logger.error('[preload:autoUpdater] setFeedURL error : ', error); + } + + autoUpdater.on('checking-for-update', () => { + //sendStatusToWindow('正在检查更新...'); + }) + autoUpdater.on('update-available', (info) => { + info.status = status.available; + info.desc = '有可用更新'; + sendStatusToWindow(mainWindow, info); + }) + autoUpdater.on('update-not-available', (info) => { + info.status = status.noAvailable; + info.desc = '没有可用更新'; + sendStatusToWindow(mainWindow, info); + }) + autoUpdater.on('error', (err) => { + let info = { + status: status.error, + desc: err + } + sendStatusToWindow(mainWindow, info); + }) + autoUpdater.on('download-progress', (progressObj) => { + let percentNumber = parseInt(progressObj.percent); + let totalSize = bytesChange(progressObj.total); + let transferredSize = bytesChange(progressObj.transferred); + let text = '已下载 ' + percentNumber + '%'; + text = text + ' (' + transferredSize + "/" + totalSize + ')'; + + let info = { + status: status.downloading, + desc: text, + percentNumber: percentNumber, + totalSize: totalSize, + transferredSize: transferredSize + } + sendStatusToWindow(mainWindow, info); + }) + autoUpdater.on('update-downloaded', (info) => { + info.status = status.downloaded; + info.desc = '下载完成'; + sendStatusToWindow(mainWindow, info); + // quit and update + eeApp.appQuit(); + autoUpdater.quitAndInstall(); + }); + + + }, + + /** + * 检查更新 + */ + checkUpdate () { + autoUpdater.checkForUpdates(); + }, + + /** + * 下载更新 + */ + download () { + autoUpdater.downloadUpdate(); + }, + + +} + +/** + * 向前端发消息 + */ +function sendStatusToWindow(mainWindow, content = {}) { + const textJson = JSON.stringify(content); + eLogger.info(textJson); + const channel = 'app.updater'; + mainWindow.webContents.send(channel, textJson); +} + +function bytesChange (limit) { + 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; +} + + \ No newline at end of file diff --git a/electron/library/awaken.js b/electron/library/awaken.js new file mode 100644 index 0000000..fd38009 --- /dev/null +++ b/electron/library/awaken.js @@ -0,0 +1,56 @@ +'use strict'; + +const { app } = require('electron'); + +/** + * 应用唤醒模块 + */ +module.exports = { + + /** + * 安装 + */ + install (eeApp) { + console.log('[preload] load awaken module'); + const protocolInfo = eeApp.config.awakeProtocol; + const PROTOCOL = protocolInfo.protocol; + + app.setAsDefaultProtocolClient(PROTOCOL); + + handleArgv(process.argv); + + app.on('second-instance', (event, argv) => { + if (process.platform === 'win32') { + handleArgv(argv) + } + }) + + // 仅用于macOS + app.on('open-url', (event, urlStr) => { + handleUrl(urlStr) + }) + + // 参数处理 + function handleArgv(argv) { + const offset = app.isPackaged ? 1 : 2; + const url = argv.find((arg, i) => i >= offset && arg.startsWith(PROTOCOL)); + handleUrl(url) + } + + // url解析 + function handleUrl(awakeUrlStr) { + if (!awakeUrlStr || awakeUrlStr.length === 0) { + return + } + const {hostname, pathname, search} = new URL(awakeUrlStr); + let awakeUrlInfo = { + urlStr: awakeUrlStr, + urlHost: hostname, + urlPath: pathname, + urlParams: search && search.slice(1) + } + console.log('[awaken] [handleUrl] awakeUrlInfo:', awakeUrlInfo); + } + } +} + diff --git a/electron/library/chromeExtension.js b/electron/library/chromeExtension.js new file mode 100644 index 0000000..fabb00e --- /dev/null +++ b/electron/library/chromeExtension.js @@ -0,0 +1,88 @@ +'use strict'; + +const { app, session } = require('electron'); +const _ = require('lodash'); +const fs = require('fs'); +const path = require('path'); + +/** + * chrome扩展模块 + */ +module.exports = { + + /** + * 安装 + */ + install (eeApp) { + console.log('[preload] load chrome extension module'); + const extensionIds = this.getAllIds(); + + for (let i = 0; i < extensionIds.length; i++) { + await this.load(extensionIds[i]); + } + }, + + /** + * 获取扩展id列表(crx解压后的目录名,即是该扩展的id) + */ + getAllIds () { + const extendsionDir = this.getDirectory(); + const ids = getDirs(extendsionDir); + + return ids; + }, + + /** + * 扩展所在目录 + */ + getDirectory () { + let extensionDirPath = ''; + let variablePath = 'build'; // 打包前路径 + if (app.isPackaged) { + variablePath = '..'; // 打包后路径 + } + extensionDirPath = path.join(app.getAppPath(), variablePath, "extraResources", "chromeExtension"); + + return extensionDirPath; + }, + + /** + * 加载扩展 + */ + load (extensionId = '') { + if (_.isEmpty(extensionId)) { + return false + } + + try { + const extensionPath = path.join(this.getDirectory(), extensionId); + console.log('[chromeExtension] [load] extensionPath:', extensionPath); + await session.defaultSession.loadExtension(extensionPath, { allowFileAccess: true }); + } catch (e) { + console.log('[chromeExtension] [load] load extension error extensionId:%s, errorInfo:%s', extensionId, e.toString()); + return false + } + + return true + } +} + +/** + * 获取目录下所有文件夹 + */ +function getDirs(dir) { + if (!dir) { + return []; + } + + const components = []; + const files = fs.readdirSync(dir); + files.forEach(function(item, index) { + const stat = fs.lstatSync(dir + '/' + item); + if (stat.isDirectory() === true) { + components.push(item); + } + }); + + return components; +}; diff --git a/electron/library/security.js b/electron/library/security.js new file mode 100644 index 0000000..21e0873 --- /dev/null +++ b/electron/library/security.js @@ -0,0 +1,26 @@ +'use strict'; + +/** + * 安全模块 + */ + +module.exports = { + + /** + * 安装 + */ + install (eeApp) { + console.log('[preload] load security module'); + const runWithDebug = process.argv.find(function(e){ + let isHasDebug = e.includes("--inspect") || e.includes("--inspect-brk") || e.includes("--remote-debugging-port"); + return isHasDebug; + }) + + // 不允许远程调试 + if (runWithDebug) { + console.log('[error] Remote debugging is not allowed, runWithDebug:', runWithDebug); + eeApp.appQuit(); + } + } + +} diff --git a/electron/library/tray.js b/electron/library/tray.js new file mode 100644 index 0000000..ff0a206 --- /dev/null +++ b/electron/library/tray.js @@ -0,0 +1,69 @@ +'use strict'; + +const {Tray, Menu} = require('electron'); +const path = require('path'); + +/** + * 托盘模块 + */ + +module.exports = { + + /** + * 安装 + */ + install (eeApp) { + console.log('[preload] load tray module'); + const trayConfig = eeApp.config.tray; + const mainWindow = eeApp.electron.mainWindow; + + // 托盘图标 + let iconPath = path.join(eeApp.config.homeDir, trayConfig.icon); + + // 托盘菜单功能列表 + let trayMenuTemplate = [ + { + label: '显示', + click: function () { + mainWindow.show(); + } + }, + { + label: '退出', + click: function () { + eeApp.appQuit(); + } + } + ] + + // 点击关闭,最小化到托盘 + mainWindow.on('close', (event) => { + mainWindow.hide(); + mainWindow.setSkipTaskbar(true); + event.preventDefault(); + }); + mainWindow.show(); + + // 测试发现:创建的Tray对象实例变量和app.whenReady()在同一模块中定义才行 + // 赋值给eeApp.electron.tray,已在框架ee-core包中定义 + // 如果赋值给其它变量,可能出现异常,估计是electron的bug + + eeApp.electron.tray = new Tray(iconPath); + let appTray = eeApp.electron.tray; + + appTray.setToolTip(trayConfig.title); // 托盘标题 + const contextMenu = Menu.buildFromTemplate(trayMenuTemplate); + appTray.setContextMenu(contextMenu); + + // 监听 显示/隐藏 + appTray.on('click', function(){ + if (mainWindow.isVisible()) { + mainWindow.hide(); + mainWindow.setSkipTaskbar(false); + } else { + mainWindow.show(); + mainWindow.setSkipTaskbar(true); + } + }); + } +} diff --git a/electron/preload/index.js b/electron/preload/index.js new file mode 100644 index 0000000..673f7a9 --- /dev/null +++ b/electron/preload/index.js @@ -0,0 +1,45 @@ +'use strict'; + +/************************************************* + ** preload为预加载模块,该文件将会在程序启动时加载 ** + *************************************************/ + +const is = require('electron-is'); +const tray = require('../library/tray'); +const security = require('../library/security'); +const awaken = require('../library/awaken'); +const chromeExtension = require('../library/chromeExtension'); + +/** + * 预加载模块入口 + */ +module.exports = async (app) => { + + //已实现的功能模块,可选择性使用和修改 + + tray.install(app); + + security.install(app); + + awaken.install(app); + + chromeExtension.install(app); + + loadUpdate(app); +} + +/** + * 加载自动升级模块 + */ +function loadUpdate (app) { + const config = app.config.autoUpdate; + if ( (is.windows() && config.windows) || (is.macOS() && config.macOS) || (is.linux() && config.linux) ) { + const autoUpdater = require('../library/autoUpdater'); + autoUpdater.install(); + + // 是否检查更新 + if (config.force) { + autoUpdater.checkUpdate(); + } + } +} \ No newline at end of file diff --git a/frontend/src/views/demo/socket/Index.vue b/frontend/src/views/demo/socket/Index.vue index 7e37368..941c935 100644 --- a/frontend/src/views/demo/socket/Index.vue +++ b/frontend/src/views/demo/socket/Index.vue @@ -70,7 +70,7 @@ export default { }, helloHandle(value) { const self = this; - this.$ipcCallMain('example.hello', value).then(r => { + this.$ipcCallMain('controller.example.hello', value).then(r => { self.$message.info(r); }) }, diff --git a/main.js b/main.js index eac4027..1a78c67 100644 --- a/main.js +++ b/main.js @@ -1,6 +1,42 @@ -const EeApp = require('ee-core').EeAppliaction; +const Appliaction = require('ee-core').Appliaction; +const { app } = require('electron'); -new EeApp(); +class Main extends Appliaction { + + constructor() { + super(); + } + + /** + * core app have been loaded + */ + async ready () { + // do some things + } + + /** + * main window have been loaded + */ + async windowReady () { + // do some things + + // const app = this; + // // preload预加载模块 + // const preload = require('./electron/preload'); + // preload(app); + + } + + /** + * before app close + */ + async beforeClose () { + // do some things + + } +} + +new Main(); diff --git a/package.json b/package.json index 60acabc..51a6437 100755 --- a/package.json +++ b/package.json @@ -4,8 +4,9 @@ "description": "A fast, desktop software development framework", "main": "main.js", "scripts": { - "start": "electron .", + "start": "electron . ", "dev": "electron . --env=local", + "test": "set DEBUG=* && electron . --env=local", "build-w": "electron-builder -w --ia32", "build-w-64": "electron-builder -w --x64", "build-m": "electron-builder -m", @@ -29,7 +30,9 @@ "files": [ "**/*", "!frontend/", - "!run/" + "!run/", + "!logs/", + "!data/" ], "extraResources": { "from": "./build/extraResources/", @@ -96,10 +99,11 @@ "eslint-plugin-prettier": "^3.0.1" }, "dependencies": { + "dayjs": "^1.10.7", "egg": "^2.33.1", - "egg-scripts": "^2.13.0", - "egg-view-ejs": "^2.0.0", - "electron-updater": "^4.3.5", - "lodash": "^4.17.21" + "egg-cluster": "^1.27.1", + "egg-cors": "^2.2.3", + "egg-scripts": "^2.15.2", + "egg-view-ejs": "^2.0.1" } } diff --git a/asset/loading.html b/public/html/loading.html similarity index 90% rename from asset/loading.html rename to public/html/loading.html index f651e6b..3b58374 100644 --- a/asset/loading.html +++ b/public/html/loading.html @@ -16,7 +16,7 @@
+