Commit 1a7e7d16 authored by liujiaxin's avatar liujiaxin

Initial commit

parents
Pipeline #90 failed with stages
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
yarn.lock
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
proxy.conf.json
app.ico

66.1 KB

app.png

199 KB

const { app, BrowserWindow, session, Menu, protocol, shell, ipcMain, remote} = require('electron');
const crypto = require('crypto');
//const SparkMD5 = require('spark-md5');
const SparkMD5 = {
hash: s => crypto.createHash('md5').update(s).digest("hex")
}
const VERSION = 1562641833886;
console.log('current version', VERSION)
let NEW_VERSION = null;
const VERSION_CHECK_URL = 'http://static.jm-jzyf.com/imman/version';
let win = null;
const STATUS = {
ENOSPC_FIRED: false,
};
process.on('uncaughtException', function (err) {
console.log('================== ipc main error ==================');
console.log(err);
if (win && err.code && err.code === 'ENOSPC') {
//if(STATUS.ENOSPC_FIRED) {
// return;
//}
console.log('background-error-message');
win.webContents.send('background-error-message' , {code: err.code });
//STATUS.ENOSPC_FIRED = true;
}
console.log('====================================================');
})
const rmdir = require('rimraf');
const fs = require('fs');
var deleteFolderRecursive = function(path) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach(function(file, index){
var curPath = path + "/" + file;
if (fs.lstatSync(curPath).isDirectory()) { // recurse
deleteFolderRecursive(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
};
const Datastore = require('nedb')
const path = require ('path');
const fetch = require('node-fetch');
const request = require('request');
// const util = require('util');
// const stream = require('stream');
// const pipeline = util.promisify(stream.pipeline);
let basepath = app.getAppPath();
basepath = path.dirname(basepath);
console.log('basepath: ', basepath)
const WindowsUpdater =path.join(path.dirname(basepath), 'updater.exe')
const HASUPDATER = fs.existsSync(path.join(basepath, 'app.asar/updater.exe'));
console.log('WindowsUpdater: ', HASUPDATER);
const appDataPath = path.join(basepath, 'appData')
if (!fs.existsSync(appDataPath)) {
fs.mkdirSync(appDataPath);
}
console.log('appDataPath: ', appDataPath)
// const userDataPath = path.join(appDataPath, 'userData')
// console.log('basepath', basepath)
// if (process.platform == "win32") {
app.setPath("appData", appDataPath);
app.setPath("userData", path.join(appDataPath, 'user'));
// }
console.log('userData: ', path.join(appDataPath, 'user'))
const appCachePath = path.join(appDataPath, 'cacheData')
if (!fs.existsSync(appCachePath)) {
fs.mkdirSync(appCachePath);
}
console.log('appCachePath: ', appCachePath)
const appDBPath = path.join(appDataPath, 'resourses')
if (!fs.existsSync(appDBPath)) {
fs.mkdirSync(appDBPath);
}
console.log('appDBPath: ', appDBPath)
const nedb = new Datastore({ filename: path.join(appDataPath, 'resources.res'), autoload: true });
const nedb_res = new Datastore({ filename: path.join(appDataPath, 'resources.map'), autoload: true });
let address = 'https://imman.ireadabc.com';
let devTool = false;
let multi = false;
console.log('process.argv', process.argv);
if (process.argv) {
const address_idx = process.argv.findIndex(el => el.startsWith('--debug-address'));
if (address_idx > -1 && process.argv[address_idx]) {
address = process.argv[address_idx];
const addr_p = address.split('=');
if (addr_p[1] && addr_p[1].trim()) {
address = addr_p[1].trim()
}
}
const dev_idx = process.argv.findIndex(el => el === '--dev-tool');
if (dev_idx > -1) {
devTool = true;
}
const multi_idx = process.argv.findIndex(el => el === '--multi');
if (multi_idx > -1) {
multi = true;
}
}
if (!multi) {
const gotTheLock = app.requestSingleInstanceLock()
console.log('gotTheLock', gotTheLock)
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (win) {
if (win.isMinimized()) win.restore()
win.focus()
}
})
// Create myWindow, load the rest of the app, etc...
app.on('ready', createWindow)
}
} else {
app.on('ready', createWindow)
}
//https://github.com/whitesmith/electron-asar-updater
function mvOrMove (src, dst) {
const wup = path.join(basepath, 'app.asar');
var winArgs = ''
console.log('Checking for ' + src + " " + fs.existsSync(src))
if (!fs.existsSync(src)) {
return;
}
try {
//fs.accessSync(src)
try {
console.log(
'Going to shell out to move: ' + src + ' to: ' + dst
)
let executable = process.execPath
const { spawn } = require('child_process')
console.log(
'Going to start the windows updater:' +
WindowsUpdater +
' ' +
src +
' ' +
dst +
' ' +
executable
)
fs.writeFileSync(
WindowsUpdater,
fs.readFileSync(
`${wup}/updater.exe`
)
)
// JSON.stringify() calls mean we're correctly quoting paths with spaces
winArgs = `${JSON.stringify(WindowsUpdater)} ${JSON.stringify(src)} ${JSON.stringify(dst)} ${JSON.stringify(executable)}`
console.log(winArgs)
// and the windowsVerbatimArguments options argument, in combination with the /s switch, stops windows stripping quotes from our commandline
// spawn(`${JSON.stringify(WindowsUpdater)}`,[`${JSON.stringify(src)}`,`${JSON.stringify(dst)}`], {detached: true, windowsVerbatimArguments: true, stdio: 'ignore'})
// so we have to spawn a cmd shell, which then runs the updater, and leaves a visible window whilst running
spawn('cmd', ['/s', '/c', '"' + winArgs + '"'], {
detached: true,
windowsVerbatimArguments: true,
stdio: 'ignore'
})
// "Updater.end()" will trigger a callback, exec app.quit() in the callback.
} catch (error) {
console.log('Shelling out to move failed: ' + error)
}
} catch (error) {
console.log("Couldn't see an " + src + ' error was: ' + error)
}
}
class lib {
static exit (cleanCookie) {
if (cleanCookie) {
BrowserWindow.getAllWindows().forEach((win) => {
win.webContents.session.clearStorageData({storages: ['cookies']}, () => {
//console.log('Successfully eliminate cookies')
})
})
}
app.quit()
app.exit(0)
}
static externalOpenURL (url) {
shell.openExternal(url)
}
}
class CacheManager{
constructor(key){
this.cacheFolderPath = path.join(appCachePath, SparkMD5.hash(key));
}
/**
* get all file from a courseware folder
*/
keys(){
console.log('keys not implement yet')
}
/**
* save a file to a courseware folder
*/
put(url, data) {
console.log('put not implement yet')
}
/**
* delete a file form a courseware folder
*/
delete(url){
console.log('delete not implement yet')
}
}
class CachesManager{
openPath = null;
constructor(){
}
/**
* delete a courseware cache folder
*/
delete(sid){
return new Promise((resolve, reject) => {
// const parts = key.split('-');
// const sid = parts[2];
const p = path.join(appCachePath, SparkMD5.hash(sid));
console.log('remove dir ', p);
if (fs.existsSync(p) && fs.lstatSync(p).isDirectory()) {
rmdir(p, function(error){
if (error) {
reject();
} else {
console.log('remove ok ');
resolve();
}
});
} else {
console.log('is not a dir ', p, sid);
resolve();
}
/*nedb_res.find({}, function(err, docs) {
console.log(docs);
});*/
nedb_res.remove({sid: sid }, { multi: true }, function(err, numRemoved) {
nedb_res.persistence.compactDatafile();
})
});
}
/**
* list all courseware cache folder
*/
keys(key){
return new Promise((resolve, reject) => {
fs.readdir(appCachePath, function (err, files) {
//handling error
if (err) {
reject()
return console.log('Unable to scan directory: ' + err);
}
//listing all files using forEach
const folders = files.filter(file => {
console.log('keys', file);
return fs.existsSync(file) && fs.lstatSync(file).isDirectory()
})
resolve(folders);
});
});
}
/**
* open a courseware cache folder,
* return CacheManager
*/
open(key){
// this.openPath = path.join(appCachePath, SparkMD5.hash(key));
const p = path.join(appCachePath, SparkMD5.hash(key));
return new Promise((resolve, reject) => {
resolve(new CacheManager(p))
});
}
match(){
return new Promise((resolve, reject) => {
});
}
}
const caches = new CachesManager();
ipcMain.on('asynchronous-db-message', (event, arg) => {
let data = {cb_key: arg.cb_key};
if (arg.action === 'insert') {
/*nedb.remove({sid: arg.data.sid }, {multi: true}, function(err, numRemoved) {
nedb.persistence.compactDatafile();
nedb.insert(arg.data, function(err) {
data.err = err;
event.reply('asynchronous-db-reply', data);
})
})*/
nedb.update({sid: arg.data.sid }, arg.data, {upsert: true, returnUpdatedDocs: true}, function(err, numAffected, affectedDocuments, upsert) {
data.err = err;
event.reply('asynchronous-db-reply', data);
})
} else if (arg.action === 'remove') {
nedb.remove(arg.query, arg.options || {}, function(err, numRemoved) {
data.err = err;
data.numRemoved = numRemoved;
nedb.persistence.compactDatafile();
event.reply('asynchronous-db-reply', data);
})
} else if (arg.action === 'find') {
nedb.find(arg.query, function (err, docs) {
data['err'] = err;
data['docs'] = docs;
event.reply('asynchronous-db-reply', data);
}) ;
} else if (arg.action === 'syncDataCache') {
nedb.find({}, function(err, docs) {
docs.forEach(async doc => {
const key = doc.sid;//`item-cache-${doc.sid}-${doc.timestamp}`;
const p = path.join(appCachePath, SparkMD5.hash(key));
if (!fs.existsSync(p)) {
nedb.remove({sid: doc.sid}, { multi: true }, (err, numRemoved) => {
nedb.persistence.compactDatafile();
});
}
});
});
}
// console.log('asynchronous-db-reply', arg.action, data)
});
class CoursewareResourceManager {
constructor() {
ipcMain.on('asynchronous-message', this.onMessage);
}
static async progressDownloader(event, arg ) {
/*let url = urlOrRequest;
let response = await fetch(url);
if (urlOrRequest instanceof Request) {
url = urlOrRequest.url;
}*/
//if (STATUS.ENOSPC_FIRED) {
// console.log('STATUS.ENOSPC_FIRED and return' )
// return new Promise( (resolve, reject) => reject({status: 'ENOSPC'}));
//}
let url = arg.url;
if (url.indexOf('?') > -1) {
url = url.slice(0, qp)
}
let isBin = false;
if (url.endsWith('.mp4')
|| url.endsWith('.mp3')
|| url.endsWith('.jpg')
|| url.endsWith('.png')){
isBin = true;
}
if (!fs.existsSync(appDataPath)) {
fs.mkdirSync(appDataPath);
}
const target_path = path.join(appCachePath, SparkMD5.hash(arg.sid), SparkMD5.hash(url));
const folder = path.dirname(target_path);
if (!fs.existsSync(folder)) {
fs.mkdirSync(folder);
}
let receivedLength = 0;
const startTime = new Date().getTime();
const finishFn = (resolve, len, time) => {
resolve(len, time)
}
return new Promise( (resolve, reject) => {
fetch(url, {timeout: 5000}).then(res => {
const contentLength = +res.headers.get('Content-Length');
let fileSizeInBytes = 0;
if (fs.existsSync(target_path)) {
const stats = fs.statSync(target_path);
fileSizeInBytes = stats.size;
}
//const stats = fs.statSync(target_path);
//const fileSizeInBytes = stats.size;
event.reply('asynchronous-reply', {
len: contentLength,
status: 'init',
...arg
});
if (fileSizeInBytes == contentLength) {
event.reply('asynchronous-reply', {
loaded: contentLength,
status: 'downloading',
...arg
});
finishFn(resolve, contentLength, 0)
return ;
}
const output = fs.createWriteStream(target_path);
res.body.on('data', (chunk) => {
receivedLength += chunk.length;
event.reply('asynchronous-reply', {
loaded: chunk.length,
status: 'downloading',
...arg
});
})
/*res.body.on('end', () => {
console.log('### DONE ###');
//resolve();
})*/
output.on("finish", () => {
//console.log('### finish ###');
//resolve();
finishFn(resolve, contentLength, new Date().getTime() - startTime)
});
res.body.pipe(output);
res.body.on("error", (err) => {
console.log('read response error', err)
reject(err);
});
output.on('error', (err) => {
console.log('write datea to disk error', err)
reject(err);
});
}).catch(err => {
reject(err);
});
});
}
async onMessage(event, arg) {
// console.log('Handling message event:', arg);
/*if (arg.command === 'syncDataCache') {
nedb.find({}, function(err, docs) {
console.log('Handling syncDataCache:', err, docs);
docs.forEach(async doc => {
const key = `item-cache-${doc.sid}-${doc.timestamp}`;
const p = path.join(appCachePath, SparkMD5.hash(key));
console.log(p, fs.existsSync(p))
if (!fs.existsSync(p)) {
nedb.remove({sid: doc.sid}, { multi: true }, (err, numRemoved) => {
nedb.persistence.compactDatafile();
});
}
});
});
return;
} */
if (arg.command === 'remove') {
if (!arg.key) {
return;
}
caches.delete(arg.sid)
.then(() => {
event.reply('asynchronous-reply', {
error: null, ...arg
});
}).catch(err => {
event.reply('asynchronous-reply', {
error: err, ...arg
});
});
nedb.remove({sid: arg.sid}, { multi: true }, function(err, numRemoved) {
nedb.persistence.compactDatafile();
})
return;
}
if (arg.command === 'version') {
event.reply('asynchronous-reply', {ver: VERSION, sid: arg.sid});
return
}
if (arg.command === 'clearAll') {
/*caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
return caches.delete(cacheName);
})
);
}).then(function() {
event.reply('asynchronous-reply', {
data:{error: null, status: 'ckeanAll', key: arg.key}
});
}).catch(err => {
event.reply('asynchronous-reply', {
data: {error: err, status: 'ckeanAll', key: arg.key}
});
})*/
return;
}
if (arg.command === 'listCachedCoursewares') {
console.log('not implement yet')
return
}
// var key = 'item-cache-' + event.data.sid + '-'+ event.data.ver;
var key = arg.key;
var p = caches.open(key).then(async (cache) => {
// throw {name: 'QuotaExceededError'}
switch (arg.command) {
// This command returns a list of the URLs corresponding to the Request objects
// that serve as keys for the current cache.
case 'keys':
console.log('not implement yet')
return
/*return cache.keys().then(function(requests) {
var urls = requests.map(function(request) {
return request.url;
});
return urls.sort();
}).then(function(urls) {
event.sender.send('asynchronous-reply', {
error: null,
urls: urls
});
});*/
break
// This command adds a new request/response pair to the cache.
case 'add':
// console.log('ipc main add', arg);
// If event.data.url isn't a valid URL, new Request() will throw a TypeError which will be handled
// by the outer .catch().
// Hardcode {mode: 'no-cors} since the default for new Requests constructed from strings is to require
// CORS, and we don't have any way of knowing whether an arbitrary URL that a user entered supports CORS.
//var request = new Request(arg.url, {mode: 'cors', cache: 'reload'}); // {mode: 'no-cors'}
// console.log('start', event.data.url);
// return fetch(request)
// .then(function(res){
// return res.arrayBuffer();
// })
CoursewareResourceManager.progressDownloader(event, arg)
/*.then(function(ab) {
var cl = ab.size ? ab.size : ab.byteLength;
return cache.put(arg.url, new Response(ab, {
// url: event.data.url,
// type: 'cors',
status: 200,
statusText: 'OK',
headers: [
['Content-Length', cl+''],
]
}));
})*/
.then(function(len, time) {
//const ps = arg.key.split('-')
const key = SparkMD5.hash(arg.key);
const url = SparkMD5.hash(arg.url)
nedb_res.remove({url: url, sid: arg.sid}, { multi: true }, function(err, numRemoved) {
const new_doc ={
key: arg.key,
url: url,
sid: arg.sid
}
nedb_res.persistence.compactDatafile();
nedb_res.insert(new_doc, (err) => {
//console.log('add resorce', err)
});
})
event.reply('asynchronous-reply', {
error: null,
...arg,
status: 'finished',
len, time
});
}).catch( err => {
console.log('progressDownloader error', err);
const data = {error: true, ...err, ...arg,};
event.reply('asynchronous-reply', data);
});
break
// This command removes a request/response pair from the cache (assuming it exists).
case 'delete':
return cache.delete(arg.url).then(function(success) {
event.reply('asynchronous-reply', {
error: success ? null : 'Item was not found in the cache.'
});
});
break
default:
// This will be handled by the outer .catch().
throw Error('Unknown command: ' + arg.command);
}
}).catch(function(error) {
// If the promise rejects, handle it by returning a standardized error message to the controlled page.
console.log('Message handling failed:', arg.url, error);
// if (error.name == 'QuotaExceededError') {
event.reply('asynchronous-reply', {
//error: {name: 'QuotaExceededError'}
...arg,
error: {name: error.name, message: error.message},
status: 'error'
});
// }
});
}
}
const rm = new CoursewareResourceManager();
const { PassThrough } = require('stream')
function createStream (text) {
const rv = new PassThrough() // PassThrough is also a Readable stream
rv.push(text)
rv.push(null)
return rv
}
/*protocol.registerStreamProtocol('atom', (request, callback) => {
callback({
statusCode: 200,
headers: {
'content-type': 'text/html'
},
data: createStream('<h5>Response</h5>')
})
}, (error) => {
if (error) console.error('Failed to register protocol')
})*/
// 保持对window对象的全局引用,如果不这么做的话,当JavaScript对象被
// 垃圾回收的时候,window对象将会自动的关闭
const UA = 'CoursewareBox/1.0'
/*session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
details.requestHeaders['User-Agent'] = 'MyAgent'
callback({ requestHeaders: details.requestHeaders })
})*/
//protocol.registerSchemesAsPrivileged([{ scheme: 'atom', privileges: { standard: true } }])
/*
protocol.registerHttpProtocol('http', (request, callback) => {
console.log('registerHttpProtocol', request.url);
callback({'url': request.url, 'method': request.method, 'session': mainSession})
});*/
function createWindow () {
console.log('run createWindow ')
const mainSession = session.fromPartition('main');
const slaveSession = session.fromPartition('slave')
/*隐藏electron创听的菜单栏*/
Menu.setApplicationMenu(null);
/*protocol.interceptHttpProtocol(
'https',
(request, callback) => {
console.log(1, request.url)
if (request.url.endsWith('png')) {
console.log(2,request.url)
}
callback({
url: request.url,
method: request.method,
session: null,
})
},
error => {
if (error) console.error(error, 'Failed to intercept protocol')
}
)*/
/*slaveSession.protocol.interceptHttpProtocol('http', (request, callback) => {
console.log('slaveSession', request.url);
callback({'url': request.url, 'method': request.method, 'session': mainSession})
}, function(error){
if (error) console.log("slaveSession interceptHttpProtocol error: " + error)
});*/
/*
mainSession.protocol.interceptHttpProtocol('http', (request, callback) => {
console.log('mainSession', request.url);
callback({'url': request.url, 'method': request.method})
return
if(request.url.indexOf('/api/') >= 0){
request.url = "custom:" + request.url.split(':')[1]
callback(request)
} else {
console.log('pass ' + request.url)
callback({'url': request.url, 'method': request.method, 'session': null})
}
}, function(error){
if (error) console.log("mainSession interceptHttpProtocol error: " + error)
})*/
// 创建浏览器窗口。
win = new BrowserWindow({
//fullscreen: true,
show: false,
width: 1400,
height: 600,
//show: false,
webPreferences: {
nodeIntegration: true,
webSecurity: false,
session: mainSession,
}
});
win.maximize()
win.show()
/*protocol.registerFileProtocol('atom', (request, callback) => {
console.log(request.url);
console.log('file ===>', request, callback);
// const url = request.url.substr(7)
// callback({ path: path.normalize(`${__dirname}/${url}`) })
// callback({ mimeType: 'text/html', data: Buffer.from('<h5>Response</h5>') })
//callback({path: path.normalize(__dirname + '/' + url)});
}, (error) => {
if (error) console.error(error, 'Failed to register protocol')
}); */
//win.webContents.getUserAgent()
//win.webContents.setUserAgent(UA);
// 加载index.html文件
// win.loadFile('index.html')
//win.loadURL('https://imman.ireadabc.com')
win.loadURL(address)
console.log('win load ' + address)
// 打开开发者工具
win.webContents.once('dom-ready', () => {
console.log('win on dom-ready', devTool)
if (devTool) {
win.webContents.openDevTools();
}
if (HASUPDATER) {
fetch(VERSION_CHECK_URL)
.then(res => res.text())
.then(body => {
const conf = JSON.parse(body);
NEW_VERSION = +conf.version;
if (VERSION < NEW_VERSION) {
const new_asar = path.join(basepath, NEW_VERSION + '.asar');
if (fs.existsSync(new_asar)) {
console.log('new version file exist');
return;
}
fetch(conf.url)
.then(res => {
const app_tmp = path.join(basepath, conf.version + '.tmp');
const app_asar = path.join(basepath, conf.version + '.asar');
const output = fs.createWriteStream(app_tmp);
//res.body.on('data', (chunk) => {
//console.log(1, chunk)
//})
res.body.on("error", (err) => {
console.log('', err)
});
output.on("finish", () => {
fs.renameSync(app_tmp, app_asar)
fs.unlinkSync(app_tmp)
});
res.body.pipe(output);
})
}
}).catch(err => {
});
}
})
//win.on('ready-to-show', function () {
// win.show() // 初始化后再显示
//})
// 当 window 被关闭,这个事件会被触发。
win.on('closed', () => {
console.log('win on closed')
const old_asar = path.join(basepath, 'app.asar');
const new_asar = path.join(basepath, NEW_VERSION + '.asar');
if (fs.existsSync(new_asar)) {
mvOrMove(new_asar, old_asar);
}
app.quit()
console.log('app exit')
// 取消引用 window 对象,如果你的应用支持多窗口的话,
// 通常会把多个 window 对象存放在一个数组里面,
// 与此同时,你应该删除相应的元素。
win = null
})
//const ses = win.webContents.session
console.log(win.webContents.session.getUserAgent())
mainSession.webRequest.onBeforeSendHeaders((details, callback) => {
details.requestHeaders['User-Agent'] = `${win.webContents.session.getUserAgent()} ${UA}`;
callback({ cancel: false, requestHeaders: details.requestHeaders });
});
mainSession.webRequest.onBeforeRequest( (details, callback) => {
//console.log(details)
if (details.url.indexOf('iplayabc-courseware.oss-cn-beijing.aliyuncs.com/speed.png') > -1) {
callback({cancel: false})
return;
}
//if(details.url.endsWith('.html')) {
//}
let url = details.url;
const qp = url.indexOf('?');
if (url.indexOf('?') > -1) {
url = url.slice(0, qp)
}
nedb_res.find({url: SparkMD5.hash(url)}).limit(1).exec(function (err, docs) {
// console.log(docs)
if (!docs.length) {
callback({cancel: false})
return
}
doc = docs[0]
const target_path = path.join(appCachePath, SparkMD5.hash(doc.sid), SparkMD5.hash(url));
if (fs.existsSync(target_path)) {
callback({cancel: false, redirectURL: target_path})
} else {
callback({cancel: false})
}
});
});
}
// Electron 会在初始化后并准备
// 创建浏览器窗口时,调用这个函数。
// 部分 API 在 ready 事件触发后才能使用。
// app.on('ready', createWindow)
// 当全部窗口关闭时退出。
app.on('window-all-closed', () => {
console.log('app on window-all-closed')
// 在 macOS 上,除非用户用 Cmd + Q 确定地退出,
// 否则绝大部分应用及其菜单栏会保持激活。
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
console.log('app on activate')
// 在macOS上,当单击dock图标并且没有其他窗口打开时,
// 通常在应用程序中重新创建一个窗口。
if (win === null) {
createWindow()
}
})
\ No newline at end of file
{
"name": "courseware-box",
"version": "1.0.0",
"author": "iplayabc.com",
"main": "main.js",
"description": "",
"scripts": {
"exe3": "electron-packager . HelloWorld --win --out ../HelloWorldApp --arch=x64 --version=1.0.0",
"pack1": "electron-packager . myClient --win --out ../myClient --arch=x64 --app-version=0.0.1",
"exe2": "electron-packager . --overwrite --platform=win32 --arch=ia32 --out=out",
"package:win": "electron-packager . --overwrite --platform=win32 --arch=ia32 --out=out --icon=app.ico",
"app": "electron-packager . --platform=win32 --arch:dir=ia32 --asar --overwrite",
"packer": "electron-packager . electron-tutorial-app --overwrite --asar=true --platform=win32 --arch=ia32 --icon=assets/icons/win/icon.ico --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=Electron Tutorial App",
"build:dir": "electron-builder --dir",
"build": "electron-builder .",
"dist": "electron-builder",
"packager": "electron-packager . CoursewareBox --out ./dist --appVersion 1.0.0 --overwrite",
"dist-win": "node_modules\\.bin\\electron-builder build --ia32 -w",
"exe": "electron-packager . CoursewareBox --platform=win32 --arch=ia32 --overwrite",
"start": "electron . ",
"package-app": "build-electron-app && electron-builder",
"build-win": "electron-packager . --asar --overwrite --platform=win32 --arch=ia32 --output=releases --icon=app.ico --version-string.CompanyName=iplayabc --version-string.FileDescription=\"1.0.0\" --version-string.ProductName=\"courseware-box\"",
"build-mac": "electron-packager . --asar --overwrite --platform=darwin --arch=x64 --prune=true --output=releases --icon=app.ico",
"build2": "electron-packager . --ignore=node_modules --asar --overwrite --platform=win32 --arch=ia32 --output=releases --icon=app.ico",
"debug": "electron . --inspect-brk=5858 --debug-address=http://192.168.1.8:4200 --dev-tool --multi"
},
"build": {
"appId": "com.ireading.imman",
"copyright": "iplayabc",
"productName": "CoursewareBox",
"win": {
"icon": "app.ico",
"target": [
{
"target": "portable",
"arch": [
"ia32"
]
}
]
}
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"perMachine": true
},
"license": "ISC",
"dependencies": {
"nedb": "^1.8.0",
"node-fetch": "^2.6.0",
"request": "^2.88.0",
"rimraf": "^2.6.3",
"unzipper": "^0.10.1"
},
"devDependencies": {
"check-for-leaks": "^1.2.1",
"electron": "^5.0.5",
"electron-builder": "^20.44.4",
"electron-packager": "^12.1.0",
"signcode": "^1.0.0"
}
}
File added
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment