38 Star 463 Fork 256

CcSimple/electron-hiprint

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
utils.js 19.37 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
const os = require("os");
const { app, Notification } = require("electron");
const address = require("address");
const ipp = require("ipp");
const { machineIdSync } = require("node-machine-id");
const Store = require("electron-store");
const { getPaperSizeInfo, getPaperSizeInfoAll } = require("win32-pdf-printer");
const { v7: uuidv7 } = require("uuid");
const log = require("./log");
Store.initRenderer();
const schema = {
mainTitle: {
type: "string",
default: "Electron-hiprint",
},
nickName: {
type: "string",
default: "",
},
openAtLogin: {
type: "boolean",
default: true,
},
openAsHidden: {
type: "boolean",
default: true,
},
connectTransit: {
type: "boolean",
default: false,
},
transitUrl: {
type: "string",
default: "",
},
transitToken: {
type: "string",
default: "",
},
allowNotify: {
type: "boolean",
default: true,
},
closeType: {
type: "string",
enum: ["tray", "quit"],
default: "tray",
},
port: {
type: "number",
minimum: 10000,
default: 17521,
},
token: {
type: "string",
default: "",
},
pluginVersion: {
type: "string",
default: "0.0.60",
},
logPath: {
type: "string",
default: app.getPath("logs"),
},
pdfPath: {
type: "string",
default: app.getPath("temp"),
},
defaultPrinter: {
type: "string",
default: "",
},
disabledGpu: {
type: "boolean",
default: false,
},
rePrint: {
type: "boolean",
default: true,
},
};
const store = new Store({ schema });
/**
* @description: 获取当前系统 IP 地址
* @return {String}
*/
function addressIp() {
return address.ip();
}
/**
* @description: 获取当前系统 IPV6 地址
* @return {String}
*/
function addressIpv6() {
return address.ipv6();
}
/**
* @description: 获取当前系统 MAC 地址
* @return {String}
*/
function addressMac() {
return new Promise((resolve) => {
address.mac(function(err, addr) {
if (err) {
resolve(err);
} else {
resolve(addr);
}
});
});
}
/**
* @description: 获取当前系统 IP、IPV6、MAC 地址
* @return {Object}
*/
function addressAll() {
return new Promise((resolve) => {
address.mac(function(err, mac) {
if (err) {
resolve({ ip: address.ip(), ipv6: address.ipv6(), mac: err });
} else {
resolve({ ip: address.ip(), ipv6: address.ipv6(), mac });
}
});
});
}
/**
* @description: address 方法重写
* @return {Object}
*/
const _address = {
ip: addressIp,
ipv6: addressIpv6,
mac: addressMac,
all: addressAll,
};
/**
* @description: 检查分片任务实例,用于自动删除超时分片信息
*/
const watchTaskInstance = generateWatchTask(
() => global.PRINT_FRAGMENTS_MAPPING,
)();
/**
* @description: 抛出当前客户端信息,提供更多有价值的信息,逐步替换原有 address
* @param {io.Socket} socket
* @return {void}
*/
function emitClientInfo(socket) {
_address.mac().then((mac) => {
socket.emit("clientInfo", {
hostname: os.hostname(), // 主机名
version: app.getVersion(), // 版本号
platform: process.platform, // 平台
arch: process.arch, // 系统架构
mac: mac, // mac 地址
ip: _address.ip(), // ip 地址
ipv6: _address.ipv6(), // ipv6 地址
clientUrl: `http://${_address.ip()}:${store.get("port") || 17521}`, // 客户端地址
machineId: machineIdSync({ original: true }), // 客户端唯一id
nickName: store.get("nickName"), // 客户端昵称
});
});
}
/**
* 生成检查分片任务的闭包函数
* @param {Object} getCheckTarget 获取校验对象,最后会得到global.PRINT_FRAGMENTS_MAPPING
* @returns {Function}
*/
function generateWatchTask(getCheckTarget) {
// 记录当前检查任务是否开启,避免重复开启任务
let isWatching = false;
/**
* @description: 检查分片任务实例创建函数
* @param {Object} config 检查参数,根据实际情况调整
* @param {number} [config.checkInterval=5] 执行内存检查的时间间隔,单位分钟
* @param {number} [config.expire=10] 分片信息过期时间,单位分钟,不应过小
*/
return function generateWatchTaskInstance(config = {}) {
// 合并用户和默认配置
const realConfig = Object.assign(
{
checkInterval: 5, // 默认检查间隔
expire: 10, // 默认过期时间
},
config,
);
return {
startWatch() {
if (isWatching) return;
this.createWatchTimeout();
},
createWatchTimeout() {
// 更新开关状态
isWatching = true;
return setTimeout(
this.clearFragmentsWhichIsExpired.bind(this),
realConfig.checkInterval * 60 * 1000,
);
},
clearFragmentsWhichIsExpired() {
const checkTarget = getCheckTarget();
const currentTimeStamp = Date.now();
Object.entries(checkTarget).map(([id, fragmentInfo]) => {
// 获取任务最后更新时间
const { updateTime } = fragmentInfo;
// 任务过期时,清除任务信息释放内存
if (currentTimeStamp - updateTime > realConfig.expire * 60 * 1000) {
delete checkTarget[id];
}
});
// 获取剩余任务数量
const printTaskCount = Object.keys(checkTarget).length;
// 还有打印任务,继续创建检查任务
if (printTaskCount) this.createWatchTimeout();
// 更新开关状态
else isWatching = false;
},
};
};
}
/**
* @description: 作为本地服务端时绑定的 socket 事件
* @param {*} server
* @return {void}
*/
function initServeEvent(server) {
// 必须传入实体
if (!server) return false;
/**
* @description: 校验 token
*/
server.use((socket, next) => {
const token = store.get("token");
if (token && token !== socket.handshake.auth.token) {
log(
`==> 插件端 Authentication error: ${socket.id}, token: ${socket.handshake.auth.token}`,
);
const err = new Error("Authentication error");
err.data = {
content: "Token 错误",
};
next(err);
} else {
next();
}
});
/**
* @description: 新的 web client 连入,绑定 socket 事件
*/
server.on("connect", async (socket) => {
log(`==> 插件端 New Connected: ${socket.id}`);
// 通知渲染进程已连接
MAIN_WINDOW.webContents.send(
"serverConnection",
server.engine.clientsCount,
);
// 判断是否允许通知
if (store.get("allowNotify")) {
// 弹出连接成功通知
const notification = new Notification({
title: "新的连接",
body: `已建立新的连接,当前连接数:${server.engine.clientsCount}`,
});
// 显示通知
notification.show();
}
// 向 client 发送打印机列表
socket.emit(
"printerList",
await MAIN_WINDOW.webContents.getPrintersAsync(),
);
// 向 client 发送客户端信息
emitClientInfo(socket);
/**
* @description: client 请求客户端信息
*/
socket.on("getClientInfo", () => {
log(`插件端 ${socket.id}: getClientInfo`);
emitClientInfo(socket);
});
/**
* @description: client请求 address ,获取本机 IP、IPV6、MAC 地址
* @description: addressType 为 null 时,返回所有地址
* @description: 逐步废弃该 api
* @param {String} addressType ip、ipv6、mac、all === null
*/
socket.on("address", (addressType) => {
log(`插件端 ${socket.id}: get address(${addressType || "未指定类型"})`);
switch (addressType) {
case "ip":
case "ipv6":
socket.emit("address", addressType, _address[addressType]());
break;
case "dns":
case "interface":
case "vboxnet":
// 用处不大的几个信息,直接废弃
socket.emit("address", addressType, null, "This type is removed.");
break;
default:
addressType = addressType === "mac" ? "mac" : "all";
_address[addressType]().then((res) => {
socket.emit("address", addressType, res);
});
break;
}
});
/**
* @description: client 请求刷新打印机列表
*/
socket.on("refreshPrinterList", async () => {
log(`插件端 ${socket.id}: refreshPrinterList`);
socket.emit(
"printerList",
await MAIN_WINDOW.webContents.getPrintersAsync(),
);
});
/**
* @description: client 获取打印机纸张信息
*/
socket.on("getPaperSizeInfo", (printer) => {
log(`插件端 ${socket.id}: getPaperSizeInfo`);
if (process.platform === "win32") {
let fun = printer ? getPaperSizeInfo : getPaperSizeInfoAll;
let paper = fun();
paper && socket.emit("paperSizeInfo", paper);
}
});
/**
* @description: client 调用 ipp 打印 详见:https://www.npmjs.com/package/ipp
*/
socket.on("ippPrint", (options) => {
log(`插件端 ${socket.id}: ippPrint`);
try {
const { url, opt, action, message } = options;
let printer = ipp.Printer(url, opt);
socket.emit("ippPrinterConnected", printer);
let msg = Object.assign(
{
"operation-attributes-tag": {
"requesting-user-name": "hiPrint",
},
},
message,
);
// data 必须是 Buffer 类型
if (msg.data && !Buffer.isBuffer(msg.data)) {
if ("string" === typeof msg.data) {
msg.data = Buffer.from(msg.data, msg.encoding || "utf8");
} else {
msg.data = Buffer.from(msg.data);
}
}
/**
* action: Get-Printer-Attributes 获取打印机支持参数
* action: Print-Job 新建打印任务
* action: Cancel-Job 取消打印任务
*/
printer.execute(action, msg, (err, res) => {
socket.emit(
"ippPrinterCallback",
err ? { type: err.name, msg: err.message } : null,
res,
);
});
} catch (error) {
log(`插件端 ${socket.id}: ippPrint error: ${error.message}`);
socket.emit("ippPrinterCallback", {
type: error.name,
msg: error.message,
});
}
});
/**
* @description: client ipp request 详见:https://www.npmjs.com/package/ipp
*/
socket.on("ippRequest", (options) => {
log(`插件端 ${socket.id}: ippRequest`);
try {
const { url, data } = options;
let _data = ipp.serialize(data);
ipp.request(url, _data, (err, res) => {
socket.emit(
"ippRequestCallback",
err ? { type: err.name, msg: err.message } : null,
res,
);
});
} catch (error) {
log(`插件端 ${socket.id}: ippRequest error: ${error.message}`);
socket.emit("ippRequestCallback", {
type: error.name,
msg: error.message,
});
}
});
/**
* @description: client 常规打印任务
*/
socket.on("news", (data) => {
if (data) {
PRINT_RUNNER.add((done) => {
data.socketId = socket.id;
data.taskId = uuidv7();
data.clientType = "local";
PRINT_WINDOW.webContents.send("print-new", data);
MAIN_WINDOW.webContents.send("printTask", true);
PRINT_RUNNER_DONE[data.taskId] = done;
});
}
});
/**
* @description: client 分批打印任务
*/
socket.on("printByFragments", (data) => {
if (data) {
const { total, index, htmlFragment, id } = data;
const currentInfo =
PRINT_FRAGMENTS_MAPPING[id] ||
(PRINT_FRAGMENTS_MAPPING[id] = {
total,
fragments: [],
count: 0,
updateTime: 0,
});
// 添加片段信息
currentInfo.fragments[index] = htmlFragment;
// 计数
currentInfo.count++;
// 记录更新时间
currentInfo.updateTime = Date.now();
// 全部片段已传输完毕
if (currentInfo.count === currentInfo.total) {
// 清除全局缓存
delete PRINT_FRAGMENTS_MAPPING[id];
// 合并全部打印片段信息
data.html = currentInfo.fragments.join("");
// 添加打印任务
PRINT_RUNNER.add((done) => {
data.socketId = socket.id;
data.taskId = uuidv7();
data.clientType = "local";
PRINT_WINDOW.webContents.send("print-new", data);
MAIN_WINDOW.webContents.send("printTask", true);
PRINT_RUNNER_DONE[data.taskId] = done;
});
}
// 开始检查任务
watchTaskInstance.startWatch();
}
});
socket.on("render-print", (data) => {
if (data) {
RENDER_RUNNER.add((done) => {
data.socketId = socket.id;
data.taskId = uuidv7();
data.clientType = "local";
RENDER_WINDOW.webContents.send("print", data);
RENDER_RUNNER_DONE[data.taskId] = done;
});
}
});
socket.on("render-jpeg", (data) => {
if (data) {
RENDER_RUNNER.add((done) => {
data.socketId = socket.id;
data.taskId = uuidv7();
data.clientType = "local";
RENDER_WINDOW.webContents.send("png", data);
RENDER_RUNNER_DONE[data.taskId] = done;
});
}
});
socket.on("render-pdf", (data) => {
if (data) {
RENDER_RUNNER.add((done) => {
data.socketId = socket.id;
data.taskId = uuidv7();
data.clientType = "local";
RENDER_WINDOW.webContents.send("pdf", data);
RENDER_RUNNER_DONE[data.taskId] = done;
});
}
});
/**
* @description: client 断开连接
*/
socket.on("disconnect", () => {
log(`==> 插件端 Disconnect: ${socket.id}`);
MAIN_WINDOW?.webContents?.send(
"serverConnection",
server.engine.clientsCount,
);
});
});
}
/**
* @description: 作为客户端连接中转服务时绑定的 socket 事件
* @return {void}
*/
function initClientEvent() {
// 作为客户端连接中转服务时只有一个全局 client
var client = global.SOCKET_CLIENT;
/**
* @description: 连接中转服务成功,绑定 socket 事件
*/
client.on("connect", async () => {
log(`==> 中转服务 Connected Transit Server: ${client.id}`);
// 通知渲染进程已连接
MAIN_WINDOW.webContents.send("clientConnection", true);
// 判断是否允许通知
if (store.get("allowNotify")) {
// 弹出连接成功通知
const notification = new Notification({
title: "已连接中转服务器",
body: `已连接至中转服务器【${store.get("transitUrl")}】,即刻开印!`,
});
// 显示通知
notification.show();
}
// 向 中转服务 发送打印机列表
client.emit(
"printerList",
await MAIN_WINDOW.webContents.getPrintersAsync(),
);
// 向 中转服务 发送客户端信息
emitClientInfo(client);
});
/**
* @description: 中转服务 请求客户端信息
*/
client.on("getClientInfo", () => {
log(`中转服务 ${client.id}: getClientInfo`);
emitClientInfo(client);
});
/**
* @description: 中转服务 请求刷新打印机列表
*/
client.on("refreshPrinterList", async () => {
log(`中转服务 ${client.id}: refreshPrinterList`);
client.emit(
"printerList",
await MAIN_WINDOW.webContents.getPrintersAsync(),
);
});
/**
* @description: 中转服务 调用 ipp 打印 详见:https://www.npmjs.com/package/ipp
*/
client.on("ippPrint", (options) => {
log(`中转服务 ${client.id}: ippPrint`);
try {
const { url, opt, action, message, replyId } = options;
let printer = ipp.Printer(url, opt);
client.emit("ippPrinterConnected", { printer, replyId });
let msg = Object.assign(
{
"operation-attributes-tag": {
"requesting-user-name": "hiPrint",
},
},
message,
);
// data 必须是 Buffer 类型
if (msg.data && !Buffer.isBuffer(msg.data)) {
if ("string" === typeof msg.data) {
msg.data = Buffer.from(msg.data, msg.encoding || "utf8");
} else {
msg.data = Buffer.from(msg.data);
}
}
/**
* action: Get-Printer-Attributes 获取打印机支持参数
* action: Print-Job 新建打印任务
* action: Cancel-Job 取消打印任务
*/
printer.execute(action, msg, (err, res) => {
client.emit(
"ippPrinterCallback",
err ? { type: err.name, msg: err.message, replyId } : { replyId },
res,
);
});
} catch (error) {
log(`中转服务 ${client.id}: ippPrint error: ${error.message}`);
client.emit("ippPrinterCallback", {
type: error.name,
msg: error.message,
replyId,
});
}
});
/**
* @description: 中转服务 ipp request 详见:https://www.npmjs.com/package/ipp
*/
client.on("ippRequest", (options) => {
log(`中转服务 ${client.id}: ippRequest`);
try {
const { url, data, replyId } = options;
let _data = ipp.serialize(data);
ipp.request(url, _data, (err, res) => {
client.emit(
"ippRequestCallback",
err ? { type: err.name, msg: err.message, replyId } : { replyId },
res,
);
});
} catch (error) {
log(`中转服务 ${client.id}: ippRequest error: ${error.message}`);
client.emit("ippRequestCallback", {
type: error.name,
msg: error.message,
replyId,
});
}
});
/**
* @description: 中转服务 常规打印任务
*/
client.on("news", (data) => {
if (data) {
PRINT_RUNNER.add((done) => {
data.socketId = client.id;
data.taskId = uuidv7();
data.clientType = "transit";
PRINT_WINDOW.webContents.send("print-new", data);
MAIN_WINDOW.webContents.send("printTask", true);
PRINT_RUNNER_DONE[data.taskId] = done;
});
}
});
client.on("render-print", (data) => {
if (data) {
RENDER_RUNNER.add((done) => {
data.socketId = client.id;
data.taskId = uuidv7();
data.clientType = "transit";
RENDER_WINDOW.webContents.send("print", data);
RENDER_RUNNER_DONE[data.taskId] = done;
});
}
});
client.on("render-jpeg", (data) => {
if (data) {
RENDER_RUNNER.add((done) => {
data.socketId = client.id;
data.taskId = uuidv7();
data.clientType = "transit";
RENDER_WINDOW.webContents.send("png", data);
RENDER_RUNNER_DONE[data.taskId] = done;
});
}
});
client.on("render-pdf", (data) => {
if (data) {
RENDER_RUNNER.add((done) => {
data.socketId = client.id;
data.taskId = uuidv7();
data.clientType = "transit";
RENDER_WINDOW.webContents.send("pdf", data);
RENDER_RUNNER_DONE[data.taskId] = done;
});
}
});
/**
* @description: 中转服务 断开连接
*/
client.on("disconnect", () => {
log(`==> 中转服务 Disconnect: ${client.id}`);
MAIN_WINDOW.webContents.send("clientConnection", false);
});
}
module.exports = {
store,
address: _address,
initServeEvent,
initClientEvent,
};
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/CcSimple/electron-hiprint.git
git@gitee.com:CcSimple/electron-hiprint.git
CcSimple
electron-hiprint
electron-hiprint
master

搜索帮助