這篇文章主要介紹
-
asar
包的簡單保護 - 支持快捷鍵的使用
- 菜單的動態修改
- 日志功能
1. asar
的包的使用
從上篇我們知道, asar
包可以用7z的插件或者直接使用asar命令解壓, 但是有時候我們不像讓人解壓直接看到我們的代碼邏輯, 可以使用一個庫來修改, 即asarmo
我也有幸貢獻了代碼, 將它的
write
方法修改成返回Promise對象, 方便進行同步操作, 比如打增量包feat: make asarmor.write() return the Promise by klren0312 · Pull Request #10 · sleeyax/asarmor (github.com)
const { Asarmor, Trashify, FileCrash } = require('asarmor')
const { join } = require('path')
const AdmZip = require('adm-zip')
const pkg = require('./package.json')
exports.default = async ({ appOutDir, packager, outDir }) => {
try {
const asarPath = join(packager.getResourcesDir(appOutDir), 'app.asar')
console.log(`applying asarmor protections to ${asarPath}`)
const asarmor = new Asarmor(asarPath)
asarmor.applyProtection(new FileCrash('background.js.LICENSE.txt'))
asarmor.applyProtection(new Trashify(['.git', '.env']))
await asarmor.write(asarPath)
const targetPath = join(appOutDir, './resources')
const zip = new AdmZip()
zip.addLocalFolder(targetPath)
const partUpdateFile = `update-win-${pkg.version}.zip`
zip.writeZip(join(outDir, partUpdateFile))
} catch (err) {
console.error(err)
}
}
asarmo庫有以下幾個功能(使用7z插件進行解壓, 雖然都會報錯, 但是只有第一種時無法將文件解壓出來, 其他其實都已經解壓出來了)
- 對壓縮包中的指定文件進行損壞(一定是不會被調用的文件, 不然會使
electron
也無法訪問, 導致無法運行)
image.png
-
生成大量隨機文件填充壓縮包, 解壓的時候阻塞解壓(可以指定文件的體積, 例如10G, 則解壓時會進行10G文件解壓), 但是這樣似乎不會導致文件無法解壓, 取消解壓后, 其實文件已經解壓出來了
image.png
image.png
image.png -
往壓縮包里添加不存在文件
image.png
image.png
image.png
asarmo實現這些的主要原理就是通過chromium-pickle來對asar
打包和解包的工具, 對包的header信息進行修改, 從而使解壓出現錯誤
-
上面的第一種方法, 我們可以看到, 我們指定的文件size修改成了負值
image.png -
第二種方法, 我們可以看到, header信息里被添加了很多隨機文件
image.png -
第三種方法, 我們可以看到, 添加了我們指定的不存在的文件
image.png
2. 支持快捷鍵的使用
electron
官方已經支持了快捷鍵的使用, 可以訪問globalShortcut
了解
我們可以在窗口focus
的時候注冊快捷鍵, 然后在blur
的時候注銷快捷鍵
// 窗口聚焦
win.on('focus', () => {
globalShortcut.register('Alt+A', () => {
// 相關邏輯
})
})
// 窗口失焦
win.on('blur', () => {
globalShortcut.unregister('Alt+A')
})
3. 菜單的動態修改
獲取const menuInstance = Menu.buildFromTemplate(this.template)
創建的菜單實例
通過menuInstance.items
獲取菜單數組, 來修改相關菜單
4. 日志功能
使用winston來實現日志功能,
通過winston-daily-rotate-file: A transport for winston which logs to a rotating file each day. (github.com)對日志進行限制, 定期清除
封裝日志組件
const { transports, createLogger, format } = require('winston')
const { combine, timestamp, printf } = format
require('winston-daily-rotate-file')
const path = require('path')
const dateFormat = date => {
const addZero = num => {
if (num < 10) {
return '0' + num
} else {
return num
}
}
const year = date.getFullYear()
const month = addZero(date.getMonth() + 1)
const day = addZero(date.getDate())
const hour = addZero(date.getHours())
const minute = addZero(date.getMinutes())
const second = addZero(date.getSeconds())
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
}
const myFormat = printf(({ level, message, timestamp }) => {
return `${dateFormat(new Date(timestamp))} - ${level}: ${message}`
})
const app =
process.type === 'browser'
? require('electron').app
: require('electron').remote.app
const logDir = path.resolve(app.getPath('userData'), 'logs')
// const logLevel = process.env.NODE_ENV === 'production' ? 'info' : 'debug';
const logLevel = 'debug'
const levels = {
debug: 0,
info: 1,
warn: 2,
error: 3
}
const d = (level, message) => {
if (levels[level] >= levels[logLevel]) {
const consoleLevel = level === 'debug' ? 'log' : level
console[consoleLevel](message)
}
}
// App進程
function AppLogger() {
const appLogFileName = path.resolve(logDir, 'appLogs%DATE%.log')
const transport = new transports.DailyRotateFile({
level: logLevel,
filename: appLogFileName,
maxSize: '5m',
maxFiles: '15d,100m' // 15天以前的自動刪除,文件大小超過100m時將舊文件刪
})
this.logger = createLogger({
format: combine(timestamp(), myFormat),
transports: [transport]
})
return this
}
AppLogger.prototype.debug = function debug(message) {
d('debug', message)
return this.logger.debug(message)
}
AppLogger.prototype.info = function info(message) {
d('info', message)
return this.logger.info(message)
}
AppLogger.prototype.warn = function warn(message) {
d('warn', message)
return this.logger.warn(message)
}
AppLogger.prototype.error = function error(message) {
d('error', message)
return this.logger.error(message)
}
const appLogger = new AppLogger()
module.exports = {
appLogger
}
使用日志組件
import { appLogger } from './Logger'
appLogger.info(`------ 日志 ------`)