aaden-cli 1.6.109-rc.0 → 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.
@@ -1,19 +1,21 @@
1
1
  #!/bin/bash
2
+ # 和 aaden-cli 其他地方一样用 -S 喂密码:aaden 没有免密 sudo、又没有终端时(被装机脚本或 set-device-id 调用)裸 sudo 会直接失败
3
+ sudoS() { echo "Innerken322." | sudo -S -p '' "$@"; }
2
4
 
3
- sudo npm config set prefix /usr/local
5
+ sudoS npm config set prefix /usr/local
4
6
  cd || exit
5
- sudo rm -rf ngrok
7
+ sudoS rm -rf ngrok
6
8
  axel -n 8 "https://aaden-storage.s3.eu-central-1.amazonaws.com/ngrok"
7
- sudo cp -f ngrok /etc/aaden/ngrok
8
- sudo chmod +x /etc/aaden/ngrok
9
+ sudoS cp -f ngrok /etc/aaden/ngrok
10
+ sudoS chmod +x /etc/aaden/ngrok
9
11
 
10
12
  DeviceId=$(cat /var/www/html/Resource/config/config.ini | grep -e DeviceId | cut -d = -f 2 | grep '[1-9]*' | sed 's/"//g')
11
13
  aaden-cli update-ngrok-config $DeviceId
12
14
 
13
15
  ServiceFileName=/etc/systemd/system/aaden-ngrok-v3.service
14
16
 
15
- sudo touch $ServiceFileName
16
- sudo chmod 777 $ServiceFileName
17
+ sudoS touch $ServiceFileName
18
+ sudoS chmod 777 $ServiceFileName
17
19
  echo -e "
18
20
  [Unit]
19
21
  Description=Ngrok Service v3 For Aaden
@@ -29,24 +31,24 @@ ExecStart=/etc/aaden/ngrok -config=/etc/aaden/ngrok-config.yml start-all --log=/
29
31
  [Install]
30
32
  WantedBy=multi-user.target
31
33
  " >$ServiceFileName
32
- sudo rm /etc/systemd/system/aaden-ngrok.service
33
- sudo rm /etc/systemd/system/aaden-ngrok-2.service
34
+ sudoS rm /etc/systemd/system/aaden-ngrok.service
35
+ sudoS rm /etc/systemd/system/aaden-ngrok-2.service
34
36
 
35
- sudo systemctl daemon-reload
36
- sudo systemctl start aaden-ngrok-v3.service
37
- sudo systemctl enable aaden-ngrok-v3.service
38
- sudo systemctl stop aaden-ngrok.service
39
- sudo systemctl stop aaden-ngrok-2.service
40
- sudo systemctl disable aaden-ngrok.service
41
- sudo systemctl disable aaden-ngrok-2.service
37
+ sudoS systemctl daemon-reload
38
+ sudoS systemctl start aaden-ngrok-v3.service
39
+ sudoS systemctl enable aaden-ngrok-v3.service
40
+ sudoS systemctl stop aaden-ngrok.service
41
+ sudoS systemctl stop aaden-ngrok-2.service
42
+ sudoS systemctl disable aaden-ngrok.service
43
+ sudoS systemctl disable aaden-ngrok-2.service
42
44
 
43
- sudo service aaden-ngrok-v3 restart
45
+ sudoS service aaden-ngrok-v3 restart
44
46
 
45
47
 
46
48
 
47
49
  ServiceFileName=/etc/avahi/services/aaden.service
48
- sudo touch $ServiceFileName
49
- sudo chmod 777 $ServiceFileName
50
+ sudoS touch $ServiceFileName
51
+ sudoS chmod 777 $ServiceFileName
50
52
  echo -e "
51
53
  <?xml version="1.0" standalone='no'?><!--*-nxml-*-->
52
54
  <!DOCTYPE service-group SYSTEM "avahi-service.dtd">
@@ -59,7 +61,7 @@ echo -e "
59
61
  </service-group>
60
62
  " >$ServiceFileName
61
63
 
62
- sudo service avahi-daemon restart
64
+ sudoS service avahi-daemon restart
63
65
 
64
66
  echo "ngrok 修复脚本执行完毕。请直接检查ngrok是否正常开启。如果开启,请无视这里的任何报错。"
65
67
 
@@ -1,4 +1,6 @@
1
1
  #!/bin/bash
2
+ # 和 aaden-cli 其他地方一样用 -S 喂密码:aaden 没有免密 sudo、又没有终端时(被装机脚本或 set-device-id 调用)裸 sudo 会直接失败
3
+ sudoS() { echo "Innerken322." | sudo -S -p '' "$@"; }
2
4
  echo 'Cronjob Before Aaden'
3
5
  echo "Innerken322." | sudo -S crontab -l -u aaden
4
6
  echo 'Cronjob Before Root'
@@ -15,11 +17,10 @@ echo "* * * * * curl --location --request GET 'http://localhost/PHP/BackendData.
15
17
  echo "* * * * * curl --location --request GET 'http://localhost/PHP/BackendData.php?op=autoSendMonthSummary'" >>conf
16
18
  echo "* * * * * curl --location --request GET 'http://localhost/PHP/ZBon.php?op=autoAddCombinedRecord'" >>conf
17
19
  echo "3 * * * * curl --location --request GET 'http://localhost/PHP/writeDETax2024.php'" >>conf
18
- echo "59 4 * * * curl --location -X POST 'http://localhost/PHP/UpdateSelf.php?op=doUpdate'" >>conf
19
- echo "@reboot sleep 60 && curl --location -X POST 'http://localhost/PHP/UpdateSelf.php?op=doUpdate'" >>conf
20
+ # 不再写 git doUpdate(59 4 / @reboot):git 通道 2026-08 已停用,后端 doUpdate 直接跳过,后端更新改走 root 的 auto-upgrade → light-update
20
21
  echo "*/5 * * * * /usr/local/bin/aaden-cli do-log" >>conf
21
- sudo crontab -u aaden -r
22
- sudo crontab -u aaden conf
22
+ sudoS crontab -u aaden -r
23
+ sudoS crontab -u aaden conf
23
24
  echo "Innerken322." | sudo -S rm -f conf
24
25
  crontab -l -u aaden
25
26
  echo "END Aaden Deploy CONFIG"
@@ -1,180 +1,234 @@
1
- echoWithWait() {
1
+ #!/bin/bash
2
+ # Aaden 收银机装机:全新的 Ubuntu(或 WSL)→ 可以交付的收银机。
3
+ # aaden-cli install <设备号> (或在 AadenDeploy 目录下 ./aadensetup.sh <设备号>)
4
+ #
5
+ # - 设备号最先问并查云端(checkDeviceId.sh),撞号在装任何东西之前拦下来
6
+ # - 每一步记下退出码和耗时;关键步骤失败就停下;最后做一遍自检,连同每一步的结果作为「出厂安装」事件上报云端
7
+ # - 全部输出另存一份到 /var/log/aaden-setup.log
8
+ # - 可以重跑:数据库已经初始化过就跳过,配置、主机名、/etc/hosts、定时任务都是覆盖写,不会重复追加
9
+ export PATH="/usr/local/bin:$PATH"
10
+ DIR="$(cd "$(dirname "$0")" && pwd)"
11
+ PW='Innerken322.'
12
+ LOG=/var/log/aaden-setup.log
13
+ WEB=/var/www/html
14
+ BACKEND_SRC=/etc/aaden/updateFiles/backendFiles
15
+
16
+ # 和 aaden-cli 其他地方一样用 -S 从 stdin 喂密码。不能再往 sudoS 的命令里重定向 stdin(密码会被当成内容),
17
+ # 需要重定向时写成 sudoS sh -c '… < 文件'
18
+ sudoS() { echo "$PW" | sudo -S -p '' "$@"; }
19
+ is_wsl() { grep -qi microsoft /proc/version 2>/dev/null; }
20
+
21
+ banner() {
22
+ echo ""
2
23
  echo "#######################################################################"
3
- echo $1 '....'
24
+ echo "# $*"
4
25
  echo "#######################################################################"
5
26
  }
6
27
 
7
- echo ""
8
- echo "#######################################################################"
9
- echo "# Start to configurate aaden! #"
10
- echo "# V 1.0.0 #"
11
- echo "#######################################################################"
12
- echo ""
13
- sudo add-apt-repository ppa:ondrej/php -y
28
+ banner "Aaden 收银机装机"
14
29
 
15
-
16
- echoWithWait "updating System...."
17
- sudo apt-get update -y
18
- sudo apt-get -o Dpkg::Options::='--force-confdef' --force-yes -fuy dist-upgrade
19
-
20
-
21
- echoWithWait "Installing Lamp...."
22
- sudo snap install axel
23
-
24
- sudo apt install apache2 -y
25
- sudo apt install net-tools -y
26
- sudo apt install software-properties-common apt-transport-https -y
27
- echoWithWait "Install PHP7.4"
28
-
29
- sudo apt install mysql-server -y
30
- sudo apt install aptitude -y
31
- sudo apt install curl -y
32
- sudo aptitude -y install expect
33
- sudo apt install default-jdk -y
34
- sudo apt install php7.4 php7.4-common libapache2-mod-php7.4 php7.4-cli -y
35
- sudo apt install php7.4-xml -y
36
- sudo apt install php7.4-curl php7.4-mysql php7.4-gd php7.4-zip php7.4-intl php7.4-common php7.4-mbstring -y
37
- sudo update-alternatives --set php /usr/bin/php7.4
38
- sudo update-alternatives --set phar /usr/bin/phar7.4
39
- sudo update-alternatives --set phar.phar /usr/bin/phar.phar7.4
40
-
41
- sudo systemctl restart apache2
42
-
43
- echoWithWait "Install Complate"
44
-
45
-
46
- sudo apt -y install vim apt-transport-https wget software-properties-common
47
-
48
-
49
- echoWithWait "Install Docker Requirment"
50
- sudo apt-get install -y \
51
- apt-transport-https \
52
- ca-certificates \
53
- curl \
54
- gnupg-agent \
55
- software-properties-common
56
-
57
- sudo apt update -y
58
- sudo apt-get install ttyload -y
59
-
60
- echoWithWait "Deploying ssh"
61
-
62
- sudo apt-get install openssh-server -y
63
-
64
- echoWithWait "Installing Pip for python3"
65
- sudo apt-get install python3-pip -y
66
-
67
- echoWithWait "Get Requests library for python3"
68
- python3 -m pip install requests
69
-
70
- sudo apt-get install unzip -y
71
- curl -sS https://getcomposer.org/installer -o composer-setup.php
72
- sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
73
- sudo composer self-update
74
- composer -v
75
-
76
- sudo chmod +x files/getFile.sh
77
- sudo chmod 777 -R .
78
-
79
- cd files/ || exit
80
- ./getFile.sh
81
- cd ..
82
-
83
- echoWithWait "Configing MYSQL SECURE, only for remote root disable no, else follow it and set to yes, please set the root password as defaultPassword"
84
-
85
- sudo chmod +x scureSql.sh
86
- sudo /bin/bash scureSql.sh 'Innerken322.'
87
-
88
- echoWithWait "Configing Mysql"
89
- cd /etc/aaden/updateFiles || exit
90
-
91
- sudo mysql -uroot < backendFiles/new.sql
92
-
93
- echoWithWait "Uploading PHP Files"
94
- sudo mv backendFiles/* /var/www/html/
95
- sudo service apache2 restart
96
- sudo chown -hR www-data:www-data /var/www/html
97
-
98
- sudo chmod -R 777 /var/www/html/Resource
99
- sudo chmod +x /var/www/html/crontab.sh
100
-
101
- # give excute permission to migrations
102
- sudo chmod +x /var/www/html/PHP/vendor/bin/phinx
103
- sudo chmod +x /var/www/html/PHP/vendor/robmorgan/phinx/bin/phinx
104
-
105
-
106
- echoWithWait "Skip App Install"
107
-
108
-
109
-
110
-
111
- echoWithWait "Add Cronjob"
112
- sudo touch conf
113
- sudo chmod 777 conf
114
- echo "* * * * * wget -O /dev/null http://localhost/PHP/AccessLog.php?op=reportStatus" >>conf
115
- echo "* * * * * /var/www/html/crontab.sh > /dev/null" >>conf
116
- crontab conf && sudo rm -f conf
117
- crontab -l
118
-
119
- echoWithWait "Finishing Install"
120
-
121
- if [ -z "$1" ]; then
122
- read -p 'DeviceId: ' DeviceId
123
- else
124
- DeviceId=$1
30
+ # ---------------------------------------------------------------- 设备号(最先问,先查撞号)
31
+ DEVICE_ID="$1"
32
+ if [ -z "$DEVICE_ID" ]; then
33
+ read -r -p '设备号: ' DEVICE_ID
125
34
  fi
35
+ bash "$DIR/checkDeviceId.sh" "$DEVICE_ID" || exit 1
36
+ DEVICE_ID=$((10#$DEVICE_ID))
37
+ export AADEN_SKIP_DEVICE_CHECK=1 # 后面 set-device-id 不再问第二遍
38
+
39
+ # sudo 凭据先缓存上并一直续着:装机要十几分钟,中途调的 aaden-cli 子命令里还有裸 sudo
40
+ sudoS -v 2>/dev/null || sudo -v || exit 1
41
+ (while kill -0 $$ 2>/dev/null; do sudoS -v 2>/dev/null; sleep 50; done) &
42
+ KEEPALIVE=$!
43
+ trap 'kill $KEEPALIVE 2>/dev/null' EXIT
44
+
45
+ sudoS touch "$LOG" && sudoS chmod 666 "$LOG"
46
+ exec > >(tee -a "$LOG") 2>&1
47
+ echo "$(date '+%F %T') 开始装机,设备号 $DEVICE_ID"
48
+
49
+ # ---------------------------------------------------------------- 步骤记录与上报
50
+ STEPS=""
51
+ step() { # step <名字> <命令…>:输出照常打到屏幕,记下退出码和耗时
52
+ local name=$1 start=$SECONDS code
53
+ shift
54
+ banner "$name"
55
+ "$@"
56
+ code=$?
57
+ STEPS="$STEPS $name=$code/$((SECONDS - start))s"
58
+ [ "$code" = 0 ] || echo "✗ $name 失败(退出码 $code),详细输出见 $LOG"
59
+ return "$code"
60
+ }
126
61
 
127
- echoWithWait "useDeviceId:$DeviceId"
128
-
129
- wget "http://innerken.com/iplogger/index.php?op=register&deviceId=$DeviceId"
130
- wget http://localhost/PHP/AutoSetup.php?DeviceId=$DeviceId
131
- wget http://localhost/PHP/AccessLog.php?op=reportStatus
132
- sudo hostnamectl set-hostname aaden$DeviceId
133
- echo $(hostname -I | cut -d\ -f1) $(hostname) | sudo tee -a /etc/hosts
134
-
135
- echoWithWait "SetUpOfflineUsbDriver"
136
-
137
- sudo usermod -a -G lp aaden
138
- sudo usermod -a -G lp www-data
139
- sudo usermod -a -G root aaden
140
- sudo usermod -a -G root www-data
141
- sudo usermod -a -G adm aaden
142
- sudo usermod -a -G sudo aaden
143
-
144
- echoWithWait "RUN MIGRATION"
145
- # run migrations
146
- cd /var/www/html/PHP || exit
147
- vendor/bin/phinx migrate
62
+ report() { # report <result> <附加内容>:「出厂安装」事件,在云端设备事件里能看到每一步和自检结果
63
+ local content="出厂安装 [${STEPS# }]$2 result:$1"
64
+ python3 - "$DEVICE_ID" "$content" "$(aaden-cli --version 2>/dev/null | tail -n 1)" <<'PY' || echo "(上报云端失败)"
65
+ import json, sys, urllib.request
66
+ device_id, content, version = sys.argv[1:4]
67
+ body = json.dumps({"deviceId": device_id, "content": content, "type": "系统生成", "operator": "aaden-cli v" + version}).encode()
68
+ urllib.request.urlopen(urllib.request.Request("https://cloud-v2.aaden.io/deviceLog/createEvent", body, {"Content-Type": "application/json"}), timeout=10)
69
+ PY
70
+ }
148
71
 
149
- aaden-cli patch
150
- # 日结物化守护进程:每日数据库备份和 DSFinV-K 日结物化都靠它(以前漏在装机脚本外,靠手动补)
151
- aaden-cli auto-write-gdpdu
152
- aaden-cli fix-cronjob
153
- aaden-cli upgrade
154
- aaden-cli update-ngrok-config $DeviceId
155
- aaden-cli fix-ngrok
72
+ must() { # 关键步骤:失败就上报并停下,后面的步骤都依赖它
73
+ step "$@" && return 0
74
+ report 1 " 中止于 $1"
75
+ banner "装机中止:$1 失败。修好之后重新运行同一条命令即可,已经完成的部分会跳过或覆盖。日志:$LOG"
76
+ exit 1
77
+ }
156
78
 
79
+ # ---------------------------------------------------------------- 1. 系统软件
80
+ install_packages() {
81
+ local apt="env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold"
82
+ # 上次装到一半断掉(断电、断网、包脚本失败)留下的半配置状态先收拾掉,否则后面的 apt 全部报错
83
+ sudoS env DEBIAN_FRONTEND=noninteractive dpkg --configure -a
84
+ command -v add-apt-repository >/dev/null || sudoS $apt install software-properties-common || return 1
85
+ # Ubuntu 22.04 起官方源没有 PHP 7.4
86
+ sudoS add-apt-repository -y ppa:ondrej/php || return 1
87
+ sudoS apt-get update || return 1
88
+ sudoS $apt install \
89
+ apache2 mysql-server \
90
+ php7.4 php7.4-common php7.4-cli libapache2-mod-php7.4 \
91
+ php7.4-xml php7.4-curl php7.4-mysql php7.4-gd php7.4-zip php7.4-intl php7.4-mbstring \
92
+ default-jdk openssh-server \
93
+ curl wget unzip axel expect vim net-tools ttyload ca-certificates apt-transport-https \
94
+ python3-requests || return 1
95
+ # 装了别的 PHP 版本时,命令行默认的 php 也指向 7.4
96
+ sudoS update-alternatives --set php /usr/bin/php7.4
97
+ sudoS update-alternatives --set phar /usr/bin/phar7.4 2>/dev/null
98
+ sudoS update-alternatives --set phar.phar /usr/bin/phar.phar7.4 2>/dev/null
99
+ sudoS systemctl restart apache2 2>/dev/null || sudoS service apache2 restart
100
+ }
101
+ must apt install_packages
157
102
 
158
- echo ""
159
- echo "#######################################################################"
160
- echo "# Auto Config Complete! #"
161
- echo "# V 1.0.0 #"
162
- echo "#######################################################################"
163
- echo ""
103
+ # ---------------------------------------------------------------- 2. 后端代码
104
+ download_backend() {
105
+ bash "$DIR/files/getFile.sh" && [ -f "$BACKEND_SRC/package.json" ]
106
+ }
107
+ must download download_backend
108
+
109
+ # ---------------------------------------------------------------- 3. 数据库
110
+ setup_database() {
111
+ # 先确保 MySQL 在跑(没有 systemd 的 WSL 不会自动起),否则下面的判断会把已有的库当成没有
112
+ sudoS service mysql start 2>/dev/null
113
+ # 没有 systemd 时 socket 目录可能是 700,aaden 跑数据库迁移会连不上(SQLSTATE 2002 Permission denied)
114
+ sudoS chmod 755 /var/run/mysqld 2>/dev/null
115
+ if sudoS mysql -uroot -e 'USE aaden' 2>/dev/null || sudoS test -d /var/lib/mysql/aaden; then
116
+ echo "数据库 aaden 已存在,跳过初始化(重跑装机不会覆盖门店数据)"
117
+ return 0
118
+ fi
119
+ sudoS bash "$DIR/scureSql.sh" "$PW"
120
+ sudoS sh -c "mysql -uroot < '$BACKEND_SRC/new.sql'" && sudoS mysql -uroot -e 'USE aaden'
121
+ }
122
+ must db setup_database
123
+
124
+ # ---------------------------------------------------------------- 4. 部署后端
125
+ deploy_backend() {
126
+ # cp 而不是 mv:重跑时目标目录已存在,mv 会失败
127
+ sudoS cp -a "$BACKEND_SRC/." "$WEB/" || return 1
128
+ sudoS rm -rf "$BACKEND_SRC"
129
+ sudoS chown -hR www-data:www-data "$WEB"
130
+ sudoS chmod -R 777 "$WEB/Resource"
131
+ sudoS chmod +x "$WEB/crontab.sh" "$WEB/PHP/vendor/bin/phinx" "$WEB/PHP/vendor/robmorgan/phinx/bin/phinx"
132
+ sudoS systemctl restart apache2 2>/dev/null || sudoS service apache2 restart
133
+ sleep 2
134
+ curl -fsS -m 10 http://localhost/package.json | grep '"version"'
135
+ }
136
+ must deploy deploy_backend
164
137
 
165
- echo ""
166
- echo "#######################################################################"
167
- echo "# Install TeamViewer #"
168
- echo "# V 1.0.0 #"
169
- echo "#######################################################################"
170
- echo ""
138
+ # ---------------------------------------------------------------- 5. 设备号:配置、主机名、ngrok、上报
139
+ must device aaden-cli set-device-id "$DEVICE_ID"
171
140
 
172
- cd || exit
173
- wget https://download.teamviewer.com/download/linux/teamviewer_amd64.deb
174
- sudo apt -y install ./teamviewer_amd64.deb
141
+ # ---------------------------------------------------------------- 6. 数据库迁移
142
+ must migrate sh -c "cd '$WEB/PHP' && vendor/bin/phinx migrate"
175
143
 
176
- wget https://aaden.online/AadenOS/background.png
144
+ # ---------------------------------------------------------------- 7. 系统配置(失败不中止,最后的自检会标出来)
145
+ add_groups() {
146
+ # 打印机(lp)、读日志(adm)、aaden 能 sudo;www-data 要能访问 USB 打印机
147
+ sudoS usermod -a -G lp,root,adm,sudo aaden && sudoS usermod -a -G lp,root www-data
148
+ }
149
+ step groups add_groups
150
+ step patch aaden-cli patch
151
+ # 日结物化守护进程:每日数据库备份和 DSFinV-K 日结物化都靠它
152
+ step gdpdu aaden-cli auto-write-gdpdu
153
+ # aaden 和 root 的定时任务整份覆盖写(do-log 上报、每日自动更新等)
154
+ step cron aaden-cli fix-cronjob
155
+ # 升级 aaden-cli、跑补丁、后端 light-update、同步 AppCenter 前端
156
+ step upgrade aaden-cli upgrade
157
+
158
+ if ! is_wsl; then
159
+ install_teamviewer() {
160
+ local deb
161
+ deb=$(mktemp --suffix=.deb)
162
+ wget -q -O "$deb" https://download.teamviewer.com/download/linux/teamviewer_amd64.deb \
163
+ && sudoS env DEBIAN_FRONTEND=noninteractive apt-get -y install "$deb"
164
+ local code=$?
165
+ rm -f "$deb"
166
+ wget -q -O "$HOME/background.png" https://aaden.online/AadenOS/background.png
167
+ return $code
168
+ }
169
+ step teamviewer install_teamviewer
170
+ fi
177
171
 
172
+ # ---------------------------------------------------------------- 8. 自检
173
+ banner "自检"
174
+ CHECKS=""
175
+ BAD=""
176
+ check() { # check <key> <说明> <命令…>
177
+ local key=$1 label=$2
178
+ shift 2
179
+ if "$@" >/dev/null 2>&1; then
180
+ CHECKS="$CHECKS $key=ok"
181
+ echo " ✓ $label"
182
+ else
183
+ CHECKS="$CHECKS $key=FAIL"
184
+ BAD="$BAD $label"
185
+ echo " ✗ $label"
186
+ fi
187
+ }
188
+ NGROK_HOST="ik$(printf '%04d' "$DEVICE_ID").ngrok.aaden.io"
178
189
 
190
+ check_cloud() {
191
+ aaden-cli do-log >/dev/null 2>&1
192
+ curl -fsS -m 15 "https://cloud-v2.aaden.io/deviceLog/install-check/$DEVICE_ID" \
193
+ | python3 -c 'import json,sys; m = json.load(sys.stdin).get("lastReportMinutesAgo"); sys.exit(0 if m is not None and m <= 10 else 1)'
194
+ }
195
+ check_ngrok() { # 隧道刚起来时要等几秒
196
+ local i
197
+ for i in 1 2 3 4 5 6; do
198
+ curl -fsS -m 10 "https://$NGROK_HOST/package.json" | grep -q '"version"' && return 0
199
+ sleep 10
200
+ done
201
+ return 1
202
+ }
203
+ check_gdpdu() {
204
+ systemctl is-active --quiet aaden-gdpdu.service 2>/dev/null \
205
+ || pgrep -f 'autoWriteGdpdu\.php' >/dev/null \
206
+ || [ -f /etc/cron.d/aaden-gdpdu ]
207
+ }
208
+ check_cron() {
209
+ crontab -l 2>/dev/null | grep -q 'do-log' && sudoS crontab -l -u root 2>/dev/null | grep -q 'auto-upgrade'
210
+ }
179
211
 
212
+ check_id() {
213
+ [ "$(awk -F= '/^\[/ { s = $0 } s ~ /^\[Device\]/ && $1 ~ /^[ \t]*DeviceId[ \t]*$/ { gsub(/[ \t"\r]/, "", $2); print $2; exit }' "$WEB/Resource/config/config.ini")" = "$DEVICE_ID" ]
214
+ }
180
215
 
216
+ check backend "后端在跑(http://localhost/package.json)" sh -c 'curl -fsS -m 10 http://localhost/package.json | grep -q version'
217
+ check id "设备号写入配置($DEVICE_ID)" check_id
218
+ check cloud "云端收到本机上报(ERP 门店列表「机器」列显示就绪)" check_cloud
219
+ check ngrok "远程访问 https://$NGROK_HOST" check_ngrok
220
+ check gdpdu "日结守护进程 aaden-gdpdu(没有它就没有每日备份)" check_gdpdu
221
+ check cron "定时任务(上报、每日自动更新)" check_cron
222
+
223
+ FAILED_STEPS=$(echo "$STEPS" | tr ' ' '\n' | grep -v '=0/' | cut -d= -f1 | tr '\n' ' ')
224
+ if [ -z "$BAD" ] && [ -z "${FAILED_STEPS// /}" ]; then
225
+ report 0 " [自检${CHECKS}]"
226
+ banner "装机完成,设备号 $DEVICE_ID,可以交付"
227
+ exit 0
228
+ fi
229
+ report 1 " [自检${CHECKS}]"
230
+ banner "装机跑完了,但有问题要处理"
231
+ [ -n "${FAILED_STEPS// /}" ] && echo "失败的步骤:$FAILED_STEPS"
232
+ [ -n "$BAD" ] && echo "没通过的自检:$BAD"
233
+ echo "处理完可以重新运行同一条命令。日志:$LOG"
234
+ exit 1
@@ -0,0 +1,104 @@
1
+ #!/bin/bash
2
+ # 装机前检查设备号:这个号在 ERP 是哪家店、是不是已经有收银机在用。拦住输错号、用了别家号的「撞号」——
3
+ # 同一个号装两台机器,云端的上报、备份、报表、远程连接(ngrok ik 号)会互相覆盖。
4
+ #
5
+ # 退出码 0 = 继续装;1 = 号不对或装机同事取消。
6
+ # 连不上云端只警告、不挡安装(现场网络常常不好)。确实要跳过检查:AADEN_SKIP_DEVICE_CHECK=1
7
+ DeviceId="$1"
8
+ CLOUD="${AADEN_CLOUD_URL:-https://cloud-v2.aaden.io}"
9
+
10
+ [ -n "$AADEN_SKIP_DEVICE_CHECK" ] && { echo "跳过设备号检查(AADEN_SKIP_DEVICE_CHECK)"; exit 0; }
11
+ if ! [[ "$DeviceId" =~ ^[0-9]+$ ]]; then
12
+ echo "设备号必须是数字:'$DeviceId'"
13
+ exit 1
14
+ fi
15
+
16
+ # 提问走 /dev/tty:脚本的标准输入可能被管道占着。没有终端(自动化装机)时 ask 返回空串
17
+ HAVE_TTY=""
18
+ { exec 3</dev/tty; } 2>/dev/null && HAVE_TTY=1
19
+ ask() {
20
+ local answer=""
21
+ [ -n "$HAVE_TTY" ] && read -r -p "$1" answer <&3 2>/dev/tty
22
+ echo "$answer"
23
+ }
24
+
25
+ URL="$CLOUD/deviceLog/install-check/$DeviceId"
26
+ JSON=$(curl -fsS -m 15 "$URL" 2>/dev/null || wget -qO- -T 15 "$URL" 2>/dev/null)
27
+
28
+ # 第一行是结论(OK / IN_USE / NO_STORE / ERROR),后面是给装机同事看的说明
29
+ REPORT=$(python3 - "$JSON" <<'PY'
30
+ import json, sys
31
+ try:
32
+ d = json.loads(sys.argv[1])
33
+ store, machine = d.get("store"), d.get("machine")
34
+ except Exception:
35
+ print("ERROR")
36
+ sys.exit()
37
+
38
+ status = {"AwaitingInstallation": "等待安装", "Installed": "已安装", "Closed": "已关停", "StopService": "停止服务"}
39
+
40
+ def when(v):
41
+ return v[:16].replace("T", " ") if v else "—"
42
+
43
+ def ago(minutes):
44
+ if minutes is None:
45
+ return "—"
46
+ if minutes < 60:
47
+ return f"{max(minutes, 1)} 分钟前"
48
+ if minutes < 24 * 60:
49
+ return f"{round(minutes / 60)} 小时前"
50
+ return f"{round(minutes / 60 / 24)} 天前"
51
+
52
+ if not store:
53
+ print("NO_STORE")
54
+ sys.exit()
55
+
56
+ parts = [p for p in (store.get("displayCode"), store.get("city"), status.get(store.get("status"), store.get("status"))) if p]
57
+ store_line = f"ERP 门店「{store.get('name') or '—'}」({','.join(parts)})"
58
+
59
+ if machine:
60
+ print("IN_USE")
61
+ print(f" 收银机里的餐厅名:{machine.get('posName') or '—'}")
62
+ print(f" 第一次上报 {when(machine.get('firstReportAt'))},最近上报 {ago(d.get('lastReportMinutesAgo'))}")
63
+ print(f" {store_line}")
64
+ else:
65
+ print("OK")
66
+ print(f" {store_line}")
67
+ print(" 云端还没收到过这个号的收银机上报")
68
+ PY
69
+ )
70
+ VERDICT=$(echo "$REPORT" | head -n 1)
71
+ DETAIL=$(echo "$REPORT" | tail -n +2)
72
+
73
+ echo ""
74
+ echo "#######################################################################"
75
+ echo "检查设备号 $DeviceId"
76
+ case "$VERDICT" in
77
+ OK)
78
+ echo "$DETAIL"
79
+ # 没有终端可问(自动化装机)时号也没人用,直接继续
80
+ if [ -n "$HAVE_TTY" ]; then
81
+ answer=$(ask "是这家店吗?[y/N] ")
82
+ [[ "$answer" =~ ^[yY] ]] || { echo "已取消。"; exit 1; }
83
+ fi
84
+ ;;
85
+ IN_USE)
86
+ echo "⚠ 这个号已经有收银机在用:"
87
+ echo "$DETAIL"
88
+ echo "同一个号装两台机器,云端的上报、备份、报表、远程连接会互相覆盖。"
89
+ echo "只有给这家店换机或重装才应该继续,而且旧机器要先关掉。"
90
+ answer=$(ask "确认继续请再输入一遍设备号(直接回车取消):")
91
+ [ "$answer" = "$DeviceId" ] || { echo "已取消。"; exit 1; }
92
+ ;;
93
+ NO_STORE)
94
+ echo "✗ ERP 里没有编号 $DeviceId 的门店:先在 ERP 建档,或者检查设备号有没有输错。"
95
+ echo " 确实要装:AADEN_SKIP_DEVICE_CHECK=1 再跑一次"
96
+ exit 1
97
+ ;;
98
+ *)
99
+ echo "⚠ 连不上云端($CLOUD),没法检查设备号有没有撞号,继续安装。"
100
+ sleep 3
101
+ ;;
102
+ esac
103
+ echo "#######################################################################"
104
+ exit 0
@@ -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
@@ -18729,7 +18729,7 @@ module.exports = JSON.parse('{"name":"axios","version":"0.19.2","description":"P
18729
18729
  /***/ ((module) => {
18730
18730
 
18731
18731
  "use strict";
18732
- module.exports = JSON.parse('{"i8":"1.6.109-rc.0"}');
18732
+ module.exports = {"i8":"1.6.109"};
18733
18733
 
18734
18734
  /***/ })
18735
18735
 
@@ -18843,6 +18843,8 @@ program.command('rollback').action(rollback)
18843
18843
  program
18844
18844
  .command('install <deviceId>')
18845
18845
  .action(install)
18846
+ // 设定本机设备号(= ERP 门店编号):撞号检查 + config.ini + 主机名 + ngrok + 立即上报。装机和镜像克隆来的机器改号都用它
18847
+ program.command('set-device-id [deviceId]').action(setDeviceId)
18846
18848
  program.command('remove-all-data').action(removeAllData)
18847
18849
  program.command('mount-tse').action(remountTSE)
18848
18850
  program.command('upgrade').action(upgrade) // auto execute.
@@ -19110,12 +19112,13 @@ async function updateNgrokConfig(deviceId) {
19110
19112
  console.log('Start to config Ngrok service')
19111
19113
  console.log('开始配置' + deviceId + '设备的Ngrok服务')
19112
19114
 
19115
+ // 和其他地方一样用 -S 喂密码:aaden 没有免密 sudo、又没有终端时(set-device-id 被脚本调用)裸 sudo 会直接失败
19113
19116
  const command = [
19114
19117
  'cd /etc',
19115
- 'sudo mkdir -p aaden',
19118
+ 'echo "Innerken322." | sudo -S -p \'\' mkdir -p aaden',
19116
19119
  'cd aaden',
19117
- 'sudo touch ngrok-config.yml',
19118
- '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',
19119
19122
  `echo "
19120
19123
  server_addr: ngrok.aaden.io:4443
19121
19124
  trust_host_root_certs: true
@@ -19527,11 +19530,29 @@ async function install(deviceId) {
19527
19530
  'cd AadenDeploy',
19528
19531
  `./aadensetup.sh ${deviceId} ${channel}` + (installApp ? ' b' : '')
19529
19532
  ]
19530
- await cli.executeInShell(command, program.debug)
19533
+ // 「出厂安装」事件由 aadensetup.sh 自己在最后上报(带每一步的结果和自检),这里不再重复记
19534
+ const {code} = await cli.executeInShell(command, program.debug)
19535
+ process.exitCode = code
19531
19536
  } else {
19532
19537
  console.log('Device Id is invalid.')
19533
19538
  }
19534
- logAction("出厂安装")
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}`)
19535
19556
  }
19536
19557
 
19537
19558
  function getMyDeviceId() {
@@ -19709,7 +19730,7 @@ function getPrivilegeCMD(path, pri = '+x', relativePath = true) {
19709
19730
  if (relativePath) {
19710
19731
  command.push(goIntoRootDir())
19711
19732
  }
19712
- command.push(`echo "Innerken322." | sudo -S chmod ${pri} ${path}`)
19733
+ command.push(`echo "Innerken322." | sudo -S -p "" chmod ${pri} ${path}`)
19713
19734
  return command
19714
19735
  }
19715
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
@@ -40,6 +40,8 @@ program.command('rollback').action(rollback)
40
40
  program
41
41
  .command('install <deviceId>')
42
42
  .action(install)
43
+ // 设定本机设备号(= ERP 门店编号):撞号检查 + config.ini + 主机名 + ngrok + 立即上报。装机和镜像克隆来的机器改号都用它
44
+ program.command('set-device-id [deviceId]').action(setDeviceId)
43
45
  program.command('remove-all-data').action(removeAllData)
44
46
  program.command('mount-tse').action(remountTSE)
45
47
  program.command('upgrade').action(upgrade) // auto execute.
@@ -307,12 +309,13 @@ async function updateNgrokConfig(deviceId) {
307
309
  console.log('Start to config Ngrok service')
308
310
  console.log('开始配置' + deviceId + '设备的Ngrok服务')
309
311
 
312
+ // 和其他地方一样用 -S 喂密码:aaden 没有免密 sudo、又没有终端时(set-device-id 被脚本调用)裸 sudo 会直接失败
310
313
  const command = [
311
314
  'cd /etc',
312
- 'sudo mkdir -p aaden',
315
+ 'echo "Innerken322." | sudo -S -p \'\' mkdir -p aaden',
313
316
  'cd aaden',
314
- 'sudo touch ngrok-config.yml',
315
- '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',
316
319
  `echo "
317
320
  server_addr: ngrok.aaden.io:4443
318
321
  trust_host_root_certs: true
@@ -724,11 +727,29 @@ async function install(deviceId) {
724
727
  'cd AadenDeploy',
725
728
  `./aadensetup.sh ${deviceId} ${channel}` + (installApp ? ' b' : '')
726
729
  ]
727
- await cli.executeInShell(command, program.debug)
730
+ // 「出厂安装」事件由 aadensetup.sh 自己在最后上报(带每一步的结果和自检),这里不再重复记
731
+ const {code} = await cli.executeInShell(command, program.debug)
732
+ process.exitCode = code
728
733
  } else {
729
734
  console.log('Device Id is invalid.')
730
735
  }
731
- logAction("出厂安装")
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}`)
732
753
  }
733
754
 
734
755
  function getMyDeviceId() {
@@ -906,7 +927,7 @@ function getPrivilegeCMD(path, pri = '+x', relativePath = true) {
906
927
  if (relativePath) {
907
928
  command.push(goIntoRootDir())
908
929
  }
909
- command.push(`echo "Innerken322." | sudo -S chmod ${pri} ${path}`)
930
+ command.push(`echo "Innerken322." | sudo -S -p "" chmod ${pri} ${path}`)
910
931
  return command
911
932
  }
912
933
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aaden-cli",
3
- "version": "1.6.109-rc.0",
3
+ "version": "1.6.109",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -19,7 +19,7 @@
19
19
  "license": "ISC",
20
20
  "release-it": {
21
21
  "hooks": {
22
- "after:bump": "ncc build index.js -o dist -C"
22
+ "after:bump": "rm -rf dist && ncc build index.js -o dist -C"
23
23
  },
24
24
  "npm": {
25
25
  "publish": false
@@ -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 成为后端唯一更新通道)"
@@ -1,19 +0,0 @@
1
- echo "Innerken322." | sudo -S su
2
- sudo touch /etc/aaden/AbstractDatevCsvFile.php
3
- sudo chmod 777 /etc/aaden/AbstractDatevCsvFile.php
4
- php -r $'print preg_replace(\'/ {4}public function toCsvArray\(\$items\)\r?\n(?:.*\r?\n)+? {4}}/\', <<<\'PHP\'
5
- public function toCsvArray($items)
6
- {
7
- yield $this->getHeader()->toArray($items[\'header\']);
8
-
9
- $fields = $this->getFields();
10
-
11
- yield array_map(Utils::itAccessMethod(\'getName\'), $fields);
12
-
13
- foreach ($items[\'body\'] as $item) {
14
- yield array_map(Utils::itAccessMethod(\'transform\', $item), $fields);
15
- }
16
- }
17
- PHP
18
- , file_get_contents("/var/www/html/PHP/Datev/FileStructures/AbstractDatevCsvFile.php"));' > /etc/aaden/AbstractDatevCsvFile.php
19
- sudo cp /etc/aaden/AbstractDatevCsvFile.php /var/www/html/PHP/Datev/FileStructures/AbstractDatevCsvFile.php
@@ -1,19 +0,0 @@
1
- echo "Innerken322." | sudo -S su
2
- sudo touch /etc/aaden/AbstractDatevCsvFile.php
3
- sudo chmod 777 /etc/aaden/AbstractDatevCsvFile.php
4
- php -r $'print preg_replace(\'/ {4}public function toCsvArray\(\$items\)\r?\n(?:.*\r?\n)+? {4}}/\', <<<\'PHP\'
5
- public function toCsvArray($items)
6
- {
7
- yield $this->getHeader()->toArray($items[\'header\']);
8
-
9
- $fields = $this->getFields();
10
-
11
- yield array_map(Utils::itAccessMethod(\'getName\'), $fields);
12
-
13
- foreach ($items[\'body\'] as $item) {
14
- yield array_map(Utils::itAccessMethod(\'transform\', $item), $fields);
15
- }
16
- }
17
- PHP
18
- , file_get_contents("/var/www/html/PHP/Datev/FileStructures/AbstractDatevCsvFile.php"));' > /etc/aaden/AbstractDatevCsvFile.php
19
- sudo cp /etc/aaden/AbstractDatevCsvFile.php /var/www/html/PHP/Datev/FileStructures/AbstractDatevCsvFile.php