@aaricchen1991/n2-cli 1.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/README.md +92 -0
- package/assets/deploy/deploy.sh +172 -0
- package/assets/deploy/domains.yaml +18 -0
- package/assets/deploy/lib/common.sh +62 -0
- package/assets/deploy/nginx/n2.conf +162 -0
- package/assets/deploy/server-setup.sh +285 -0
- package/assets/deploy/ssl/README.md +320 -0
- package/assets/deploy/ssl/check-and-setup-ssl.sh +222 -0
- package/assets/deploy/ssl/domains.txt +3 -0
- package/assets/deploy/ssl/renew-ssl.sh +236 -0
- package/assets/deploy/ssl/setup-ssl.sh +474 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +186 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +29 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +134 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/config.test.d.ts +2 -0
- package/dist/commands/config.test.d.ts.map +1 -0
- package/dist/commands/config.test.js +215 -0
- package/dist/commands/config.test.js.map +1 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +106 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/init.test.d.ts +2 -0
- package/dist/commands/init.test.d.ts.map +1 -0
- package/dist/commands/init.test.js +70 -0
- package/dist/commands/init.test.js.map +1 -0
- package/dist/commands/nginx.d.ts +10 -0
- package/dist/commands/nginx.d.ts.map +1 -0
- package/dist/commands/nginx.js +72 -0
- package/dist/commands/nginx.js.map +1 -0
- package/dist/commands/nginx.test.d.ts +2 -0
- package/dist/commands/nginx.test.d.ts.map +1 -0
- package/dist/commands/nginx.test.js +75 -0
- package/dist/commands/nginx.test.js.map +1 -0
- package/dist/commands/ssl-logs.d.ts +17 -0
- package/dist/commands/ssl-logs.d.ts.map +1 -0
- package/dist/commands/ssl-logs.js +55 -0
- package/dist/commands/ssl-logs.js.map +1 -0
- package/dist/commands/ssl-logs.test.d.ts +2 -0
- package/dist/commands/ssl-logs.test.d.ts.map +1 -0
- package/dist/commands/ssl-logs.test.js +54 -0
- package/dist/commands/ssl-logs.test.js.map +1 -0
- package/dist/commands/ssl.d.ts +16 -0
- package/dist/commands/ssl.d.ts.map +1 -0
- package/dist/commands/ssl.js +105 -0
- package/dist/commands/ssl.js.map +1 -0
- package/dist/commands/ssl.test.d.ts +2 -0
- package/dist/commands/ssl.test.d.ts.map +1 -0
- package/dist/commands/ssl.test.js +95 -0
- package/dist/commands/ssl.test.js.map +1 -0
- package/dist/lib/config-store.d.ts +14 -0
- package/dist/lib/config-store.d.ts.map +1 -0
- package/dist/lib/config-store.js +111 -0
- package/dist/lib/config-store.js.map +1 -0
- package/dist/lib/config-store.test.d.ts +2 -0
- package/dist/lib/config-store.test.d.ts.map +1 -0
- package/dist/lib/config-store.test.js +173 -0
- package/dist/lib/config-store.test.js.map +1 -0
- package/dist/lib/domains.d.ts +37 -0
- package/dist/lib/domains.d.ts.map +1 -0
- package/dist/lib/domains.js +134 -0
- package/dist/lib/domains.js.map +1 -0
- package/dist/lib/domains.test.d.ts +2 -0
- package/dist/lib/domains.test.d.ts.map +1 -0
- package/dist/lib/domains.test.js +141 -0
- package/dist/lib/domains.test.js.map +1 -0
- package/dist/lib/logger.d.ts +19 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +58 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/nginx.d.ts +7 -0
- package/dist/lib/nginx.d.ts.map +1 -0
- package/dist/lib/nginx.js +86 -0
- package/dist/lib/nginx.js.map +1 -0
- package/dist/lib/nginx.test.d.ts +2 -0
- package/dist/lib/nginx.test.d.ts.map +1 -0
- package/dist/lib/nginx.test.js +46 -0
- package/dist/lib/nginx.test.js.map +1 -0
- package/dist/lib/paths.d.ts +13 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +36 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/paths.test.d.ts +2 -0
- package/dist/lib/paths.test.d.ts.map +1 -0
- package/dist/lib/paths.test.js +52 -0
- package/dist/lib/paths.test.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SSL 证书申请和安装脚本
|
|
3
|
+
# 使用 acme.sh 和阿里云 DNS API 自动申请 Let's Encrypt 证书
|
|
4
|
+
# 使用方法: setup-ssl.sh --domains <域名> [--ali-key ... --ali-secret ...]
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
# 颜色输出
|
|
9
|
+
RED='\033[0;31m'
|
|
10
|
+
GREEN='\033[0;32m'
|
|
11
|
+
YELLOW='\033[1;33m'
|
|
12
|
+
NC='\033[0m' # No Color
|
|
13
|
+
|
|
14
|
+
log() {
|
|
15
|
+
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
error() {
|
|
19
|
+
echo -e "${RED}[ERROR]${NC} $1" >&2
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
warning() {
|
|
23
|
+
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# 检查是否以 root 权限运行
|
|
27
|
+
if [ "$EUID" -ne 0 ]; then
|
|
28
|
+
error "请使用 sudo 运行此脚本"
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# 确保在有效目录下执行,避免 getcwd 报错
|
|
33
|
+
cd /
|
|
34
|
+
|
|
35
|
+
# 要申请证书的域名(通过 --domains 传入,如 --domains n2-admin.cdqxtech.com)
|
|
36
|
+
TARGET_DOMAIN=""
|
|
37
|
+
|
|
38
|
+
# 阿里云 API 凭证
|
|
39
|
+
ALI_KEY=""
|
|
40
|
+
ALI_SECRET=""
|
|
41
|
+
|
|
42
|
+
# Email 地址(用于 ZeroSSL 账户注册,可选)
|
|
43
|
+
ACME_EMAIL=""
|
|
44
|
+
|
|
45
|
+
# 调试模式
|
|
46
|
+
DEBUG_MODE=false
|
|
47
|
+
|
|
48
|
+
# 解析命令行参数
|
|
49
|
+
while [[ $# -gt 0 ]]; do
|
|
50
|
+
case $1 in
|
|
51
|
+
--domains)
|
|
52
|
+
if [ -n "$2" ]; then
|
|
53
|
+
IFS=',' read -ra DOMAIN_ARRAY <<< "$2"
|
|
54
|
+
if [ ${#DOMAIN_ARRAY[@]} -ge 1 ]; then
|
|
55
|
+
TARGET_DOMAIN="${DOMAIN_ARRAY[0]}"
|
|
56
|
+
fi
|
|
57
|
+
shift 2
|
|
58
|
+
else
|
|
59
|
+
shift
|
|
60
|
+
fi
|
|
61
|
+
;;
|
|
62
|
+
--ali-key)
|
|
63
|
+
if [ -n "$2" ]; then
|
|
64
|
+
ALI_KEY="$2"
|
|
65
|
+
shift 2
|
|
66
|
+
else
|
|
67
|
+
shift
|
|
68
|
+
fi
|
|
69
|
+
;;
|
|
70
|
+
--ali-secret)
|
|
71
|
+
if [ -n "$2" ]; then
|
|
72
|
+
ALI_SECRET="$2"
|
|
73
|
+
shift 2
|
|
74
|
+
else
|
|
75
|
+
shift
|
|
76
|
+
fi
|
|
77
|
+
;;
|
|
78
|
+
--email)
|
|
79
|
+
if [ -n "$2" ]; then
|
|
80
|
+
ACME_EMAIL="$2"
|
|
81
|
+
shift 2
|
|
82
|
+
else
|
|
83
|
+
shift
|
|
84
|
+
fi
|
|
85
|
+
;;
|
|
86
|
+
--debug)
|
|
87
|
+
DEBUG_MODE=true
|
|
88
|
+
set -x # 启用 bash 调试模式,显示所有执行的命令
|
|
89
|
+
shift
|
|
90
|
+
;;
|
|
91
|
+
*)
|
|
92
|
+
shift
|
|
93
|
+
;;
|
|
94
|
+
esac
|
|
95
|
+
done
|
|
96
|
+
|
|
97
|
+
log "开始配置 SSL 证书..."
|
|
98
|
+
if [ -z "$TARGET_DOMAIN" ]; then
|
|
99
|
+
error "请通过 --domains 指定要申请证书的域名,例如: --domains n2-admin.cdqxtech.com"
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
log "申请域名: $TARGET_DOMAIN"
|
|
103
|
+
|
|
104
|
+
# 检查阿里云 API 凭证(优先使用参数,其次使用环境变量)
|
|
105
|
+
if [ -z "$ALI_KEY" ]; then
|
|
106
|
+
ALI_KEY="$Ali_Key"
|
|
107
|
+
fi
|
|
108
|
+
if [ -z "$ALI_SECRET" ]; then
|
|
109
|
+
ALI_SECRET="$Ali_Secret"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
if [ -z "$ALI_KEY" ] || [ -z "$ALI_SECRET" ]; then
|
|
113
|
+
error "未设置阿里云 API 凭证"
|
|
114
|
+
error "请通过参数传递:"
|
|
115
|
+
error " --ali-key \"your_access_key_id\" --ali-secret \"your_access_key_secret\""
|
|
116
|
+
error "或者设置环境变量:"
|
|
117
|
+
error " export Ali_Key=\"your_access_key_id\""
|
|
118
|
+
error " export Ali_Secret=\"your_access_key_secret\""
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# 设置环境变量供 acme.sh 使用
|
|
123
|
+
export Ali_Key="$ALI_KEY"
|
|
124
|
+
export Ali_Secret="$ALI_SECRET"
|
|
125
|
+
|
|
126
|
+
# 验证阿里云 API 凭证的函数
|
|
127
|
+
verify_ali_api_credentials() {
|
|
128
|
+
log "验证阿里云 API 凭证..."
|
|
129
|
+
|
|
130
|
+
# 检查凭证是否为空
|
|
131
|
+
if [ -z "$ALI_KEY" ] || [ -z "$ALI_SECRET" ]; then
|
|
132
|
+
error "API 凭证为空"
|
|
133
|
+
return 1
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# 检查凭证格式(AccessKey ID 通常以 LTAI 开头,长度约 16-32 字符)
|
|
137
|
+
if [ ${#ALI_KEY} -lt 10 ] || [ ${#ALI_KEY} -gt 64 ]; then
|
|
138
|
+
warning "AccessKey ID 长度异常,请检查是否正确"
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
if [ ${#ALI_SECRET} -lt 20 ] || [ ${#ALI_SECRET} -gt 64 ]; then
|
|
142
|
+
warning "AccessKey Secret 长度异常,请检查是否正确"
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
log "API 凭证格式检查通过(实际有效性将在证书申请时验证)"
|
|
146
|
+
return 0
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# 检查域名是否在阿里云 DNS 管理的函数
|
|
150
|
+
check_domain_in_aliyun() {
|
|
151
|
+
local domain=$1
|
|
152
|
+
log "检查域名 $domain 是否在阿里云 DNS 管理..."
|
|
153
|
+
|
|
154
|
+
# 提取主域名(例如 tools.qxtool.vip -> qxtool.vip)
|
|
155
|
+
local main_domain=$(echo "$domain" | sed -E 's/^[^.]*\.(.+)$/\1/')
|
|
156
|
+
if [ "$main_domain" = "$domain" ]; then
|
|
157
|
+
main_domain="$domain"
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
log "主域名: $main_domain"
|
|
161
|
+
log "注意:如果域名不在阿里云 DNS 管理,证书申请将失败"
|
|
162
|
+
log "请确保域名已在阿里云 DNS 控制台添加"
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# 检查并处理 Let's Encrypt 速率限制错误
|
|
166
|
+
check_rate_limit_error() {
|
|
167
|
+
local error_output="$1"
|
|
168
|
+
local domain="$2"
|
|
169
|
+
|
|
170
|
+
# 检查是否包含速率限制错误
|
|
171
|
+
if echo "$error_output" | grep -qi "rateLimited\|429\|too many failed authorizations"; then
|
|
172
|
+
error ""
|
|
173
|
+
error "⚠️ 检测到 Let's Encrypt 速率限制错误"
|
|
174
|
+
error ""
|
|
175
|
+
error "原因:在过去的1小时内,域名 $domain 有太多失败的授权尝试(最多5次)"
|
|
176
|
+
error ""
|
|
177
|
+
|
|
178
|
+
# 尝试提取重试时间
|
|
179
|
+
local retry_after=$(echo "$error_output" | grep -i "retry after" | sed -n 's/.*retry after \([^:]*\):.*/\1/p' | head -1)
|
|
180
|
+
if [ -n "$retry_after" ]; then
|
|
181
|
+
error "下次可重试时间:"
|
|
182
|
+
error " UTC: $retry_after UTC"
|
|
183
|
+
|
|
184
|
+
# 尝试转换为本地时间
|
|
185
|
+
local local_time=""
|
|
186
|
+
local wait_info=""
|
|
187
|
+
|
|
188
|
+
# 方法1: 使用 GNU date (Linux)
|
|
189
|
+
if local_time=$(TZ=Asia/Shanghai date -d "$retry_after UTC" +"%Y-%m-%d %H:%M:%S %Z" 2>/dev/null); then
|
|
190
|
+
error " 本地 (CST): $local_time"
|
|
191
|
+
|
|
192
|
+
# 计算剩余等待时间
|
|
193
|
+
local current_timestamp=$(date +%s 2>/dev/null)
|
|
194
|
+
local retry_timestamp=$(date -d "$retry_after UTC" +%s 2>/dev/null)
|
|
195
|
+
if [ -n "$current_timestamp" ] && [ -n "$retry_timestamp" ] && [ $retry_timestamp -gt $current_timestamp ]; then
|
|
196
|
+
local wait_seconds=$((retry_timestamp - current_timestamp))
|
|
197
|
+
local wait_hours=$((wait_seconds / 3600))
|
|
198
|
+
local wait_mins=$(((wait_seconds % 3600) / 60))
|
|
199
|
+
wait_info="${wait_hours}小时${wait_mins}分钟"
|
|
200
|
+
fi
|
|
201
|
+
# 方法2: 使用 BSD date (macOS) - 先转换为时间戳,再加8小时
|
|
202
|
+
elif retry_timestamp=$(date -j -f "%Y-%m-%d %H:%M:%S" "$retry_after" +%s 2>/dev/null); then
|
|
203
|
+
# 加8小时(28800秒)
|
|
204
|
+
local local_timestamp=$((retry_timestamp + 28800))
|
|
205
|
+
if local_time=$(TZ=Asia/Shanghai date -j -f "%s" "$local_timestamp" +"%Y-%m-%d %H:%M:%S %Z" 2>/dev/null); then
|
|
206
|
+
error " 本地 (CST): $local_time"
|
|
207
|
+
|
|
208
|
+
# 计算剩余等待时间
|
|
209
|
+
local current_timestamp=$(date +%s 2>/dev/null)
|
|
210
|
+
if [ -n "$current_timestamp" ] && [ $retry_timestamp -gt $current_timestamp ]; then
|
|
211
|
+
local wait_seconds=$((retry_timestamp - current_timestamp))
|
|
212
|
+
local wait_hours=$((wait_seconds / 3600))
|
|
213
|
+
local wait_mins=$(((wait_seconds % 3600) / 60))
|
|
214
|
+
wait_info="${wait_hours}小时${wait_mins}分钟"
|
|
215
|
+
fi
|
|
216
|
+
fi
|
|
217
|
+
# 方法3: 手动计算(UTC+8)
|
|
218
|
+
else
|
|
219
|
+
local retry_date=$(echo "$retry_after" | awk '{print $1}')
|
|
220
|
+
local retry_time=$(echo "$retry_after" | awk '{print $2}')
|
|
221
|
+
if [ -n "$retry_date" ] && [ -n "$retry_time" ]; then
|
|
222
|
+
local utc_hour=$(echo "$retry_time" | cut -d: -f1 | sed 's/^0//')
|
|
223
|
+
[ -z "$utc_hour" ] && utc_hour=0
|
|
224
|
+
local utc_min=$(echo "$retry_time" | cut -d: -f2)
|
|
225
|
+
local utc_sec=$(echo "$retry_time" | cut -d: -f3)
|
|
226
|
+
|
|
227
|
+
local local_hour=$((utc_hour + 8))
|
|
228
|
+
local local_date="$retry_date"
|
|
229
|
+
if [ $local_hour -ge 24 ]; then
|
|
230
|
+
local_hour=$((local_hour - 24))
|
|
231
|
+
# 简单跨天处理(假设月份不超过31天)
|
|
232
|
+
local year=$(echo "$retry_date" | cut -d- -f1)
|
|
233
|
+
local month=$(echo "$retry_date" | cut -d- -f2)
|
|
234
|
+
local day=$(echo "$retry_date" | cut -d- -f3 | sed 's/^0//')
|
|
235
|
+
[ -z "$day" ] && day=0
|
|
236
|
+
day=$((day + 1))
|
|
237
|
+
local_date=$(printf "%04d-%02d-%02d" $year $month $day)
|
|
238
|
+
fi
|
|
239
|
+
|
|
240
|
+
local local_time_str=$(printf "%02d:%s:%s" $local_hour "$utc_min" "$utc_sec")
|
|
241
|
+
error " 本地 (CST): $local_date $local_time_str CST"
|
|
242
|
+
fi
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
if [ -n "$wait_info" ]; then
|
|
246
|
+
error " 还需等待: $wait_info"
|
|
247
|
+
fi
|
|
248
|
+
error ""
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
error "解决方案:"
|
|
252
|
+
error "1. 等待速率限制解除(通常需要等待1小时)"
|
|
253
|
+
error "2. 检查 DNS 配置是否正确,避免重复失败"
|
|
254
|
+
error "3. 如果急需证书,可以考虑:"
|
|
255
|
+
error " - 使用 Let's Encrypt Staging 环境进行测试(不会触发速率限制)"
|
|
256
|
+
error " - 使用其他 CA(如 ZeroSSL)"
|
|
257
|
+
error ""
|
|
258
|
+
error "使用 Staging 环境测试(不会触发速率限制):"
|
|
259
|
+
error " $0 --domains \"$domain\" --ali-key \"\$Ali_Key\" --ali-secret \"\$Ali_Secret\" --staging"
|
|
260
|
+
error ""
|
|
261
|
+
error "查看详细错误信息:"
|
|
262
|
+
error " tail -100 ~/.acme.sh/acme.sh.log"
|
|
263
|
+
error ""
|
|
264
|
+
return 1
|
|
265
|
+
fi
|
|
266
|
+
|
|
267
|
+
return 0
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# 创建 SSL 证书目录
|
|
271
|
+
log "创建 SSL 证书目录..."
|
|
272
|
+
mkdir -p /etc/nginx/ssl
|
|
273
|
+
chmod 755 /etc/nginx/ssl
|
|
274
|
+
|
|
275
|
+
# 检查并安装 acme.sh
|
|
276
|
+
log "检查 acme.sh 安装..."
|
|
277
|
+
ACME_SH_HOME="$HOME/.acme.sh"
|
|
278
|
+
if [ ! -f "$ACME_SH_HOME/acme.sh" ]; then
|
|
279
|
+
log "安装 acme.sh..."
|
|
280
|
+
curl https://get.acme.sh | sh
|
|
281
|
+
if [ $? -ne 0 ]; then
|
|
282
|
+
error "acme.sh 安装失败"
|
|
283
|
+
exit 1
|
|
284
|
+
fi
|
|
285
|
+
# 重新加载环境变量
|
|
286
|
+
source "$HOME/.bashrc" 2>/dev/null || true
|
|
287
|
+
source "$HOME/.profile" 2>/dev/null || true
|
|
288
|
+
else
|
|
289
|
+
log "acme.sh 已安装"
|
|
290
|
+
fi
|
|
291
|
+
|
|
292
|
+
# 确保 acme.sh 在 PATH 中
|
|
293
|
+
export PATH="$HOME/.acme.sh:$PATH"
|
|
294
|
+
|
|
295
|
+
# 检查 acme.sh 是否可用
|
|
296
|
+
if ! command -v acme.sh >/dev/null 2>&1; then
|
|
297
|
+
# 尝试直接使用完整路径
|
|
298
|
+
if [ -f "$ACME_SH_HOME/acme.sh" ]; then
|
|
299
|
+
alias acme.sh="$ACME_SH_HOME/acme.sh"
|
|
300
|
+
ACME_CMD="$ACME_SH_HOME/acme.sh"
|
|
301
|
+
else
|
|
302
|
+
error "无法找到 acme.sh,请检查安装"
|
|
303
|
+
exit 1
|
|
304
|
+
fi
|
|
305
|
+
else
|
|
306
|
+
ACME_CMD="acme.sh"
|
|
307
|
+
fi
|
|
308
|
+
|
|
309
|
+
# 注册 acme.sh 账户(如果需要)
|
|
310
|
+
# 如果提供了 email,注册账户;否则使用 Let's Encrypt(不需要注册)
|
|
311
|
+
USE_ZEROSSL=false
|
|
312
|
+
if [ -n "$ACME_EMAIL" ]; then
|
|
313
|
+
log "注册 acme.sh 账户(使用 ZeroSSL)..."
|
|
314
|
+
if [ -f "$ACME_SH_HOME/acme.sh" ]; then
|
|
315
|
+
if "$ACME_SH_HOME/acme.sh" --register-account -m "$ACME_EMAIL"; then
|
|
316
|
+
USE_ZEROSSL=true
|
|
317
|
+
log "ZeroSSL 账户注册成功"
|
|
318
|
+
else
|
|
319
|
+
warning "账户注册失败,将使用 Let's Encrypt"
|
|
320
|
+
USE_ZEROSSL=false
|
|
321
|
+
fi
|
|
322
|
+
else
|
|
323
|
+
if $ACME_CMD --register-account -m "$ACME_EMAIL"; then
|
|
324
|
+
USE_ZEROSSL=true
|
|
325
|
+
log "ZeroSSL 账户注册成功"
|
|
326
|
+
else
|
|
327
|
+
warning "账户注册失败,将使用 Let's Encrypt"
|
|
328
|
+
USE_ZEROSSL=false
|
|
329
|
+
fi
|
|
330
|
+
fi
|
|
331
|
+
fi
|
|
332
|
+
|
|
333
|
+
# 如果没有使用 ZeroSSL,明确设置使用 Let's Encrypt CA
|
|
334
|
+
if [ "$USE_ZEROSSL" = false ]; then
|
|
335
|
+
log "使用 Let's Encrypt CA(不需要账户注册)..."
|
|
336
|
+
# 明确设置使用 Let's Encrypt CA
|
|
337
|
+
if [ -f "$ACME_SH_HOME/acme.sh" ]; then
|
|
338
|
+
"$ACME_SH_HOME/acme.sh" --set-default-ca --server letsencrypt || true
|
|
339
|
+
else
|
|
340
|
+
$ACME_CMD --set-default-ca --server letsencrypt || true
|
|
341
|
+
fi
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
# 验证 API 凭证
|
|
345
|
+
verify_ali_api_credentials || {
|
|
346
|
+
error "API 凭证验证失败"
|
|
347
|
+
exit 1
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
# 检查域名
|
|
351
|
+
check_domain_in_aliyun "$TARGET_DOMAIN"
|
|
352
|
+
|
|
353
|
+
# 申请 Tools 域名证书
|
|
354
|
+
log "申请 $TARGET_DOMAIN 证书..."
|
|
355
|
+
ISSUE_CMD_ARGS="--dns dns_ali -d $TARGET_DOMAIN"
|
|
356
|
+
if [ "$USE_ZEROSSL" = false ]; then
|
|
357
|
+
ISSUE_CMD_ARGS="$ISSUE_CMD_ARGS --server letsencrypt"
|
|
358
|
+
fi
|
|
359
|
+
|
|
360
|
+
# 添加调试选项
|
|
361
|
+
if [ "$DEBUG_MODE" = true ]; then
|
|
362
|
+
ISSUE_CMD_ARGS="$ISSUE_CMD_ARGS --debug"
|
|
363
|
+
log "启用调试模式"
|
|
364
|
+
fi
|
|
365
|
+
|
|
366
|
+
# 添加日志选项以便查看详细错误
|
|
367
|
+
ISSUE_CMD_ARGS="$ISSUE_CMD_ARGS --log"
|
|
368
|
+
|
|
369
|
+
log "执行命令: $ACME_CMD --issue $ISSUE_CMD_ARGS"
|
|
370
|
+
log "如果失败,请查看日志: tail -f ~/.acme.sh/acme.sh.log"
|
|
371
|
+
|
|
372
|
+
# 捕获错误输出
|
|
373
|
+
ERROR_OUTPUT=""
|
|
374
|
+
if [ -f "$ACME_SH_HOME/acme.sh" ]; then
|
|
375
|
+
if ! ERROR_OUTPUT=$("$ACME_SH_HOME/acme.sh" --issue $ISSUE_CMD_ARGS 2>&1); then
|
|
376
|
+
# 检查是否是速率限制错误
|
|
377
|
+
if ! check_rate_limit_error "$ERROR_OUTPUT" "$TARGET_DOMAIN"; then
|
|
378
|
+
exit 1
|
|
379
|
+
fi
|
|
380
|
+
|
|
381
|
+
error "$TARGET_DOMAIN 证书申请失败"
|
|
382
|
+
error ""
|
|
383
|
+
error "可能的原因:"
|
|
384
|
+
error "1. 阿里云 API 凭证无效或权限不足"
|
|
385
|
+
error "2. 域名 $TARGET_DOMAIN 不在阿里云 DNS 管理"
|
|
386
|
+
error "3. RAM 用户缺少 DNS 管理权限(需要 AliyunDNSFullAccess 或自定义权限)"
|
|
387
|
+
error "4. 网络连接问题"
|
|
388
|
+
error ""
|
|
389
|
+
error "排查步骤:"
|
|
390
|
+
error "1. 检查 API 凭证是否正确: echo \$Ali_Key"
|
|
391
|
+
error "2. 确认域名在阿里云 DNS 控制台已添加"
|
|
392
|
+
error "3. 检查 RAM 用户权限是否包含 alidns:AddDomainRecord"
|
|
393
|
+
error "4. 查看详细日志: tail -100 ~/.acme.sh/acme.sh.log"
|
|
394
|
+
error "5. 使用调试模式重新运行: $0 --debug --ali-key \"\$Ali_Key\" --ali-secret \"\$Ali_Secret\""
|
|
395
|
+
exit 1
|
|
396
|
+
fi
|
|
397
|
+
else
|
|
398
|
+
if ! ERROR_OUTPUT=$($ACME_CMD --issue $ISSUE_CMD_ARGS 2>&1); then
|
|
399
|
+
# 检查是否是速率限制错误
|
|
400
|
+
if ! check_rate_limit_error "$ERROR_OUTPUT" "$TARGET_DOMAIN"; then
|
|
401
|
+
exit 1
|
|
402
|
+
fi
|
|
403
|
+
|
|
404
|
+
error "$TARGET_DOMAIN 证书申请失败"
|
|
405
|
+
error ""
|
|
406
|
+
error "可能的原因:"
|
|
407
|
+
error "1. 阿里云 API 凭证无效或权限不足"
|
|
408
|
+
error "2. 域名 $TARGET_DOMAIN 不在阿里云 DNS 管理"
|
|
409
|
+
error "3. RAM 用户缺少 DNS 管理权限(需要 AliyunDNSFullAccess 或自定义权限)"
|
|
410
|
+
error "4. 网络连接问题"
|
|
411
|
+
error ""
|
|
412
|
+
error "排查步骤:"
|
|
413
|
+
error "1. 检查 API 凭证是否正确: echo \$Ali_Key"
|
|
414
|
+
error "2. 确认域名在阿里云 DNS 控制台已添加"
|
|
415
|
+
error "3. 检查 RAM 用户权限是否包含 alidns:AddDomainRecord"
|
|
416
|
+
error "4. 查看详细日志: tail -100 ~/.acme.sh/acme.sh.log"
|
|
417
|
+
error "5. 使用调试模式重新运行: $0 --debug --ali-key \"\$Ali_Key\" --ali-secret \"\$Ali_Secret\""
|
|
418
|
+
exit 1
|
|
419
|
+
fi
|
|
420
|
+
fi
|
|
421
|
+
|
|
422
|
+
log "$TARGET_DOMAIN 证书申请成功"
|
|
423
|
+
|
|
424
|
+
# 安装 Tools 域名证书到 nginx
|
|
425
|
+
log "安装 $TARGET_DOMAIN 证书到 nginx..."
|
|
426
|
+
if [ -f "$ACME_SH_HOME/acme.sh" ]; then
|
|
427
|
+
"$ACME_SH_HOME/acme.sh" --install-cert \
|
|
428
|
+
-d "$TARGET_DOMAIN" \
|
|
429
|
+
--key-file /etc/nginx/ssl/${TARGET_DOMAIN}.key \
|
|
430
|
+
--fullchain-file /etc/nginx/ssl/${TARGET_DOMAIN}.crt \
|
|
431
|
+
--reloadcmd "systemctl reload nginx || true"
|
|
432
|
+
else
|
|
433
|
+
$ACME_CMD --install-cert \
|
|
434
|
+
-d "$TARGET_DOMAIN" \
|
|
435
|
+
--key-file /etc/nginx/ssl/${TARGET_DOMAIN}.key \
|
|
436
|
+
--fullchain-file /etc/nginx/ssl/${TARGET_DOMAIN}.crt \
|
|
437
|
+
--reloadcmd "systemctl reload nginx || true"
|
|
438
|
+
fi
|
|
439
|
+
|
|
440
|
+
# 设置证书文件权限
|
|
441
|
+
chmod 600 /etc/nginx/ssl/${TARGET_DOMAIN}.key
|
|
442
|
+
chmod 644 /etc/nginx/ssl/${TARGET_DOMAIN}.crt
|
|
443
|
+
chown root:root /etc/nginx/ssl/${TARGET_DOMAIN}.key
|
|
444
|
+
chown root:root /etc/nginx/ssl/${TARGET_DOMAIN}.crt
|
|
445
|
+
|
|
446
|
+
log "$TARGET_DOMAIN 证书安装完成"
|
|
447
|
+
|
|
448
|
+
# 测试 nginx 配置
|
|
449
|
+
log "测试 nginx 配置..."
|
|
450
|
+
if nginx -t; then
|
|
451
|
+
log "Nginx 配置验证成功"
|
|
452
|
+
# 重新加载 nginx
|
|
453
|
+
if systemctl is-active --quiet nginx 2>/dev/null; then
|
|
454
|
+
log "重新加载 Nginx..."
|
|
455
|
+
systemctl reload nginx || warning "Nginx 重新加载失败,请手动检查"
|
|
456
|
+
else
|
|
457
|
+
warning "Nginx 未运行,请手动启动: sudo systemctl start nginx"
|
|
458
|
+
fi
|
|
459
|
+
else
|
|
460
|
+
error "Nginx 配置验证失败,请检查配置文件"
|
|
461
|
+
exit 1
|
|
462
|
+
fi
|
|
463
|
+
|
|
464
|
+
log ""
|
|
465
|
+
log "SSL 证书配置完成!"
|
|
466
|
+
log ""
|
|
467
|
+
log "证书文件位置:"
|
|
468
|
+
log " - $TARGET_DOMAIN: /etc/nginx/ssl/${TARGET_DOMAIN}.crt"
|
|
469
|
+
log ""
|
|
470
|
+
log "注意:"
|
|
471
|
+
log " - acme.sh 已自动配置 cron 任务,证书将自动续期"
|
|
472
|
+
log " - 证书续期后会自动重新加载 nginx"
|
|
473
|
+
log " - 可以通过 'crontab -l' 查看续期任务"
|
|
474
|
+
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;GAGG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* n2-deploy CLI: server init, nginx config, SSL management.
|
|
4
|
+
* Config: default ~/.deploy/config.yaml (ALIYUN keys + domains).
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import consola from "consola";
|
|
8
|
+
import { runConfigDelete, runConfigGet, runConfigInit, runConfigList, runConfigSet, } from "./commands/config.js";
|
|
9
|
+
import { runInit } from "./commands/init.js";
|
|
10
|
+
import { runNginx } from "./commands/nginx.js";
|
|
11
|
+
import { runSslLogs } from "./commands/ssl-logs.js";
|
|
12
|
+
import { runSsl } from "./commands/ssl.js";
|
|
13
|
+
import { getDefaultConfigPath } from "./lib/paths.js";
|
|
14
|
+
const defaultConfig = getDefaultConfigPath();
|
|
15
|
+
const program = new Command();
|
|
16
|
+
program
|
|
17
|
+
.name("n2-deploy")
|
|
18
|
+
.description("Server init, nginx config generation, and SSL certificate management")
|
|
19
|
+
.version("0.1.0");
|
|
20
|
+
const configCmd = program
|
|
21
|
+
.command("config")
|
|
22
|
+
.description("配置管理:初始化、查看、设置、删除配置项")
|
|
23
|
+
.option("-c, --config <path>", "配置文件路径(默认 ~/.deploy/config.yaml)", defaultConfig);
|
|
24
|
+
function configPathFromCmd(cmd) {
|
|
25
|
+
const selfOpts = cmd.opts?.() ?? {};
|
|
26
|
+
const parentOpts = cmd.parent?.opts?.() ?? {};
|
|
27
|
+
return (selfOpts.config ??
|
|
28
|
+
parentOpts.config ??
|
|
29
|
+
defaultConfig);
|
|
30
|
+
}
|
|
31
|
+
configCmd
|
|
32
|
+
.command("init")
|
|
33
|
+
.description("初始化配置文件(创建 ~/.deploy/config.yaml 默认模板)")
|
|
34
|
+
.option("-c, --config <path>", "配置文件路径", defaultConfig)
|
|
35
|
+
.option("-f, --force", "已存在时覆盖")
|
|
36
|
+
.action(async (opts, cmd) => {
|
|
37
|
+
const configPath = opts.config ?? configPathFromCmd(cmd);
|
|
38
|
+
try {
|
|
39
|
+
await runConfigInit({ config: configPath, force: opts.force });
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
consola.error(e);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
configCmd
|
|
47
|
+
.command("list")
|
|
48
|
+
.description("列出所有配置项")
|
|
49
|
+
.option("-c, --config <path>", "配置文件路径", defaultConfig)
|
|
50
|
+
.action(async (opts, cmd) => {
|
|
51
|
+
const configPath = opts?.config ?? configPathFromCmd(cmd);
|
|
52
|
+
try {
|
|
53
|
+
await runConfigList({ config: configPath });
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
consola.error(e);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
configCmd
|
|
61
|
+
.command("get [key]")
|
|
62
|
+
.description("查看配置项(不传 key 则列出全部)。key: aliyun_access_key_id, api.backend_port, admin.domains 等")
|
|
63
|
+
.option("-c, --config <path>", "配置文件路径", defaultConfig)
|
|
64
|
+
.action(async (key, opts, cmd) => {
|
|
65
|
+
const configPath = opts?.config ?? configPathFromCmd(cmd);
|
|
66
|
+
try {
|
|
67
|
+
await runConfigGet({ config: configPath, key });
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
consola.error(e);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
configCmd
|
|
75
|
+
.command("set <key> [values...]")
|
|
76
|
+
.description("设置配置项。domains 类可传多个值,如: config set admin.domains a.com b.com")
|
|
77
|
+
.option("-c, --config <path>", "配置文件路径", defaultConfig)
|
|
78
|
+
.action(async (key, values, opts, cmd) => {
|
|
79
|
+
const configPath = opts?.config ?? configPathFromCmd(cmd);
|
|
80
|
+
try {
|
|
81
|
+
await runConfigSet({
|
|
82
|
+
config: configPath,
|
|
83
|
+
key,
|
|
84
|
+
values: Array.isArray(values) ? values : values ? [values] : [],
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
consola.error(e);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
configCmd
|
|
93
|
+
.command("delete <key>")
|
|
94
|
+
.description("删除/清空配置项(domains 清空列表,密钥置空)")
|
|
95
|
+
.option("-c, --config <path>", "配置文件路径", defaultConfig)
|
|
96
|
+
.action(async (key, opts, cmd) => {
|
|
97
|
+
const configPath = opts?.config ?? configPathFromCmd(cmd);
|
|
98
|
+
try {
|
|
99
|
+
await runConfigDelete({ config: configPath, key });
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
consola.error(e);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
program
|
|
107
|
+
.command("init")
|
|
108
|
+
.description("初始化服务器(幂等):nginx、目录、deploy.sh、占位证书")
|
|
109
|
+
.option("-c, --config <path>", "配置文件路径(默认 ~/.deploy/config.yaml)", defaultConfig)
|
|
110
|
+
.option("--scripts-dir <path>", "部署脚本目录(默认 N2_DEPLOY_SCRIPTS_DIR 或包内 assets/deploy)")
|
|
111
|
+
.action(async (opts) => {
|
|
112
|
+
try {
|
|
113
|
+
await runInit({ config: opts.config, scriptsDir: opts.scriptsDir });
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
consola.error(e);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
program
|
|
121
|
+
.command("nginx")
|
|
122
|
+
.description("Generate nginx config from domains config; optionally install and reload")
|
|
123
|
+
.option("-c, --config <path>", "Path to deploy config (default: ~/.deploy/config.yaml)", defaultConfig)
|
|
124
|
+
.option("-o, --output <path>", "Output path for nginx config", "nginx/n2.conf")
|
|
125
|
+
.option("-i, --install", "Install to system nginx dir and reload")
|
|
126
|
+
.action(async (opts) => {
|
|
127
|
+
try {
|
|
128
|
+
await runNginx({
|
|
129
|
+
config: opts.config,
|
|
130
|
+
output: opts.output,
|
|
131
|
+
install: opts.install,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
consola.error(e);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
program
|
|
140
|
+
.command("ssl")
|
|
141
|
+
.description("Setup/renew SSL certificates for all domains (acme.sh + Aliyun DNS)")
|
|
142
|
+
.option("-c, --config <path>", "Path to deploy config (default: ~/.deploy/config.yaml)", defaultConfig)
|
|
143
|
+
.option("--ali-key <key>", "Aliyun Access Key ID (or set ALIYUN_ACCESS_KEY_ID)")
|
|
144
|
+
.option("--ali-secret <secret>", "Aliyun Access Key Secret (or set ALIYUN_ACCESS_KEY_SECRET)")
|
|
145
|
+
.option("--log-file <path>", "SSL log file path", "/opt/deploy/logs/ssl.log")
|
|
146
|
+
.option("--scripts-dir <path>", "Path to deploy scripts")
|
|
147
|
+
.action(async (opts) => {
|
|
148
|
+
try {
|
|
149
|
+
await runSsl({
|
|
150
|
+
config: opts.config,
|
|
151
|
+
aliKey: opts.aliKey,
|
|
152
|
+
aliSecret: opts.aliSecret,
|
|
153
|
+
logFile: opts.logFile,
|
|
154
|
+
scriptsDir: opts.scriptsDir,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
consola.error(e);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
program
|
|
163
|
+
.command("ssl-logs")
|
|
164
|
+
.description("View SSL log (create, renew, success, failure)")
|
|
165
|
+
.option("--log-file <path>", "SSL log file path", "/opt/deploy/logs/ssl.log")
|
|
166
|
+
.option("-n, --lines <n>", "Number of lines to show", "50")
|
|
167
|
+
.option("-f, --follow", "Follow log (tail -f)")
|
|
168
|
+
.option("--action <action>", "Filter by action: create | renew | success | failure | run")
|
|
169
|
+
.option("--result <result>", "Filter by result: ok | fail")
|
|
170
|
+
.action(async (opts) => {
|
|
171
|
+
try {
|
|
172
|
+
await runSslLogs({
|
|
173
|
+
logFile: opts.logFile,
|
|
174
|
+
lines: parseInt(opts.lines, 10) || 50,
|
|
175
|
+
follow: opts.follow,
|
|
176
|
+
action: opts.action,
|
|
177
|
+
result: opts.result,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
consola.error(e);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
program.parse();
|
|
186
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|