aaden-cli 1.6.108 → 1.6.109
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/AadenDeploy/Utils/enableNgrok.sh +21 -19
- package/AadenDeploy/Utils/fixCronJob.sh +5 -4
- package/AadenDeploy/aadensetup.sh +217 -163
- package/AadenDeploy/checkDeviceId.sh +104 -0
- package/AadenDeploy/setDeviceId.sh +130 -0
- package/README.md +2 -1
- package/dist/index.js +186 -11
- package/dist/patch/202608190700removeGitDoUpdateCron.sh +39 -0
- package/index.js +30 -7
- package/modules/firebase.js +4 -3
- package/modules/tse-info.js +144 -0
- package/package.json +2 -2
- package/patch/202608190700removeGitDoUpdateCron.sh +39 -0
- package/dist/patch/202304051415FixDatevMemory.sh +0 -19
- package/patch/202304051415FixDatevMemory.sh +0 -19
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 设定本机设备号(= ERP 门店编号)。全新装机(aadensetup.sh)和镜像克隆来的机器改号都走这一个入口:
|
|
3
|
+
# aaden-cli set-device-id <号>
|
|
4
|
+
#
|
|
5
|
+
# 1. 先查云端:这个号是 ERP 里哪家店、是不是已经有收银机在用(checkDeviceId.sh),拦住撞号
|
|
6
|
+
# 2. 写配置:没有 config.ini 时让后端 AutoSetup.php 按默认配置生成;已有时只改 [Device] 段和 config.json,其余配置原样保留
|
|
7
|
+
# 3. 主机名 aadenXXXX,/etc/hosts 里只留一行(以前每次装机都往里追加一行)
|
|
8
|
+
# 4. ngrok 隧道 ikXXXX / SSH 端口 10000+号,重启隧道;第一次装时下载 ngrok 并装成服务
|
|
9
|
+
# 5. 马上上报一次,云端 ERP 门店列表的「机器」列随即显示就绪
|
|
10
|
+
#
|
|
11
|
+
# 退出码:0 成功;1 号不对或取消;2 写配置失败
|
|
12
|
+
export PATH="/usr/local/bin:$PATH"
|
|
13
|
+
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
14
|
+
CONFIG_DIR=/var/www/html/Resource/config
|
|
15
|
+
CONFIG_INI=$CONFIG_DIR/config.ini
|
|
16
|
+
CONFIG_JSON=$CONFIG_DIR/config.json
|
|
17
|
+
|
|
18
|
+
# 和 aaden-cli 其他地方一样用 -S 从 stdin 喂密码(aaden 不一定有免密 sudo)。
|
|
19
|
+
# 注意:不能再往 sudoS 的命令里用管道喂内容——stdin 已经被密码占了,内容一律先写临时文件再 sudoS cp。
|
|
20
|
+
sudoS() { echo "Innerken322." | sudo -S -p '' "$@"; }
|
|
21
|
+
# 先把 sudo 凭据缓存上:后面调的 aaden-cli 子命令(fix-ngrok 等)里还有裸 sudo
|
|
22
|
+
sudoS -v 2>/dev/null
|
|
23
|
+
|
|
24
|
+
NEW_ID="$1"
|
|
25
|
+
if [ -z "$NEW_ID" ]; then
|
|
26
|
+
read -r -p '设备号: ' NEW_ID
|
|
27
|
+
fi
|
|
28
|
+
NEW_ID=$(printf '%s' "$NEW_ID" | tr -d '[:space:]')
|
|
29
|
+
if ! [[ "$NEW_ID" =~ ^[0-9]+$ ]]; then
|
|
30
|
+
echo "设备号必须是数字:'$NEW_ID'"
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
NEW_ID=$((10#$NEW_ID)) # 去掉前导零,和云端、ERP 编号一致
|
|
34
|
+
NAME="IK$(printf '%04d' "$NEW_ID")"
|
|
35
|
+
HOST="aaden$(printf '%04d' "$NEW_ID")" # 和 ngrok 的 ik 号一样补到 4 位:aaden0001、aaden1958
|
|
36
|
+
|
|
37
|
+
ini_device_id() {
|
|
38
|
+
[ -f "$CONFIG_INI" ] || return
|
|
39
|
+
awk -F= '/^\[/ { s = $0 } s ~ /^\[Device\]/ && $1 ~ /^[ \t]*DeviceId[ \t]*$/ { gsub(/[ \t"\r]/, "", $2); print $2; exit }' "$CONFIG_INI"
|
|
40
|
+
}
|
|
41
|
+
OLD_ID=$(ini_device_id)
|
|
42
|
+
|
|
43
|
+
if [ "$OLD_ID" = "$NEW_ID" ]; then
|
|
44
|
+
echo "本机已经是设备号 $NEW_ID,只重新应用主机名、ngrok 并上报"
|
|
45
|
+
else
|
|
46
|
+
[ -n "$OLD_ID" ] && echo "本机原设备号:$OLD_ID → 改成 $NEW_ID"
|
|
47
|
+
bash "$DIR/checkDeviceId.sh" "$NEW_ID" || exit 1
|
|
48
|
+
|
|
49
|
+
# 已经启用 TSE 的机器改号:TSE 记录会挂到新号上。正常流程是先改号,再「清空数据、启用 TSE」
|
|
50
|
+
if [ -n "$OLD_ID" ] && grep -qiE '^[ \t]*TseEnabled[ \t]*=[ \t]*"?(true|1)"?[ \t]*$' "$CONFIG_INI"; then
|
|
51
|
+
echo "⚠ 本机已经启用 TSE(原设备号 $OLD_ID)。正常应该先改号,再清空数据、启用 TSE。"
|
|
52
|
+
answer=""
|
|
53
|
+
{ exec 3</dev/tty; } 2>/dev/null && read -r -p "确定要改号请输入 yes:" answer <&3 2>/dev/tty
|
|
54
|
+
[ "$answer" = "yes" ] || { echo "已取消。"; exit 1; }
|
|
55
|
+
fi
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# ---------------------------------------------------------------- 配置
|
|
59
|
+
if [ ! -f "$CONFIG_INI" ]; then
|
|
60
|
+
# 全新装机:后端按默认配置生成 config.ini / config.json(以 www-data 写,权限和以前一样)
|
|
61
|
+
curl -fsS -m 30 "http://localhost/PHP/AutoSetup.php?DeviceId=$NEW_ID" >/dev/null \
|
|
62
|
+
|| { echo "调用 AutoSetup.php 失败(Apache/PHP 还没起来?)"; exit 2; }
|
|
63
|
+
elif [ "$OLD_ID" != "$NEW_ID" ]; then
|
|
64
|
+
TMP=$(mktemp)
|
|
65
|
+
awk -v id="$NEW_ID" -v name="$NAME" '
|
|
66
|
+
function fill() {
|
|
67
|
+
if (!doneId) print "DeviceId = \"" id "\""
|
|
68
|
+
if (!doneName) print "DeviceName = \"" name "\""
|
|
69
|
+
}
|
|
70
|
+
/^\[/ {
|
|
71
|
+
if (inDev) fill()
|
|
72
|
+
inDev = ($0 ~ /^\[Device\][ \t\r]*$/)
|
|
73
|
+
if (inDev) seen = 1
|
|
74
|
+
print; next
|
|
75
|
+
}
|
|
76
|
+
inDev && /^[ \t]*DeviceId[ \t]*=/ { print "DeviceId = \"" id "\""; doneId = 1; next }
|
|
77
|
+
inDev && /^[ \t]*DeviceName[ \t]*=/ { print "DeviceName = \"" name "\""; doneName = 1; next }
|
|
78
|
+
{ print }
|
|
79
|
+
END {
|
|
80
|
+
if (inDev) fill()
|
|
81
|
+
if (!seen) { print "[Device]"; print "DeviceId = \"" id "\""; print "DeviceName = \"" name "\"" }
|
|
82
|
+
}' "$CONFIG_INI" >"$TMP"
|
|
83
|
+
# cp 覆盖内容、保留原文件的属主和权限
|
|
84
|
+
sudoS cp "$TMP" "$CONFIG_INI" || { rm -f "$TMP"; echo "写 $CONFIG_INI 失败"; exit 2; }
|
|
85
|
+
printf '{"DeviceId":"%s","DeviceName":"%s"}' "$NEW_ID" "$NAME" >"$TMP"
|
|
86
|
+
sudoS cp "$TMP" "$CONFIG_JSON"
|
|
87
|
+
rm -f "$TMP"
|
|
88
|
+
fi
|
|
89
|
+
if [ "$(ini_device_id)" != "$NEW_ID" ]; then
|
|
90
|
+
echo "写完配置后读到的设备号是「$(ini_device_id)」,不是 $NEW_ID"
|
|
91
|
+
exit 2
|
|
92
|
+
fi
|
|
93
|
+
echo "配置已写入:$CONFIG_INI → DeviceId = $NEW_ID"
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------- 主机名
|
|
96
|
+
# 先写 /etc/hosts 再改主机名,否则改完的那一刻 sudo 会报 unable to resolve host
|
|
97
|
+
TMP=$(mktemp)
|
|
98
|
+
grep -vE '[[:space:]]aaden[0-9]+[[:space:]]*$' /etc/hosts >"$TMP"
|
|
99
|
+
echo "127.0.1.1 $HOST" >>"$TMP"
|
|
100
|
+
sudoS cp "$TMP" /etc/hosts
|
|
101
|
+
rm -f "$TMP"
|
|
102
|
+
# WSL 没有 systemd 时 hostnamectl 不可用,退回直接写 /etc/hostname
|
|
103
|
+
if ! sudoS hostnamectl set-hostname "$HOST" 2>/dev/null; then
|
|
104
|
+
TMP=$(mktemp)
|
|
105
|
+
echo "$HOST" >"$TMP"
|
|
106
|
+
sudoS cp "$TMP" /etc/hostname && sudoS hostname "$HOST"
|
|
107
|
+
rm -f "$TMP"
|
|
108
|
+
fi
|
|
109
|
+
echo "主机名:$HOST"
|
|
110
|
+
|
|
111
|
+
# ---------------------------------------------------------------- ngrok
|
|
112
|
+
aaden-cli update-ngrok-config "$NEW_ID" >/dev/null
|
|
113
|
+
if [ -f /etc/systemd/system/aaden-ngrok-v3.service ] && [ -x /etc/aaden/ngrok ]; then
|
|
114
|
+
sudoS systemctl restart aaden-ngrok-v3 2>/dev/null || sudoS service aaden-ngrok-v3 restart
|
|
115
|
+
else
|
|
116
|
+
aaden-cli fix-ngrok # 第一次:下载 ngrok、装 systemd 服务
|
|
117
|
+
fi
|
|
118
|
+
echo "ngrok:ik$(printf '%04d' "$NEW_ID").ngrok.aaden.io,SSH 端口 $((10000 + NEW_ID))"
|
|
119
|
+
|
|
120
|
+
# ---------------------------------------------------------------- 上报
|
|
121
|
+
# do-log 连不上云端时退出码也是 0,所以回头问云端确认收到了没有
|
|
122
|
+
aaden-cli do-log >/dev/null 2>&1
|
|
123
|
+
MINUTES=$(curl -fsS -m 15 "${AADEN_CLOUD_URL:-https://cloud-v2.aaden.io}/deviceLog/install-check/$NEW_ID" 2>/dev/null \
|
|
124
|
+
| python3 -c 'import json, sys; m = json.load(sys.stdin).get("lastReportMinutesAgo"); print("" if m is None else m)' 2>/dev/null)
|
|
125
|
+
if [ -n "$MINUTES" ] && [ "$MINUTES" -le 5 ]; then
|
|
126
|
+
echo "云端已收到本机上报,ERP 门店列表的「机器」列显示就绪"
|
|
127
|
+
else
|
|
128
|
+
echo "⚠ 云端还没收到本机上报(网络不通?)。联网后每 5 分钟会自动上报一次"
|
|
129
|
+
fi
|
|
130
|
+
exit 0
|
package/README.md
CHANGED
|
@@ -28,7 +28,8 @@ Options:
|
|
|
28
28
|
|
|
29
29
|
Commands:
|
|
30
30
|
update [channel]
|
|
31
|
-
install [options] <deviceId> [channel]
|
|
31
|
+
install [options] <deviceId> [channel] 装机(= AadenDeploy/aadensetup.sh):撞号检查、装软件、部署后端、设备号、自检并上报
|
|
32
|
+
set-device-id [deviceId] 设定本机设备号:撞号检查、config.ini、主机名、ngrok、立即上报(镜像克隆的机器改号也用它)
|
|
32
33
|
setup-wifi
|
|
33
34
|
upgrade
|
|
34
35
|
where
|
package/dist/index.js
CHANGED
|
@@ -99,7 +99,7 @@ const {hillo} = __nccwpck_require__(6365)
|
|
|
99
99
|
const version = (__nccwpck_require__(4147)/* .version */ .i8)
|
|
100
100
|
const colors = __nccwpck_require__(3045)
|
|
101
101
|
const defaultData = {
|
|
102
|
-
frontendVersion: '', cliVersion: '', diskUsage: '', upTime: '', ngrokIsNew: false, hostInfo: {}
|
|
102
|
+
frontendVersion: '', cliVersion: '', diskUsage: '', upTime: '', ngrokIsNew: false, hostInfo: {}, tseInfo: {}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
async function deviceEcho(data) {
|
|
@@ -110,7 +110,7 @@ async function deviceEcho(data) {
|
|
|
110
110
|
Object.entries(data).forEach(en => {
|
|
111
111
|
console.log(...en)
|
|
112
112
|
})
|
|
113
|
-
const {frontendVersion, cliVersion, diskUsage, upTime, ngrokIsNew, hostInfo} = Object.assign({}, defaultData, data)
|
|
113
|
+
const {frontendVersion, cliVersion, diskUsage, upTime, ngrokIsNew, hostInfo, tseInfo} = Object.assign({}, defaultData, data)
|
|
114
114
|
if (parseInt(myDeviceId) === -1) {
|
|
115
115
|
return
|
|
116
116
|
}
|
|
@@ -132,7 +132,8 @@ async function deviceEcho(data) {
|
|
|
132
132
|
lastUptime: upTime,
|
|
133
133
|
ngrokOk: ngrokIsNew,
|
|
134
134
|
deviceName: `${name}\n${adress1}\n${postCode} ${city} ${state}`,
|
|
135
|
-
...hostInfo
|
|
135
|
+
...hostInfo,
|
|
136
|
+
...tseInfo
|
|
136
137
|
})
|
|
137
138
|
console.log('report to remote server complete')
|
|
138
139
|
} catch (e) {
|
|
@@ -249,6 +250,157 @@ function collectHostInfo() {
|
|
|
249
250
|
module.exports = {collectHostInfo}
|
|
250
251
|
|
|
251
252
|
|
|
253
|
+
/***/ }),
|
|
254
|
+
|
|
255
|
+
/***/ 6677:
|
|
256
|
+
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
|
257
|
+
|
|
258
|
+
const fs = __nccwpck_require__(7147)
|
|
259
|
+
const os = __nccwpck_require__(2037)
|
|
260
|
+
const http = __nccwpck_require__(3685)
|
|
261
|
+
const https = __nccwpck_require__(5687)
|
|
262
|
+
|
|
263
|
+
// do-log 顺带上报的 TSE 适配服务(TseHost)信息,用来在云端看车队的 TSE 版本和服务状态。
|
|
264
|
+
// WSL 门店的 TseHost 跑在 Windows 侧,网站目录里的 jar 不一定是实际在跑的那个,
|
|
265
|
+
// 所以和 PHP 一样按 config.ini 的 [TSE] TseHost 去访问服务,jar 也按服务配置去找。
|
|
266
|
+
// 每一项单独兜底:探测失败就报 null,不能让 do-log 本身挂掉。
|
|
267
|
+
|
|
268
|
+
const CONFIG_INI = '/var/www/html/Resource/config/config.ini'
|
|
269
|
+
const DEFAULT_TSE_HOST = 'http://localhost:8014'
|
|
270
|
+
// Linux:aaden-tse.service 执行 tseServing.sh,里面 java -jar 的那个 jar
|
|
271
|
+
const LINUX_SERVING_SCRIPT = '/var/www/html/TseHost/tseServing.sh'
|
|
272
|
+
const LINUX_DEFAULT_JAR = '/var/www/html/TseHost/tse-0.0.2.jar'
|
|
273
|
+
// Windows(WSL 门店):WinSW 服务目录,jar 名写在 AadenTseServiceRegister.xml 里
|
|
274
|
+
const WINDOWS_USERS_DIR = '/mnt/c/Users'
|
|
275
|
+
const WINDOWS_TSE_DIR = 'Desktop/AadenTseOnWindows'
|
|
276
|
+
const WINDOWS_DEFAULT_JAR = 'tse-0.0.3.jar'
|
|
277
|
+
// 和 PHP 的 Api.php 一样给 3 秒
|
|
278
|
+
const PROBE_TIMEOUT_MS = 3000
|
|
279
|
+
|
|
280
|
+
async function safe(fn, fallback = null) {
|
|
281
|
+
try {
|
|
282
|
+
const value = await fn()
|
|
283
|
+
return value === undefined ? fallback : value
|
|
284
|
+
} catch (e) {
|
|
285
|
+
return fallback
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function readTseSection() {
|
|
290
|
+
const section = {}
|
|
291
|
+
let inTse = false
|
|
292
|
+
for (const raw of fs.readFileSync(CONFIG_INI, 'utf-8').split(/\r?\n/)) {
|
|
293
|
+
const line = raw.trim()
|
|
294
|
+
if (!line || line.startsWith(';') || line.startsWith('#')) continue
|
|
295
|
+
const header = line.match(/^\[(.+)]$/)
|
|
296
|
+
if (header) {
|
|
297
|
+
inTse = header[1].trim() === 'TSE'
|
|
298
|
+
continue
|
|
299
|
+
}
|
|
300
|
+
if (!inTse) continue
|
|
301
|
+
const eq = line.indexOf('=')
|
|
302
|
+
if (eq < 0) continue
|
|
303
|
+
section[line.slice(0, eq).trim()] = line.slice(eq + 1).trim().replace(/^"(.*)"$/, '$1')
|
|
304
|
+
}
|
|
305
|
+
return section
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 与 PHP 的 INI_SCANNER_TYPED 一致:true/on/yes 为真,数字按非 0 为真
|
|
309
|
+
function parseEnabled(value) {
|
|
310
|
+
if (value === undefined) return false
|
|
311
|
+
const v = String(value).trim().toLowerCase()
|
|
312
|
+
if (['true', 'on', 'yes'].includes(v)) return true
|
|
313
|
+
if (/^-?\d+$/.test(v)) return parseInt(v, 10) !== 0
|
|
314
|
+
return false
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 连不上(服务没起、Windows 侧没开、超时)统一当作 status 0
|
|
318
|
+
function httpGet(url) {
|
|
319
|
+
return new Promise(resolve => {
|
|
320
|
+
// 兜底:响应中途断掉时 end 不会触发,不能让 do-log 卡在这里
|
|
321
|
+
const timer = setTimeout(() => resolve({status: 0, body: ''}), PROBE_TIMEOUT_MS + 1000)
|
|
322
|
+
const done = result => {
|
|
323
|
+
clearTimeout(timer)
|
|
324
|
+
resolve(result)
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
const client = url.startsWith('https:') ? https : http
|
|
328
|
+
const req = client.get(url, {timeout: PROBE_TIMEOUT_MS}, res => {
|
|
329
|
+
let body = ''
|
|
330
|
+
res.setEncoding('utf-8')
|
|
331
|
+
res.on('data', chunk => {
|
|
332
|
+
if (body.length < 4096) body += chunk
|
|
333
|
+
})
|
|
334
|
+
res.on('end', () => done({status: res.statusCode, body}))
|
|
335
|
+
res.on('error', () => done({status: 0, body: ''}))
|
|
336
|
+
})
|
|
337
|
+
req.on('timeout', () => req.destroy(new Error('timeout')))
|
|
338
|
+
req.on('error', () => done({status: 0, body: ''}))
|
|
339
|
+
} catch (e) {
|
|
340
|
+
done({status: 0, body: ''})
|
|
341
|
+
}
|
|
342
|
+
})
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function servingJarPath() {
|
|
346
|
+
if (/microsoft|wsl/i.test(os.release()) && fs.existsSync(WINDOWS_USERS_DIR)) {
|
|
347
|
+
for (const user of fs.readdirSync(WINDOWS_USERS_DIR)) {
|
|
348
|
+
const dir = `${WINDOWS_USERS_DIR}/${user}/${WINDOWS_TSE_DIR}`
|
|
349
|
+
const xml = `${dir}/AadenTseServiceRegister.xml`
|
|
350
|
+
if (!fs.existsSync(xml)) continue
|
|
351
|
+
const match = fs.readFileSync(xml, 'utf-8').match(/-jar\s+"?%BASE%\\([^"\s]+\.jar)/i)
|
|
352
|
+
return `${dir}/${match ? match[1] : WINDOWS_DEFAULT_JAR}`
|
|
353
|
+
}
|
|
354
|
+
// 没找到 Windows 侧的服务目录:按 Linux 的装法找
|
|
355
|
+
}
|
|
356
|
+
if (!fs.existsSync(LINUX_SERVING_SCRIPT)) return LINUX_DEFAULT_JAR
|
|
357
|
+
const match = fs.readFileSync(LINUX_SERVING_SCRIPT, 'utf-8').match(/-jar\s+(\S+\.jar)/)
|
|
358
|
+
return match ? match[1] : LINUX_DEFAULT_JAR
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async function probeTse() {
|
|
362
|
+
const section = await safe(readTseSection, {})
|
|
363
|
+
const tseEnabled = parseEnabled(section.TseEnabled)
|
|
364
|
+
const tseHost = (section.TseHost || DEFAULT_TSE_HOST).replace(/\/+$/, '')
|
|
365
|
+
// 不同版本的 jar 大小不同,没有 /adapterVersion 的老版本只能靠它区分
|
|
366
|
+
const tseJarSize = await safe(() => {
|
|
367
|
+
const jar = servingJarPath()
|
|
368
|
+
return jar && fs.existsSync(jar) ? fs.statSync(jar).size : null
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
if (!tseEnabled) {
|
|
372
|
+
return {tseEnabled, tseHost, tseStatus: 'disabled', tseAdapterVersion: null, tseJarSize}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ok:TSE 正常;error:服务在但 TSE 在自检 / 没初始化 / 硬件报错;down:服务连不上
|
|
376
|
+
const ping = await httpGet(`${tseHost}/ping`)
|
|
377
|
+
const tseStatus = ping.status === 0 ? 'down' : (ping.status === 200 ? 'ok' : 'error')
|
|
378
|
+
|
|
379
|
+
// 连不上时报 null,云端保留上次的版本;服务在但没有这个接口(2024-09 之前的构建)报空串
|
|
380
|
+
let tseAdapterVersion = null
|
|
381
|
+
if (ping.status !== 0) {
|
|
382
|
+
const res = await httpGet(`${tseHost}/adapterVersion`)
|
|
383
|
+
if (res.status === 200) {
|
|
384
|
+
tseAdapterVersion = await safe(() => {
|
|
385
|
+
const major = JSON.parse(res.body).major
|
|
386
|
+
return major === undefined || major === null ? null : String(major)
|
|
387
|
+
})
|
|
388
|
+
} else if (res.status === 404) {
|
|
389
|
+
tseAdapterVersion = ''
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return {tseEnabled, tseHost, tseStatus, tseAdapterVersion, tseJarSize}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// 整体出错就什么都不报,云端保留上次的值
|
|
397
|
+
async function collectTseInfo() {
|
|
398
|
+
return safe(probeTse, {})
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
module.exports = {collectTseInfo}
|
|
402
|
+
|
|
403
|
+
|
|
252
404
|
/***/ }),
|
|
253
405
|
|
|
254
406
|
/***/ 6545:
|
|
@@ -18577,7 +18729,7 @@ module.exports = JSON.parse('{"name":"axios","version":"0.19.2","description":"P
|
|
|
18577
18729
|
/***/ ((module) => {
|
|
18578
18730
|
|
|
18579
18731
|
"use strict";
|
|
18580
|
-
module.exports = {"i8":"1.6.
|
|
18732
|
+
module.exports = {"i8":"1.6.109"};
|
|
18581
18733
|
|
|
18582
18734
|
/***/ })
|
|
18583
18735
|
|
|
@@ -18664,6 +18816,7 @@ const InnerKenTimeStampFormat = 'YYYYMMDDHHmmss'
|
|
|
18664
18816
|
const prompts = __nccwpck_require__(9646)
|
|
18665
18817
|
const {deviceEcho, logAction, getCurrentDeviceId} = __nccwpck_require__(7415)
|
|
18666
18818
|
const {collectHostInfo} = __nccwpck_require__(3727)
|
|
18819
|
+
const {collectTseInfo} = __nccwpck_require__(6677)
|
|
18667
18820
|
const GlobalDeviceId = 'notset'
|
|
18668
18821
|
|
|
18669
18822
|
const {program} = __nccwpck_require__(1904)
|
|
@@ -18690,6 +18843,8 @@ program.command('rollback').action(rollback)
|
|
|
18690
18843
|
program
|
|
18691
18844
|
.command('install <deviceId>')
|
|
18692
18845
|
.action(install)
|
|
18846
|
+
// 设定本机设备号(= ERP 门店编号):撞号检查 + config.ini + 主机名 + ngrok + 立即上报。装机和镜像克隆来的机器改号都用它
|
|
18847
|
+
program.command('set-device-id [deviceId]').action(setDeviceId)
|
|
18693
18848
|
program.command('remove-all-data').action(removeAllData)
|
|
18694
18849
|
program.command('mount-tse').action(remountTSE)
|
|
18695
18850
|
program.command('upgrade').action(upgrade) // auto execute.
|
|
@@ -18957,12 +19112,13 @@ async function updateNgrokConfig(deviceId) {
|
|
|
18957
19112
|
console.log('Start to config Ngrok service')
|
|
18958
19113
|
console.log('开始配置' + deviceId + '设备的Ngrok服务')
|
|
18959
19114
|
|
|
19115
|
+
// 和其他地方一样用 -S 喂密码:aaden 没有免密 sudo、又没有终端时(set-device-id 被脚本调用)裸 sudo 会直接失败
|
|
18960
19116
|
const command = [
|
|
18961
19117
|
'cd /etc',
|
|
18962
|
-
'sudo mkdir -p aaden',
|
|
19118
|
+
'echo "Innerken322." | sudo -S -p \'\' mkdir -p aaden',
|
|
18963
19119
|
'cd aaden',
|
|
18964
|
-
'sudo touch ngrok-config.yml',
|
|
18965
|
-
'sudo chmod 777 ngrok-config.yml',
|
|
19120
|
+
'echo "Innerken322." | sudo -S -p \'\' touch ngrok-config.yml',
|
|
19121
|
+
'echo "Innerken322." | sudo -S -p \'\' chmod 777 ngrok-config.yml',
|
|
18966
19122
|
`echo "
|
|
18967
19123
|
server_addr: ngrok.aaden.io:4443
|
|
18968
19124
|
trust_host_root_certs: true
|
|
@@ -19374,11 +19530,29 @@ async function install(deviceId) {
|
|
|
19374
19530
|
'cd AadenDeploy',
|
|
19375
19531
|
`./aadensetup.sh ${deviceId} ${channel}` + (installApp ? ' b' : '')
|
|
19376
19532
|
]
|
|
19377
|
-
|
|
19533
|
+
// 「出厂安装」事件由 aadensetup.sh 自己在最后上报(带每一步的结果和自检),这里不再重复记
|
|
19534
|
+
const {code} = await cli.executeInShell(command, program.debug)
|
|
19535
|
+
process.exitCode = code
|
|
19378
19536
|
} else {
|
|
19379
19537
|
console.log('Device Id is invalid.')
|
|
19380
19538
|
}
|
|
19381
|
-
|
|
19539
|
+
}
|
|
19540
|
+
|
|
19541
|
+
async function setDeviceId(deviceId) {
|
|
19542
|
+
const oldId = getMyDeviceId()
|
|
19543
|
+
const command = [
|
|
19544
|
+
...getPrivilegeCMD('AadenDeploy/setDeviceId.sh'),
|
|
19545
|
+
'cd AadenDeploy',
|
|
19546
|
+
`./setDeviceId.sh ${deviceId && deviceId !== 'notset' ? deviceId : ''}`
|
|
19547
|
+
]
|
|
19548
|
+
const {code} = await cli.executeInShell(command, program.debug)
|
|
19549
|
+
// aadensetup.sh 按退出码判断这一步成没成
|
|
19550
|
+
process.exitCode = code
|
|
19551
|
+
if (code !== 0) {
|
|
19552
|
+
return
|
|
19553
|
+
}
|
|
19554
|
+
const newId = getMyDeviceId()
|
|
19555
|
+
await logAction(String(oldId) === String(newId) ? `重新应用设备号 ${newId}` : `设定设备号 ${oldId === -1 ? '(无)' : oldId} → ${newId}`)
|
|
19382
19556
|
}
|
|
19383
19557
|
|
|
19384
19558
|
function getMyDeviceId() {
|
|
@@ -19540,7 +19714,8 @@ async function doLog() {
|
|
|
19540
19714
|
diskUsage: diskUsage[diskUsage.length - 1].replace('\n', ''),
|
|
19541
19715
|
upTime: getUpTime(),
|
|
19542
19716
|
ngrokIsNew: ngrokNewestVersion(),
|
|
19543
|
-
hostInfo: collectHostInfo()
|
|
19717
|
+
hostInfo: collectHostInfo(),
|
|
19718
|
+
tseInfo: await collectTseInfo()
|
|
19544
19719
|
}
|
|
19545
19720
|
)
|
|
19546
19721
|
console.log('finish')
|
|
@@ -19555,7 +19730,7 @@ function getPrivilegeCMD(path, pri = '+x', relativePath = true) {
|
|
|
19555
19730
|
if (relativePath) {
|
|
19556
19731
|
command.push(goIntoRootDir())
|
|
19557
19732
|
}
|
|
19558
|
-
command.push(`echo "Innerken322." | sudo -S chmod ${pri} ${path}`)
|
|
19733
|
+
command.push(`echo "Innerken322." | sudo -S -p "" chmod ${pri} ${path}`)
|
|
19559
19734
|
return command
|
|
19560
19735
|
}
|
|
19561
19736
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 一次性修复:摘掉老 git 通道的 doUpdate cron。
|
|
3
|
+
#
|
|
4
|
+
# 病症:设备 crontab 里有
|
|
5
|
+
# 59 4 * * * curl -X POST 'http://localhost/PHP/UpdateSelf.php?op=doUpdate'
|
|
6
|
+
# @reboot ... curl -X POST '.../UpdateSelf.php?op=doUpdate'
|
|
7
|
+
# 它每天 04:59(和 @reboot)从**设备本地陈旧 gitCache** 部署后端;而 git fetch 因 deploy key
|
|
8
|
+
# 未分发到全网而失败(FETCH_HEAD 0 字节),缓存卡在旧版本;git 通道又没有"绝不降级"保护 →
|
|
9
|
+
# 于是把 light-update 刚更好的新版本降级回退(实测 1.9.63 → 1.9.62/1.9.61,每天来回打架)。
|
|
10
|
+
#
|
|
11
|
+
# 结论:light-update(S3/code.zip 通道)已是后端唯一活着的更新通道,且自带"只升不降"版本门。
|
|
12
|
+
# 摘掉 doUpdate cron,终结两条通道打架。crontab 不走 git/S3 部署 → 改了就稳。
|
|
13
|
+
#
|
|
14
|
+
# 幂等:没有该行就不动;grep -vF 精确匹配整串,不误伤 crontab.sh / light-update 那几行。
|
|
15
|
+
set -u
|
|
16
|
+
|
|
17
|
+
MARK='UpdateSelf.php?op=doUpdate'
|
|
18
|
+
|
|
19
|
+
strip_for() { # $1 = 目标用户(空=当前用户)
|
|
20
|
+
local u="$1" cur new
|
|
21
|
+
local args=()
|
|
22
|
+
[ -n "$u" ] && args=(-u "$u")
|
|
23
|
+
cur=$(crontab "${args[@]}" -l 2>/dev/null) || return 0
|
|
24
|
+
printf '%s\n' "$cur" | grep -qF "$MARK" || return 0 # 没有该 cron → 不动
|
|
25
|
+
new=$(printf '%s\n' "$cur" | grep -vF "$MARK")
|
|
26
|
+
printf '%s\n' "$new" | crontab "${args[@]}" - \
|
|
27
|
+
&& echo " 已从 ${u:-当前用户} 的 crontab 摘除 doUpdate cron"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# doUpdate cron 在 aaden 用户的 crontab。patch 正常由 root 的 auto-upgrade 触发 → 用 -u aaden;
|
|
31
|
+
# 兜底再扫一遍当前用户(万一某些设备以 aaden 直接跑 patch),谁有就摘谁。
|
|
32
|
+
if [ "$(id -u)" = "0" ]; then
|
|
33
|
+
strip_for aaden
|
|
34
|
+
strip_for ""
|
|
35
|
+
else
|
|
36
|
+
strip_for ""
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
echo " removeGitDoUpdateCron 完成(light-update 成为后端唯一更新通道)"
|
package/index.js
CHANGED
|
@@ -13,6 +13,7 @@ const InnerKenTimeStampFormat = 'YYYYMMDDHHmmss'
|
|
|
13
13
|
const prompts = require('prompts')
|
|
14
14
|
const {deviceEcho, logAction, getCurrentDeviceId} = require('./modules/firebase')
|
|
15
15
|
const {collectHostInfo} = require('./modules/host-info')
|
|
16
|
+
const {collectTseInfo} = require('./modules/tse-info')
|
|
16
17
|
const GlobalDeviceId = 'notset'
|
|
17
18
|
|
|
18
19
|
const {program} = require('commander')
|
|
@@ -39,6 +40,8 @@ program.command('rollback').action(rollback)
|
|
|
39
40
|
program
|
|
40
41
|
.command('install <deviceId>')
|
|
41
42
|
.action(install)
|
|
43
|
+
// 设定本机设备号(= ERP 门店编号):撞号检查 + config.ini + 主机名 + ngrok + 立即上报。装机和镜像克隆来的机器改号都用它
|
|
44
|
+
program.command('set-device-id [deviceId]').action(setDeviceId)
|
|
42
45
|
program.command('remove-all-data').action(removeAllData)
|
|
43
46
|
program.command('mount-tse').action(remountTSE)
|
|
44
47
|
program.command('upgrade').action(upgrade) // auto execute.
|
|
@@ -306,12 +309,13 @@ async function updateNgrokConfig(deviceId) {
|
|
|
306
309
|
console.log('Start to config Ngrok service')
|
|
307
310
|
console.log('开始配置' + deviceId + '设备的Ngrok服务')
|
|
308
311
|
|
|
312
|
+
// 和其他地方一样用 -S 喂密码:aaden 没有免密 sudo、又没有终端时(set-device-id 被脚本调用)裸 sudo 会直接失败
|
|
309
313
|
const command = [
|
|
310
314
|
'cd /etc',
|
|
311
|
-
'sudo mkdir -p aaden',
|
|
315
|
+
'echo "Innerken322." | sudo -S -p \'\' mkdir -p aaden',
|
|
312
316
|
'cd aaden',
|
|
313
|
-
'sudo touch ngrok-config.yml',
|
|
314
|
-
'sudo chmod 777 ngrok-config.yml',
|
|
317
|
+
'echo "Innerken322." | sudo -S -p \'\' touch ngrok-config.yml',
|
|
318
|
+
'echo "Innerken322." | sudo -S -p \'\' chmod 777 ngrok-config.yml',
|
|
315
319
|
`echo "
|
|
316
320
|
server_addr: ngrok.aaden.io:4443
|
|
317
321
|
trust_host_root_certs: true
|
|
@@ -723,11 +727,29 @@ async function install(deviceId) {
|
|
|
723
727
|
'cd AadenDeploy',
|
|
724
728
|
`./aadensetup.sh ${deviceId} ${channel}` + (installApp ? ' b' : '')
|
|
725
729
|
]
|
|
726
|
-
|
|
730
|
+
// 「出厂安装」事件由 aadensetup.sh 自己在最后上报(带每一步的结果和自检),这里不再重复记
|
|
731
|
+
const {code} = await cli.executeInShell(command, program.debug)
|
|
732
|
+
process.exitCode = code
|
|
727
733
|
} else {
|
|
728
734
|
console.log('Device Id is invalid.')
|
|
729
735
|
}
|
|
730
|
-
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
async function setDeviceId(deviceId) {
|
|
739
|
+
const oldId = getMyDeviceId()
|
|
740
|
+
const command = [
|
|
741
|
+
...getPrivilegeCMD('AadenDeploy/setDeviceId.sh'),
|
|
742
|
+
'cd AadenDeploy',
|
|
743
|
+
`./setDeviceId.sh ${deviceId && deviceId !== 'notset' ? deviceId : ''}`
|
|
744
|
+
]
|
|
745
|
+
const {code} = await cli.executeInShell(command, program.debug)
|
|
746
|
+
// aadensetup.sh 按退出码判断这一步成没成
|
|
747
|
+
process.exitCode = code
|
|
748
|
+
if (code !== 0) {
|
|
749
|
+
return
|
|
750
|
+
}
|
|
751
|
+
const newId = getMyDeviceId()
|
|
752
|
+
await logAction(String(oldId) === String(newId) ? `重新应用设备号 ${newId}` : `设定设备号 ${oldId === -1 ? '(无)' : oldId} → ${newId}`)
|
|
731
753
|
}
|
|
732
754
|
|
|
733
755
|
function getMyDeviceId() {
|
|
@@ -889,7 +911,8 @@ async function doLog() {
|
|
|
889
911
|
diskUsage: diskUsage[diskUsage.length - 1].replace('\n', ''),
|
|
890
912
|
upTime: getUpTime(),
|
|
891
913
|
ngrokIsNew: ngrokNewestVersion(),
|
|
892
|
-
hostInfo: collectHostInfo()
|
|
914
|
+
hostInfo: collectHostInfo(),
|
|
915
|
+
tseInfo: await collectTseInfo()
|
|
893
916
|
}
|
|
894
917
|
)
|
|
895
918
|
console.log('finish')
|
|
@@ -904,7 +927,7 @@ function getPrivilegeCMD(path, pri = '+x', relativePath = true) {
|
|
|
904
927
|
if (relativePath) {
|
|
905
928
|
command.push(goIntoRootDir())
|
|
906
929
|
}
|
|
907
|
-
command.push(`echo "Innerken322." | sudo -S chmod ${pri} ${path}`)
|
|
930
|
+
command.push(`echo "Innerken322." | sudo -S -p "" chmod ${pri} ${path}`)
|
|
908
931
|
return command
|
|
909
932
|
}
|
|
910
933
|
|
package/modules/firebase.js
CHANGED
|
@@ -3,7 +3,7 @@ const {hillo} = require('hillo')
|
|
|
3
3
|
const version = require('./../package.json').version
|
|
4
4
|
const colors = require('colors')
|
|
5
5
|
const defaultData = {
|
|
6
|
-
frontendVersion: '', cliVersion: '', diskUsage: '', upTime: '', ngrokIsNew: false, hostInfo: {}
|
|
6
|
+
frontendVersion: '', cliVersion: '', diskUsage: '', upTime: '', ngrokIsNew: false, hostInfo: {}, tseInfo: {}
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export async function deviceEcho(data) {
|
|
@@ -14,7 +14,7 @@ export async function deviceEcho(data) {
|
|
|
14
14
|
Object.entries(data).forEach(en => {
|
|
15
15
|
console.log(...en)
|
|
16
16
|
})
|
|
17
|
-
const {frontendVersion, cliVersion, diskUsage, upTime, ngrokIsNew, hostInfo} = Object.assign({}, defaultData, data)
|
|
17
|
+
const {frontendVersion, cliVersion, diskUsage, upTime, ngrokIsNew, hostInfo, tseInfo} = Object.assign({}, defaultData, data)
|
|
18
18
|
if (parseInt(myDeviceId) === -1) {
|
|
19
19
|
return
|
|
20
20
|
}
|
|
@@ -36,7 +36,8 @@ export async function deviceEcho(data) {
|
|
|
36
36
|
lastUptime: upTime,
|
|
37
37
|
ngrokOk: ngrokIsNew,
|
|
38
38
|
deviceName: `${name}\n${adress1}\n${postCode} ${city} ${state}`,
|
|
39
|
-
...hostInfo
|
|
39
|
+
...hostInfo,
|
|
40
|
+
...tseInfo
|
|
40
41
|
})
|
|
41
42
|
console.log('report to remote server complete')
|
|
42
43
|
} catch (e) {
|