@flun/windows 3.0.10 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -14
- package/LICENSE +28 -12
- package/README.md +294 -174
- package/bin/cli.js +218 -0
- package/bin/winsw/winsw.exe.config +1 -1
- package/copy-files.js +34 -34
- package/index.d.ts +133 -129
- package/lib/binaries.js +133 -132
- package/lib/cmd.js +4 -4
- package/lib/daemon.js +82 -77
- package/lib/eventlog.js +13 -7
- package/lib/shared.js +5 -3
- package/lib/winsw.js +15 -13
- package/lib/wrapper.js +15 -15
- package/package.json +7 -3
- package/win-service.config.js +68 -0
- package/sevWin.js +0 -18
package/lib/eventlog.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { execPromise, execSync, isPermissionError } from './shared.js';
|
|
2
2
|
import { elevate } from './binaries.js';
|
|
3
3
|
|
|
4
|
-
const eventLogs = ['APPLICATION', 'SYSTEM', 'SECURITY'],
|
|
4
|
+
const eventLogs = ['APPLICATION', 'SYSTEM', 'SECURITY'],
|
|
5
|
+
validTypes = ['ERROR', 'WARNING', 'INFORMATION', 'SUCCESSAUDIT', 'FAILUREAUDIT'];
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* 事件日志记录器类,用于向Windows事件查看器写入日志
|
|
8
9
|
* - 提供记录信息、警告、错误和审计成功、审计失败事件的方法
|
|
9
|
-
* >查看定义:@see {@link EventLogger}、{@link info}、{@link warn}、{@link error}、{@link auditSuccess}、
|
|
10
|
+
* >查看定义:@see {@link EventLogger}、{@link info}、{@link warn}、{@link error}、{@link auditSuccess}、
|
|
11
|
+
* {@link auditFailure}
|
|
10
12
|
* @class EventLogger
|
|
11
13
|
* @example
|
|
12
14
|
* // 配置示例
|
|
@@ -28,9 +30,9 @@ const eventLogs = ['APPLICATION', 'SYSTEM', 'SECURITY'], validTypes = ['ERROR',
|
|
|
28
30
|
class EventLogger {
|
|
29
31
|
/**
|
|
30
32
|
* @constructor
|
|
31
|
-
* @param {string|Object} [config] -
|
|
33
|
+
* @param {string|Object} [config] - 配置字符串(作为 source)或配置对象
|
|
32
34
|
* @param {string} [config.source='Node.js'] - 事件源名称
|
|
33
|
-
* @param {string} [config.eventLog='APPLICATION'] -
|
|
35
|
+
* @param {string} [config.eventLog='APPLICATION'] - 事件日志名称(APPLICATION, SYSTEM, SECURITY)
|
|
34
36
|
*/
|
|
35
37
|
constructor(config = {}) {
|
|
36
38
|
if (typeof config === 'string') config = { source: config };
|
|
@@ -57,7 +59,7 @@ class EventLogger {
|
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
/**
|
|
60
|
-
*
|
|
62
|
+
* 获取当前事件日志名称(大写)
|
|
61
63
|
* @returns {string}
|
|
62
64
|
*/
|
|
63
65
|
get eventLog() {
|
|
@@ -124,10 +126,14 @@ class EventLogger {
|
|
|
124
126
|
// 判断是否需要使用PowerShell
|
|
125
127
|
if (this.#shouldUsePowerShell(vType, vId)) {
|
|
126
128
|
// 使用PowerShell的Write-EventLog命令
|
|
127
|
-
const entryTypeMap = {
|
|
129
|
+
const entryTypeMap = {
|
|
130
|
+
'ERROR': 'Error', 'WARNING': 'Warning',
|
|
131
|
+
'SUCCESSAUDIT': 'SuccessAudit', 'FAILUREAUDIT': 'FailureAudit'
|
|
132
|
+
},
|
|
128
133
|
entryType = entryTypeMap[vType] || 'Information', escapedMsg = pMsg.replace(/"/g, '""'),
|
|
129
134
|
powHad = 'powershell -Command "Write-EventLog -LogName';
|
|
130
|
-
command = `${powHad} '${vLog}' -Source '${vSrc}' -EventId ${vId}
|
|
135
|
+
command = `${powHad} '${vLog}' -Source '${vSrc}' -EventId ${vId}` +
|
|
136
|
+
` -EntryType ${entryType} -Message \\\"${escapedMsg}\\\""`;
|
|
131
137
|
} else {
|
|
132
138
|
const eventCreateId = Math.min(Math.max(1, vId), 1000); // 限制在1-1000范围内
|
|
133
139
|
command = `eventcreate /L ${vLog} /T ${vType} /SO "${vSrc}" /D "${pMsg}" /ID ${eventCreateId}`;
|
package/lib/shared.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 共享模块,导出在多个文件中使用的常用函数和模块
|
|
3
|
-
* >查看定义:@see {@link exec}、{@link execSync}、{@link fork}、{@link promisify}、{@link path}、
|
|
3
|
+
* >查看定义:@see {@link exec}、{@link execSync}、{@link fork}、{@link promisify}、{@link path}、
|
|
4
|
+
* {@link fs}、{@link EventEmitter}
|
|
4
5
|
*/
|
|
5
6
|
import { exec, execSync, fork } from 'child_process';
|
|
6
7
|
import { promisify } from 'util';
|
|
@@ -24,14 +25,15 @@ const execPromise = promisify(exec);
|
|
|
24
25
|
*/
|
|
25
26
|
const isPermissionError = error => {
|
|
26
27
|
const errorMessage = error?.message || error?.toString() || String(error),
|
|
27
|
-
permissionErrors = ['拒绝访问', 'Access is denied', '权限不足', 'Insufficient privileges',
|
|
28
|
+
permissionErrors = ['拒绝访问', 'Access is denied', '权限不足', 'Insufficient privileges',
|
|
29
|
+
'AccessDenied', '需要提升权限',
|
|
28
30
|
'requires elevation', 'Administrator', 'admin', '权限被拒绝'],
|
|
29
31
|
lowerMessage = errorMessage.toLowerCase();
|
|
30
32
|
return permissionErrors.some(keyword => lowerMessage.includes(keyword.toLowerCase()));
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
/**
|
|
34
|
-
*
|
|
36
|
+
* 获取当前模块所在目录的路径(类似 CommonJS 的 __dirname)
|
|
35
37
|
* >查看定义:@see {@link getDirname}
|
|
36
38
|
* @param {string} metaUrl - 传入 `import.meta.url`
|
|
37
39
|
* @returns {string} 当前模块的目录路径
|
package/lib/winsw.js
CHANGED
|
@@ -6,31 +6,31 @@ const __dirname = getDirname(import.meta.url);
|
|
|
6
6
|
/**
|
|
7
7
|
* @method generateXml 生成 winsw 配置文件的 XML;
|
|
8
8
|
* >查看定义:@see {@link generateXml}
|
|
9
|
-
* @param {Object} config -
|
|
9
|
+
* @param {Object} config - 配置对象,必须包含以下必需属性:
|
|
10
10
|
* - *id* 服务的标识符,只能包含字母和数字,不能有空格
|
|
11
11
|
* - *name* 服务的描述性名称
|
|
12
|
-
* - *execPath* Node.js
|
|
13
|
-
* - *script* Node.js
|
|
12
|
+
* - *execPath* Node.js 可执行文件的绝对路径(如:C:\Program Files\nodejs\node.exe)
|
|
13
|
+
* - *script* Node.js 服务器脚本的绝对路径(包装脚本)
|
|
14
14
|
* - *workingdirectory* 服务运行的工作目录
|
|
15
15
|
* @param {string} [config.description] - 在服务管理器中显示的描述信息
|
|
16
|
-
* @param {string|string[]} [config.nodeOptions] - Node.js
|
|
16
|
+
* @param {string|string[]} [config.nodeOptions] - Node.js 选项(数组或空格分隔的字符串)
|
|
17
17
|
* @param {string} [config.wrapperArgs] - 传递给包装脚本的额外参数
|
|
18
18
|
* @param {string} [config.logmode] - 日志模式(老式):'rotate'(默认), 'reset', 'roll', 'append'
|
|
19
19
|
* @param {Object} [config.logging] - 日志配置对象,可替代 logmode
|
|
20
20
|
* @param {string} [config.logging.mode] - 日志模式(现代),支持 'append', 'reset', 'none', 'roll-by-time', 'roll-by-size'
|
|
21
|
-
* @param {string} [config.logging.pattern] - roll-by-time
|
|
22
|
-
* @param {number} [config.logging.sizeThreshold] - roll-by-size
|
|
23
|
-
* @param {number} [config.logging.keepFiles] - roll-by-size
|
|
24
|
-
* @param {string} [config.logpath] -
|
|
21
|
+
* @param {string} [config.logging.pattern] - roll-by-time 模式的时间格式模式(默认:'yyyyMMdd')
|
|
22
|
+
* @param {number} [config.logging.sizeThreshold] - roll-by-size 模式的大小阈值(默认:10240)
|
|
23
|
+
* @param {number} [config.logging.keepFiles] - roll-by-size 模式保留的文件数(默认:8)
|
|
24
|
+
* @param {string} [config.logpath] - 日志存储目录的绝对路径(默认:当前目录)
|
|
25
25
|
* @param {string|string[]} [config.dependencies] - 进程依赖项的逗号分隔列表或数组
|
|
26
|
-
* @param {{ name: string, value: string }[]|Object<string, string>} [config.env] -
|
|
26
|
+
* @param {{ name: string, value: string }[]|Object<string, string>} [config.env] - 环境变量配置(键/值对象或对象数组)
|
|
27
27
|
* @param {Object} [config.logOnAs] - 服务登录凭据配置
|
|
28
28
|
* @param {string} [config.logOnAs.account] - 登录账户名
|
|
29
29
|
* @param {string} [config.logOnAs.password] - 登录密码
|
|
30
30
|
* @param {string} [config.logOnAs.domain] - 账户所在域
|
|
31
31
|
* @param {boolean} [config.allowServiceLogon] - 是否允许服务登录
|
|
32
32
|
* @param {boolean} [config.stopparentfirst] - 是否先停止父进程
|
|
33
|
-
* @param {number} [config.stoptimeout] -
|
|
33
|
+
* @param {number} [config.stoptimeout] - 停止超时时间(秒)
|
|
34
34
|
* @returns {string} 生成的 XML 字符串
|
|
35
35
|
* @throws {string} 当缺少必需配置时抛出错误
|
|
36
36
|
*/
|
|
@@ -41,7 +41,8 @@ const generateXml = config => {
|
|
|
41
41
|
} = config;
|
|
42
42
|
|
|
43
43
|
// 验证必需配置项是否存在
|
|
44
|
-
if (!id || !name || !script || !execPath || !workingdirectory)
|
|
44
|
+
if (!id || !name || !script || !execPath || !workingdirectory)
|
|
45
|
+
throw "id,name,script,execPath,workingdirectory为必需配置项";
|
|
45
46
|
|
|
46
47
|
let xml = [{ id }, { name }, { description }, { executable: execPath }]; // 基础 XML 结构
|
|
47
48
|
|
|
@@ -58,7 +59,8 @@ const generateXml = config => {
|
|
|
58
59
|
|
|
59
60
|
// 日志配置处理:优先使用 logging 对象,其次使用 logmode
|
|
60
61
|
if (logging) {
|
|
61
|
-
const { mode = 'append', pattern = 'yyyyMMdd', sizeThreshold = 10240, keepFiles = 8 } = logging,
|
|
62
|
+
const { mode = 'append', pattern = 'yyyyMMdd', sizeThreshold = 10240, keepFiles = 8 } = logging,
|
|
63
|
+
logContent = [{ _attr: { mode } }];
|
|
62
64
|
if (mode === 'roll-by-time') logContent.push({ pattern }); // 按时间滚动日志
|
|
63
65
|
else if (mode === 'roll-by-size') logContent.push({ sizeThreshold }), logContent.push({ keepFiles }); // 按大小滚动日志
|
|
64
66
|
xml.push({ log: logContent });
|
|
@@ -88,7 +90,7 @@ const generateXml = config => {
|
|
|
88
90
|
* @method createExe 创建 winsw 可执行文件及其配置文件
|
|
89
91
|
* >查看定义:@see {@link createExe}
|
|
90
92
|
* @param {string} name - 可执行文件名称(不含扩展名);
|
|
91
|
-
* @param {string} dir -
|
|
93
|
+
* @param {string} dir - 输出目录(默认:当前工作目录)
|
|
92
94
|
* @param {() => void} [callback] - 完成后的回调函数
|
|
93
95
|
* @description 将 winsw.exe 的安装版本复制为根据服务id重命名的特定版本,
|
|
94
96
|
* 并复制 .exe.config 文件以支持 .NET 4+ 运行时环境;
|
package/lib/wrapper.js
CHANGED
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
* 4. 重启次数限制和频率控制
|
|
9
9
|
* 5. 支持通过事件日志或控制台输出日志
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* -f, --file:
|
|
11
|
+
* 命令行参数:
|
|
12
|
+
* -f, --file: 要运行的脚本绝对路径(必需)
|
|
13
13
|
* -d, --cwd: 子进程工作目录
|
|
14
|
-
* -l, --log:
|
|
15
|
-
* -e, --eventlog:
|
|
16
|
-
* -m, --maxretries:
|
|
14
|
+
* -l, --log: 日志描述名称(必需)
|
|
15
|
+
* -e, --eventlog: 事件日志容器(APPLICATION或SYSTEM)
|
|
16
|
+
* -m, --maxretries: 最大重启次数(-1表示无限制)
|
|
17
17
|
* -r, --maxrestarts: 时间窗口内最大重启次数
|
|
18
|
-
* -w, --wait:
|
|
18
|
+
* -w, --wait: 重启等待时间(秒)
|
|
19
19
|
* -g, --grow: 等待时间增长系数
|
|
20
20
|
* -a, --abortonerror: 错误时是否终止
|
|
21
21
|
* -s, --stopparentfirst: 是否允许优雅退出
|
|
@@ -27,7 +27,7 @@ import yargs from 'yargs/yargs';
|
|
|
27
27
|
import { hideBin } from 'yargs/helpers';
|
|
28
28
|
import { EventLogger } from './eventlog.js';
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// 时间窗口(秒),默认时间窗口内最大重启次数,默认重启等待时间(秒),默认等待时间增长系数
|
|
31
31
|
const timeWindow = 60, defaultMaxRestarts = 3, defaultWaitTime = 1, defaultGrowth = 0.25,
|
|
32
32
|
booleanChoices = ['y', 'n', 'yes', 'no', 'true', 'false'],
|
|
33
33
|
/**
|
|
@@ -104,7 +104,7 @@ const timeWindow = 60, defaultMaxRestarts = 3, defaultWaitTime = 1, defaultGrowt
|
|
|
104
104
|
alias: 'm',
|
|
105
105
|
type: 'number',
|
|
106
106
|
default: -1,
|
|
107
|
-
description: '
|
|
107
|
+
description: '进程自动重启的最大次数(-1表示无限制)'
|
|
108
108
|
})
|
|
109
109
|
.option('maxrestarts', {
|
|
110
110
|
alias: 'r',
|
|
@@ -126,7 +126,7 @@ const timeWindow = 60, defaultMaxRestarts = 3, defaultWaitTime = 1, defaultGrowt
|
|
|
126
126
|
alias: 'g',
|
|
127
127
|
type: 'number',
|
|
128
128
|
default: defaultGrowth,
|
|
129
|
-
description: '
|
|
129
|
+
description: '等待时间增长的增长百分比(0-1之间)',
|
|
130
130
|
check: growth => {
|
|
131
131
|
if (growth < 0 || growth > 1) throw new Error('增长率必须在0到1之间');
|
|
132
132
|
return true;
|
|
@@ -150,7 +150,7 @@ const timeWindow = 60, defaultMaxRestarts = 3, defaultWaitTime = 1, defaultGrowt
|
|
|
150
150
|
})
|
|
151
151
|
.option('scriptoptions', {
|
|
152
152
|
type: 'string',
|
|
153
|
-
description: '
|
|
153
|
+
description: '要传递给脚本的选项(空格分隔)',
|
|
154
154
|
default: '',
|
|
155
155
|
coerce: parseQuotedOption
|
|
156
156
|
}).help().parse();
|
|
@@ -223,7 +223,7 @@ const prepareScriptArgs = () => {
|
|
|
223
223
|
|
|
224
224
|
// 检查总重启次数限制
|
|
225
225
|
if (argv.maxretries >= 0 && restartAttempts > argv.maxretries)
|
|
226
|
-
logger.error(
|
|
226
|
+
logger.error(`重启次数过多;${scriptPath} 将不会被重启,已超过最大重启次数 ${argv.maxretries}`), process.exit(1);
|
|
227
227
|
|
|
228
228
|
launchProcess('warn', `在意外退出后 ${waitTime} 毫秒重启: 尝试次数=${restartAttempts}`);
|
|
229
229
|
}, waitTime);
|
|
@@ -233,7 +233,7 @@ const prepareScriptArgs = () => {
|
|
|
233
233
|
|
|
234
234
|
/**
|
|
235
235
|
* 启动子进程
|
|
236
|
-
* @param {string} logLevel -
|
|
236
|
+
* @param {string} logLevel - 日志级别(info, warn, error等)
|
|
237
237
|
* @param {string} message - 日志消息
|
|
238
238
|
* @returns {void}
|
|
239
239
|
*/
|
|
@@ -273,7 +273,7 @@ const prepareScriptArgs = () => {
|
|
|
273
273
|
logger.warn(exitMessage);
|
|
274
274
|
// 检查是否需要因错误而终止
|
|
275
275
|
if (code !== 0 && (argv.abortonerror === 'yes' || argv.abortonerror === 'y'))
|
|
276
|
-
logger.error(`${scriptPath} 以错误代码 ${code}
|
|
276
|
+
logger.error(`${scriptPath} 以错误代码 ${code} 退出,终止包装器进程`), process.exit(code || 1);
|
|
277
277
|
else if (isForceKill) process.exit(0);
|
|
278
278
|
|
|
279
279
|
childProcess = null, monitorChildProcess(); // 重新监控并可能重启
|
|
@@ -306,10 +306,10 @@ const prepareScriptArgs = () => {
|
|
|
306
306
|
// 注册进程终止事件处理程序
|
|
307
307
|
process.on('exit', terminateChildProcess);
|
|
308
308
|
process.on('SIGINT', () => {
|
|
309
|
-
logger.info('收到 SIGINT
|
|
309
|
+
logger.info('收到 SIGINT 信号,正在终止...'), terminateChildProcess();
|
|
310
310
|
});
|
|
311
311
|
process.on('SIGTERM', () => {
|
|
312
|
-
logger.info('收到 SIGTERM
|
|
312
|
+
logger.info('收到 SIGTERM 信号,正在终止...'), terminateChildProcess();
|
|
313
313
|
});
|
|
314
314
|
|
|
315
315
|
// 处理未捕获异常
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flun/windows",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "支持 Windows 服务、事件日志、UAC 以及多种与操作系统交互的辅助方法",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"windows",
|
|
@@ -23,12 +23,15 @@
|
|
|
23
23
|
".": "./index.js"
|
|
24
24
|
},
|
|
25
25
|
"main": "index.js",
|
|
26
|
+
"bin": {
|
|
27
|
+
"windows": "bin/cli.js"
|
|
28
|
+
},
|
|
26
29
|
"files": [
|
|
27
30
|
"lib",
|
|
28
31
|
"bin",
|
|
29
32
|
"index.d.ts",
|
|
30
33
|
"index.js",
|
|
31
|
-
"
|
|
34
|
+
"win-service.config.js",
|
|
32
35
|
"copy-files.js",
|
|
33
36
|
"CHANGELOG.md",
|
|
34
37
|
"README.md",
|
|
@@ -36,6 +39,7 @@
|
|
|
36
39
|
],
|
|
37
40
|
"preferGlobal": true,
|
|
38
41
|
"dependencies": {
|
|
42
|
+
"chalk": "^6.0.1",
|
|
39
43
|
"xml": "1.0.1",
|
|
40
44
|
"yargs": "^18.2.0"
|
|
41
45
|
},
|
|
@@ -47,7 +51,7 @@
|
|
|
47
51
|
"type": "git",
|
|
48
52
|
"url": "git+https://github.com/OpenFlun/windows.git"
|
|
49
53
|
},
|
|
50
|
-
"homepage": "https://
|
|
54
|
+
"homepage": "https://github.com/OpenFlun/windows#readme",
|
|
51
55
|
"bugs": {
|
|
52
56
|
"url": "https://github.com/OpenFlun/windows/issues"
|
|
53
57
|
},
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows 服务配置模板
|
|
3
|
+
* 带 * 的为必填项,其余可删除或按需保留;
|
|
4
|
+
* 本配置既支持命令行方式(如 windows install),
|
|
5
|
+
* 也支持在函数方式中导入:import config from './win-service.config.js';
|
|
6
|
+
* 具体用法见 README 的「命令行」与「函数方式」章节;
|
|
7
|
+
*
|
|
8
|
+
* 关于 quiet:默认 false,各方法执行后会在终端打印结果(如"服务安装完成;");
|
|
9
|
+
* 若本包嵌入到自己的生产项目,终端输出会混入宿主日志,此时设为 true,
|
|
10
|
+
* 库只发事件不打印,由宿主自行监听 install/start/stop/uninstall/error 等事件;
|
|
11
|
+
* 命令行方式恒为 quiet,不受此字段影响;
|
|
12
|
+
*/
|
|
13
|
+
export default {
|
|
14
|
+
// * 服务名称,用于服务管理与注册表标识;不能含 <>:"\/|?*
|
|
15
|
+
name: 'MyApp',
|
|
16
|
+
// * 启动服务的入口脚本绝对路径
|
|
17
|
+
script: 'C:\\path\\to\\main.js',
|
|
18
|
+
|
|
19
|
+
// 服务描述,显示在服务管理器中
|
|
20
|
+
description: 'Node.js 服务',
|
|
21
|
+
// 传递给 node 进程的选项,可为数组或空格分隔的字符串
|
|
22
|
+
nodeOptions: ['--harmony'],
|
|
23
|
+
// 传递给脚本的选项
|
|
24
|
+
scriptOptions: '',
|
|
25
|
+
// node 可执行文件路径,默认取当前进程的 execPath
|
|
26
|
+
execPath: process.execPath,
|
|
27
|
+
// 服务工作目录,默认取当前工作目录
|
|
28
|
+
workingDirectory: process.cwd(),
|
|
29
|
+
|
|
30
|
+
// 无响应/故障前的最大重试次数,默认 null(不启用)
|
|
31
|
+
maxRetries: null,
|
|
32
|
+
// 60 秒内最大重启次数,0 表示不启用,默认 3
|
|
33
|
+
maxRestarts: 3,
|
|
34
|
+
// 停止服务的超时秒数,默认 30
|
|
35
|
+
stoptimeout: 30,
|
|
36
|
+
// 脚本停止后等待重启的秒数,默认 1
|
|
37
|
+
wait: 1,
|
|
38
|
+
// 重启等待时间的增长百分比,默认 0.25
|
|
39
|
+
grow: 0.25,
|
|
40
|
+
// 脚本运行错误时是否退出进程,默认 false
|
|
41
|
+
abortOnError: false,
|
|
42
|
+
// 是否先停止父进程,默认 false
|
|
43
|
+
stopparentfirst: false,
|
|
44
|
+
|
|
45
|
+
// 日志文件路径,默认与可执行文件同目录
|
|
46
|
+
logpath: null,
|
|
47
|
+
// 日志模式:rotate(默认)、truncate、append
|
|
48
|
+
logmode: 'rotate',
|
|
49
|
+
|
|
50
|
+
// 环境变量,形式为 { name, value } 或同构数组
|
|
51
|
+
env: { name: 'NODE_ENV', value: 'production' },
|
|
52
|
+
|
|
53
|
+
// 日志配置对象,可替代 logmode;含 mode、pattern、sizeThreshold、keepFiles
|
|
54
|
+
logging: undefined,
|
|
55
|
+
|
|
56
|
+
// 服务登录凭据;默认使用 LocalSystem
|
|
57
|
+
logOnAs: {
|
|
58
|
+
account: undefined,
|
|
59
|
+
password: null,
|
|
60
|
+
domain: process.env.COMPUTERNAME,
|
|
61
|
+
mungeCredentialsAfterInstall: true,
|
|
62
|
+
},
|
|
63
|
+
// 是否允许服务登录,默认 undefined
|
|
64
|
+
allowServiceLogon: undefined,
|
|
65
|
+
|
|
66
|
+
// 为 true 时不打印执行结果,只发事件;嵌入生产项目时建议开启
|
|
67
|
+
quiet: false,
|
|
68
|
+
};
|
package/sevWin.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Service } from '@flun/windows'; // 可选参数(EventLogger,elevate, sudo, isAdminUser, kill, list)
|
|
2
|
-
|
|
3
|
-
const serviceName = 'TestApp', scriptPath = 'D:\\test\\dev.js', // 请根据实际路径修改
|
|
4
|
-
svc = new Service({
|
|
5
|
-
name: serviceName,
|
|
6
|
-
description: 'Node.js 开发服务器',
|
|
7
|
-
script: scriptPath,
|
|
8
|
-
nodeOptions: ['--harmony', '--max-old-space-size=4096'],
|
|
9
|
-
env: { name: "NODE_ENV", value: "production" },
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const installService = () => {
|
|
13
|
-
console.log('🚀 开始安装服务...');
|
|
14
|
-
svc.on('install', () => svc.start());
|
|
15
|
-
svc.on('start', () => console.log('✅ 安装成功,服务已启动!!'));
|
|
16
|
-
svc.install();
|
|
17
|
-
}
|
|
18
|
-
installService();
|