@dazhicheng/common 1.0.45 → 1.0.47
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/.env.test +16 -16
- package/.gitignore +8 -8
- package/.gitlab-ci.yml +32 -26
- package/.husky/commit-msg +1 -1
- package/.husky/pre-commit +3 -3
- package/.prettierrc.txt +21 -21
- package/.stylelintignore +7 -7
- package/deploy-docker.sh +271 -0
- package/docker-entrypoint.sh +21 -21
- package/eslint.config.mjs.txt +94 -94
- package/nginx_dev.conf +85 -85
- package/nginx_test.conf +87 -87
- package/package.json +2 -1
- package/pnpm-workspace.yaml +9 -9
- package/src/types/index.d.ts +1 -0
- package/sync-common.mjs +1 -0
package/.env.test
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
# 【测试】环境变量
|
|
2
|
-
|
|
3
|
-
# 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/)
|
|
4
|
-
VITE_BASE_URL = /
|
|
5
|
-
|
|
6
|
-
# API 地址前缀
|
|
7
|
-
VITE_API_URL = test-api
|
|
8
|
-
|
|
9
|
-
# Delete console
|
|
10
|
-
VITE_DROP_CONSOLE = true
|
|
11
|
-
|
|
12
|
-
# 飞书扫码登录回调地址
|
|
13
|
-
VITE_FEISHU_REDIRECT_URI = http://fulfillment-qd.tiantai-jp.com:23006/feishuOAuth
|
|
14
|
-
|
|
15
|
-
# webSocket地址
|
|
16
|
-
VITE_WEBSOCKET_URL = ws://fulfillment-test.tiantai-jp.com:11000
|
|
1
|
+
# 【测试】环境变量
|
|
2
|
+
|
|
3
|
+
# 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/)
|
|
4
|
+
VITE_BASE_URL = /
|
|
5
|
+
|
|
6
|
+
# API 地址前缀
|
|
7
|
+
VITE_API_URL = test-api
|
|
8
|
+
|
|
9
|
+
# Delete console
|
|
10
|
+
VITE_DROP_CONSOLE = true
|
|
11
|
+
|
|
12
|
+
# 飞书扫码登录回调地址
|
|
13
|
+
VITE_FEISHU_REDIRECT_URI = http://fulfillment-qd.tiantai-jp.com:23006/feishuOAuth
|
|
14
|
+
|
|
15
|
+
# webSocket地址
|
|
16
|
+
VITE_WEBSOCKET_URL = ws://fulfillment-test.tiantai-jp.com:11000
|
package/.gitignore
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
node_modules
|
|
2
|
-
.DS_Store
|
|
3
|
-
dist
|
|
4
|
-
dist-ssr
|
|
5
|
-
*.local
|
|
6
|
-
.cursorrules
|
|
7
|
-
.vite
|
|
8
|
-
.claude
|
|
1
|
+
node_modules
|
|
2
|
+
.DS_Store
|
|
3
|
+
dist
|
|
4
|
+
dist-ssr
|
|
5
|
+
*.local
|
|
6
|
+
.cursorrules
|
|
7
|
+
.vite
|
|
8
|
+
.claude
|
package/.gitlab-ci.yml
CHANGED
|
@@ -38,6 +38,7 @@ variables:
|
|
|
38
38
|
- dist/
|
|
39
39
|
- nginx_*.conf
|
|
40
40
|
- docker-entrypoint.sh
|
|
41
|
+
- deploy-docker.sh
|
|
41
42
|
- Dockerfile
|
|
42
43
|
expire_in: 1 day
|
|
43
44
|
|
|
@@ -57,29 +58,20 @@ variables:
|
|
|
57
58
|
- dist-test/
|
|
58
59
|
- nginx_*.conf
|
|
59
60
|
- docker-entrypoint.sh
|
|
61
|
+
- deploy-docker.sh
|
|
60
62
|
- Dockerfile
|
|
61
63
|
expire_in: 1 day
|
|
62
64
|
|
|
63
65
|
# 部署锚点(通用脚本直接写在这里,不需要项目覆盖) -------------------------
|
|
64
66
|
# 使用 named volume 持久化静态资源:发版时合并新构建并保留旧 hashed 文件,
|
|
65
67
|
# 使已打开的旧页面仍能懒加载到旧入口资源。
|
|
66
|
-
#
|
|
68
|
+
# 热更新逻辑见 deploy-docker.sh(sync-common 同步到各 *-web)。
|
|
67
69
|
.deploy_template: &deploy_template
|
|
68
70
|
stage: deploy
|
|
69
71
|
image: docker:26.1.4
|
|
70
72
|
script:
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
rm -rf dist
|
|
74
|
-
mv "dist-${NODE_ENV}" dist
|
|
75
|
-
fi
|
|
76
|
-
- docker volume create ${IMAGE_NAME}-assets || true
|
|
77
|
-
- docker build --build-arg NODE_ENV=$NODE_ENV -t $IMAGE_NAME:latest .
|
|
78
|
-
- docker stop $IMAGE_NAME || true
|
|
79
|
-
- docker rm $IMAGE_NAME || true
|
|
80
|
-
- >
|
|
81
|
-
docker run -d --name $IMAGE_NAME --restart always -p $HOST_PORT:$CONTAINER_PORT -v ${IMAGE_NAME}-assets:/usr/share/nginx/html -e ASSET_RETENTION_DAYS=14 $IMAGE_NAME:latest
|
|
82
|
-
|
|
73
|
+
- chmod +x deploy-docker.sh
|
|
74
|
+
- ./deploy-docker.sh
|
|
83
75
|
- docker image prune -f || true
|
|
84
76
|
|
|
85
77
|
# MR 验证 -----------------------------------------------------------------
|
|
@@ -136,27 +128,41 @@ MR 构建验证:
|
|
|
136
128
|
# <<<开发环境发版_107_NO>>>
|
|
137
129
|
# only: [develop]
|
|
138
130
|
|
|
139
|
-
|
|
131
|
+
# develop:双构建产物 dist-test;构建失败则不发版(needs 非 optional)
|
|
132
|
+
测试环境发版_develop:
|
|
133
|
+
<<: *deploy_template
|
|
134
|
+
needs: [构建_develop]
|
|
135
|
+
tags: [test]
|
|
136
|
+
resource_group: deploy-test
|
|
137
|
+
variables:
|
|
138
|
+
NODE_ENV: test
|
|
139
|
+
<<<测试环境发版>>>
|
|
140
|
+
only: [develop]
|
|
141
|
+
|
|
142
|
+
# test:单构建产物 dist/;构建失败则不发版
|
|
143
|
+
测试环境发版_test:
|
|
140
144
|
<<: *deploy_template
|
|
141
|
-
needs:
|
|
142
|
-
- job: 构建_develop
|
|
143
|
-
optional: true
|
|
144
|
-
- job: 构建_test
|
|
145
|
-
optional: true
|
|
145
|
+
needs: [构建_test]
|
|
146
146
|
tags: [test]
|
|
147
147
|
resource_group: deploy-test
|
|
148
148
|
variables:
|
|
149
149
|
NODE_ENV: test
|
|
150
150
|
<<<测试环境发版>>>
|
|
151
|
-
only: [test
|
|
151
|
+
only: [test]
|
|
152
|
+
|
|
153
|
+
# 测试环境发版_107_develop:
|
|
154
|
+
# <<: *deploy_template
|
|
155
|
+
# needs: [构建_develop]
|
|
156
|
+
# tags: [test_107]
|
|
157
|
+
# resource_group: deploy-test_107
|
|
158
|
+
# variables:
|
|
159
|
+
# NODE_ENV: test
|
|
160
|
+
# <<<测试环境发版_107_NO>>>
|
|
161
|
+
# only: [develop]
|
|
152
162
|
|
|
153
|
-
# 测试环境发版
|
|
163
|
+
# 测试环境发版_107_test:
|
|
154
164
|
# <<: *deploy_template
|
|
155
|
-
# needs:
|
|
156
|
-
# - job: 构建_develop
|
|
157
|
-
# optional: true
|
|
158
|
-
# - job: 构建_test
|
|
159
|
-
# optional: true
|
|
165
|
+
# needs: [构建_test]
|
|
160
166
|
# tags: [test_107]
|
|
161
167
|
# resource_group: deploy-test_107
|
|
162
168
|
# variables:
|
package/.husky/commit-msg
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#!/usr/bin/env sh
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
2
|
pnpm exec commitlint < "$1"
|
package/.husky/pre-commit
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env sh
|
|
2
|
-
pnpm run typecheck
|
|
3
|
-
pnpm run lint:lint-staged
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
pnpm run typecheck
|
|
3
|
+
pnpm run lint:lint-staged
|
package/.prettierrc.txt
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
{
|
|
2
|
-
"printWidth": 100,
|
|
3
|
-
"tabWidth": 2,
|
|
4
|
-
"useTabs": false,
|
|
5
|
-
"semi": true,
|
|
6
|
-
"vueIndentScriptAndStyle": true,
|
|
7
|
-
"singleQuote": true,
|
|
8
|
-
"quoteProps": "as-needed",
|
|
9
|
-
"bracketSpacing": true,
|
|
10
|
-
"trailingComma": "all",
|
|
11
|
-
"bracketSameLine": false,
|
|
12
|
-
"jsxSingleQuote": false,
|
|
13
|
-
"arrowParens": "always",
|
|
14
|
-
"insertPragma": false,
|
|
15
|
-
"requirePragma": false,
|
|
16
|
-
"proseWrap": "never",
|
|
17
|
-
"htmlWhitespaceSensitivity": "strict",
|
|
18
|
-
"endOfLine": "auto",
|
|
19
|
-
"rangeStart": 0,
|
|
20
|
-
<<<自定义>>>
|
|
21
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"printWidth": 100,
|
|
3
|
+
"tabWidth": 2,
|
|
4
|
+
"useTabs": false,
|
|
5
|
+
"semi": true,
|
|
6
|
+
"vueIndentScriptAndStyle": true,
|
|
7
|
+
"singleQuote": true,
|
|
8
|
+
"quoteProps": "as-needed",
|
|
9
|
+
"bracketSpacing": true,
|
|
10
|
+
"trailingComma": "all",
|
|
11
|
+
"bracketSameLine": false,
|
|
12
|
+
"jsxSingleQuote": false,
|
|
13
|
+
"arrowParens": "always",
|
|
14
|
+
"insertPragma": false,
|
|
15
|
+
"requirePragma": false,
|
|
16
|
+
"proseWrap": "never",
|
|
17
|
+
"htmlWhitespaceSensitivity": "strict",
|
|
18
|
+
"endOfLine": "auto",
|
|
19
|
+
"rangeStart": 0,
|
|
20
|
+
<<<自定义>>>
|
|
21
|
+
}
|
package/.stylelintignore
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
dist
|
|
2
|
-
node_modules
|
|
3
|
-
public
|
|
4
|
-
.husky
|
|
5
|
-
.vscode
|
|
6
|
-
src/components/Layout/MenuLeft/index.vue
|
|
7
|
-
src/assets
|
|
1
|
+
dist
|
|
2
|
+
node_modules
|
|
3
|
+
public
|
|
4
|
+
.husky
|
|
5
|
+
.vscode
|
|
6
|
+
src/components/Layout/MenuLeft/index.vue
|
|
7
|
+
src/assets
|
|
8
8
|
stats.html
|
package/deploy-docker.sh
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# 静态 SPA Docker 热更新部署:合并 volume、按需 reload nginx / 重建容器。
|
|
3
|
+
# 由 GitLab CI deploy job 调用;依赖环境变量见下方「必需变量」。
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
# --- 必需变量(由 CI job variables 注入)---
|
|
7
|
+
: "${IMAGE_NAME:?IMAGE_NAME is required}"
|
|
8
|
+
: "${NODE_ENV:?NODE_ENV is required}"
|
|
9
|
+
: "${HOST_PORT:?HOST_PORT is required}"
|
|
10
|
+
: "${CONTAINER_PORT:?CONTAINER_PORT is required}"
|
|
11
|
+
|
|
12
|
+
ASSET_RETENTION_DAYS="${ASSET_RETENTION_DAYS:-14}"
|
|
13
|
+
|
|
14
|
+
# 构建产物校验(须在 dist-${NODE_ENV} 还原为 dist/ 之前)
|
|
15
|
+
if [ -d "dist-${NODE_ENV}" ]; then
|
|
16
|
+
test -f "dist-${NODE_ENV}/index.html" || {
|
|
17
|
+
echo "missing dist-${NODE_ENV}/index.html, abort deploy" >&2
|
|
18
|
+
exit 1
|
|
19
|
+
}
|
|
20
|
+
else
|
|
21
|
+
test -f dist/index.html || {
|
|
22
|
+
echo "missing dist/index.html, abort deploy" >&2
|
|
23
|
+
exit 1
|
|
24
|
+
}
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
if [ -d "dist-${NODE_ENV}" ]; then
|
|
28
|
+
rm -rf dist
|
|
29
|
+
mv "dist-${NODE_ENV}" dist
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
ASSETS_VOL="${IMAGE_NAME}-assets"
|
|
33
|
+
docker volume create "$ASSETS_VOL" || true
|
|
34
|
+
docker build --build-arg NODE_ENV="$NODE_ENV" -t "${IMAGE_NAME}:latest" .
|
|
35
|
+
|
|
36
|
+
# 合并静态资源到持久卷(entrypoint 执行 cp/find 后 CMD=true 退出,不停正在跑的 nginx)
|
|
37
|
+
docker run --rm \
|
|
38
|
+
-v "${ASSETS_VOL}:/usr/share/nginx/html" \
|
|
39
|
+
-e "ASSET_RETENTION_DAYS=${ASSET_RETENTION_DAYS}" \
|
|
40
|
+
"${IMAGE_NAME}:latest" \
|
|
41
|
+
true
|
|
42
|
+
|
|
43
|
+
file_hash_from_image() {
|
|
44
|
+
_path="$1"
|
|
45
|
+
_cid=$(docker create "${IMAGE_NAME}:latest")
|
|
46
|
+
docker cp "$_cid:$_path" /tmp/img-file-hash.bin
|
|
47
|
+
docker rm -f "$_cid" >/dev/null
|
|
48
|
+
sha256sum /tmp/img-file-hash.bin | awk '{print $1}'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
file_hash_from_container() {
|
|
52
|
+
_path="$1"
|
|
53
|
+
docker cp "$IMAGE_NAME:$_path" /tmp/ctr-file-hash.bin 2>/dev/null || return 1
|
|
54
|
+
sha256sum /tmp/ctr-file-hash.bin | awk '{print $1}'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# 热更新 default.conf:先备份,nginx -t 失败则还原,避免坏配置落盘导致重启起不来
|
|
58
|
+
sync_nginx_conf() {
|
|
59
|
+
_new=$(file_hash_from_image /etc/nginx/conf.d/default.conf)
|
|
60
|
+
_old=$(file_hash_from_container /etc/nginx/conf.d/default.conf || true)
|
|
61
|
+
if [ -z "$_old" ] || [ "$_new" = "$_old" ]; then
|
|
62
|
+
return 0
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
_cid=$(docker create "${IMAGE_NAME}:latest")
|
|
66
|
+
docker cp "$_cid:/etc/nginx/conf.d/default.conf" /tmp/nginx-default-new.conf
|
|
67
|
+
docker rm -f "$_cid" >/dev/null
|
|
68
|
+
|
|
69
|
+
docker exec "$IMAGE_NAME" sh -c \
|
|
70
|
+
'cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak'
|
|
71
|
+
docker cp /tmp/nginx-default-new.conf "$IMAGE_NAME:/etc/nginx/conf.d/default.conf"
|
|
72
|
+
if ! docker exec "$IMAGE_NAME" nginx -t; then
|
|
73
|
+
docker exec "$IMAGE_NAME" sh -c \
|
|
74
|
+
'mv /etc/nginx/conf.d/default.conf.bak /etc/nginx/conf.d/default.conf'
|
|
75
|
+
echo "nginx -t failed, restored previous default.conf" >&2
|
|
76
|
+
return 1
|
|
77
|
+
fi
|
|
78
|
+
docker exec "$IMAGE_NAME" sh -c 'rm -f /etc/nginx/conf.d/default.conf.bak'
|
|
79
|
+
docker exec "$IMAGE_NAME" nginx -s reload
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
need_recreate() {
|
|
83
|
+
if [ "${FORCE_RECREATE:-0}" = "1" ]; then
|
|
84
|
+
echo "FORCE_RECREATE=1, will recreate container"
|
|
85
|
+
return 0
|
|
86
|
+
fi
|
|
87
|
+
# 端口映射变化(HostConfig 在 stopped 时仍可读)
|
|
88
|
+
_bound=$(docker inspect -f \
|
|
89
|
+
"{{with index .HostConfig.PortBindings \"${CONTAINER_PORT}/tcp\"}}{{(index . 0).HostPort}}{{end}}" \
|
|
90
|
+
"$IMAGE_NAME" 2>/dev/null || true)
|
|
91
|
+
if [ "$_bound" != "$HOST_PORT" ]; then
|
|
92
|
+
echo "port mapping changed (${_bound:-none} -> ${HOST_PORT}), will recreate"
|
|
93
|
+
return 0
|
|
94
|
+
fi
|
|
95
|
+
_new_ep=$(file_hash_from_image /docker-entrypoint.sh)
|
|
96
|
+
_old_ep=$(file_hash_from_container /docker-entrypoint.sh || true)
|
|
97
|
+
if [ -n "$_old_ep" ] && [ "$_new_ep" != "$_old_ep" ]; then
|
|
98
|
+
echo "docker-entrypoint.sh changed, will recreate"
|
|
99
|
+
return 0
|
|
100
|
+
fi
|
|
101
|
+
_new_ngx=$(file_hash_from_image /usr/sbin/nginx)
|
|
102
|
+
_old_ngx=$(file_hash_from_container /usr/sbin/nginx || true)
|
|
103
|
+
if [ -n "$_old_ngx" ] && [ "$_new_ngx" != "$_old_ngx" ]; then
|
|
104
|
+
echo "nginx binary changed, will recreate"
|
|
105
|
+
return 0
|
|
106
|
+
fi
|
|
107
|
+
return 1
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
start_container() {
|
|
111
|
+
docker run -d --name "$IMAGE_NAME" --restart always \
|
|
112
|
+
-p "${HOST_PORT}:${CONTAINER_PORT}" \
|
|
113
|
+
-v "${ASSETS_VOL}:/usr/share/nginx/html" \
|
|
114
|
+
-e "ASSET_RETENTION_DAYS=${ASSET_RETENTION_DAYS}" \
|
|
115
|
+
"${IMAGE_NAME}:latest"
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# 探活:优先本机 curl;CI 容器内无 curl 时用 host 网络 curl 容器访问宿主机映射端口
|
|
119
|
+
http_probe() {
|
|
120
|
+
_port="$1"
|
|
121
|
+
if command -v curl >/dev/null 2>&1; then
|
|
122
|
+
curl -sf --connect-timeout 1 "http://127.0.0.1:${_port}/" >/dev/null
|
|
123
|
+
return $?
|
|
124
|
+
fi
|
|
125
|
+
docker run --rm --network host curlimages/curl:8.5.0 \
|
|
126
|
+
-sf --connect-timeout 1 "http://127.0.0.1:${_port}/" >/dev/null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
wait_http() {
|
|
130
|
+
_port="$1"
|
|
131
|
+
_attempts="${2:-30}"
|
|
132
|
+
_i=1
|
|
133
|
+
while [ "$_i" -le "$_attempts" ]; do
|
|
134
|
+
if http_probe "$_port"; then
|
|
135
|
+
return 0
|
|
136
|
+
fi
|
|
137
|
+
sleep 0.5
|
|
138
|
+
_i=$((_i + 1))
|
|
139
|
+
done
|
|
140
|
+
return 1
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# 收集宿主机已占用 TCP 端口(Docker 映射 + 系统监听)
|
|
144
|
+
collect_used_ports() {
|
|
145
|
+
_out=""
|
|
146
|
+
for _id in $(docker ps -q 2>/dev/null); do
|
|
147
|
+
_chunk=$(docker inspect -f \
|
|
148
|
+
'{{range $p, $conf := .NetworkSettings.Ports}}{{if $conf}}{{range $conf}}{{.HostPort}}{{"\n"}}{{end}}{{end}}{{end}}' \
|
|
149
|
+
"$_id" 2>/dev/null || true)
|
|
150
|
+
_out="${_out}${_chunk}"
|
|
151
|
+
done
|
|
152
|
+
if command -v ss >/dev/null 2>&1; then
|
|
153
|
+
_out="${_out}$(ss -H -ltn 2>/dev/null | awk '{print $4}' | sed -e 's/^\[::\]://' -e 's/^.*://')
|
|
154
|
+
"
|
|
155
|
+
elif command -v netstat >/dev/null 2>&1; then
|
|
156
|
+
_out="${_out}$(netstat -ltn 2>/dev/null | awk 'NR>2 {sub(/^.*:/, "", $4); print $4}')"
|
|
157
|
+
fi
|
|
158
|
+
echo "$_out" | grep -E '^[0-9]+$' | sort -nu
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
is_port_in_use() {
|
|
162
|
+
echo "$USED_PORTS" | grep -qx "$1"
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# 在系统临时端口范围内选第一个空闲端口(不依赖 HOST_PORT 偏移)
|
|
166
|
+
pick_available_sidecar_port() {
|
|
167
|
+
if [ -n "${SIDECAR_HOST_PORT:-}" ]; then
|
|
168
|
+
if ! is_port_in_use "$SIDECAR_HOST_PORT"; then
|
|
169
|
+
echo "$SIDECAR_HOST_PORT"
|
|
170
|
+
return 0
|
|
171
|
+
fi
|
|
172
|
+
echo "sidecar: SIDECAR_HOST_PORT=${SIDECAR_HOST_PORT} already in use" >&2
|
|
173
|
+
return 1
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
if [ -r /proc/sys/net/ipv4/ip_local_port_range ]; then
|
|
177
|
+
_range=$(cat /proc/sys/net/ipv4/ip_local_port_range)
|
|
178
|
+
else
|
|
179
|
+
_range="32768 60999"
|
|
180
|
+
fi
|
|
181
|
+
_min=$(echo "$_range" | awk '{print $1}')
|
|
182
|
+
_max=$(echo "$_range" | awk '{print $2}')
|
|
183
|
+
|
|
184
|
+
_port="$_min"
|
|
185
|
+
while [ "$_port" -le "$_max" ]; do
|
|
186
|
+
if ! is_port_in_use "$_port"; then
|
|
187
|
+
echo "$_port"
|
|
188
|
+
return 0
|
|
189
|
+
fi
|
|
190
|
+
_port=$((_port + 1))
|
|
191
|
+
done
|
|
192
|
+
return 1
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# 重建前旁路探活:先枚举已用端口,再绑定空闲端口
|
|
196
|
+
start_sidecar_container() {
|
|
197
|
+
NEXT_NAME="${IMAGE_NAME}-next"
|
|
198
|
+
TEMP_PORT=""
|
|
199
|
+
|
|
200
|
+
docker rm -f "$NEXT_NAME" 2>/dev/null || true
|
|
201
|
+
|
|
202
|
+
USED_PORTS=$(collect_used_ports)
|
|
203
|
+
_used_flat=$(echo "$USED_PORTS" | tr '\n' ' ' | sed 's/ $//')
|
|
204
|
+
echo "sidecar: in-use host ports: ${_used_flat:-none}" >&2
|
|
205
|
+
|
|
206
|
+
TEMP_PORT=$(pick_available_sidecar_port) || TEMP_PORT=""
|
|
207
|
+
|
|
208
|
+
if [ -n "$TEMP_PORT" ]; then
|
|
209
|
+
echo "sidecar: selected free port ${TEMP_PORT}" >&2
|
|
210
|
+
docker run -d --name "$NEXT_NAME" \
|
|
211
|
+
-p "127.0.0.1:${TEMP_PORT}:${CONTAINER_PORT}" \
|
|
212
|
+
-v "${ASSETS_VOL}:/usr/share/nginx/html" \
|
|
213
|
+
-e "ASSET_RETENTION_DAYS=${ASSET_RETENTION_DAYS}" \
|
|
214
|
+
"${IMAGE_NAME}:latest"
|
|
215
|
+
echo "$TEMP_PORT"
|
|
216
|
+
return 0
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
echo "sidecar: no free port in ephemeral range, fallback to docker auto-assign" >&2
|
|
220
|
+
docker run -d --name "$NEXT_NAME" \
|
|
221
|
+
-p "127.0.0.1::${CONTAINER_PORT}" \
|
|
222
|
+
-v "${ASSETS_VOL}:/usr/share/nginx/html" \
|
|
223
|
+
-e "ASSET_RETENTION_DAYS=${ASSET_RETENTION_DAYS}" \
|
|
224
|
+
"${IMAGE_NAME}:latest"
|
|
225
|
+
docker port "$NEXT_NAME" "${CONTAINER_PORT}/tcp" | sed 's/.*://'
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
probe_sidecar_before_recreate() {
|
|
229
|
+
NEXT_NAME="${IMAGE_NAME}-next"
|
|
230
|
+
TEMP_PORT=$(start_sidecar_container) || {
|
|
231
|
+
echo "sidecar container failed to start" >&2
|
|
232
|
+
exit 1
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if ! wait_http "$TEMP_PORT"; then
|
|
236
|
+
echo "sidecar probe failed on port ${TEMP_PORT}" >&2
|
|
237
|
+
docker logs "$NEXT_NAME" 2>&1 || true
|
|
238
|
+
docker rm -f "$NEXT_NAME" || true
|
|
239
|
+
exit 1
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
docker stop "$NEXT_NAME" && docker rm "$NEXT_NAME"
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
wait_main_port() {
|
|
246
|
+
if ! wait_http "$HOST_PORT"; then
|
|
247
|
+
echo "main port probe failed on ${HOST_PORT}" >&2
|
|
248
|
+
if docker inspect "$IMAGE_NAME" >/dev/null 2>&1; then
|
|
249
|
+
docker logs "$IMAGE_NAME" 2>&1 || true
|
|
250
|
+
fi
|
|
251
|
+
exit 1
|
|
252
|
+
fi
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if ! docker inspect "$IMAGE_NAME" >/dev/null 2>&1; then
|
|
256
|
+
start_container
|
|
257
|
+
wait_main_port
|
|
258
|
+
elif need_recreate; then
|
|
259
|
+
probe_sidecar_before_recreate
|
|
260
|
+
docker stop "$IMAGE_NAME" || true
|
|
261
|
+
docker rm "$IMAGE_NAME" || true
|
|
262
|
+
start_container
|
|
263
|
+
wait_main_port
|
|
264
|
+
else
|
|
265
|
+
# 已停止则先 start,再走同一套 conf 备份 / -t / reload(docker exec 需要运行中)
|
|
266
|
+
if [ "$(docker inspect -f '{{.State.Running}}' "$IMAGE_NAME")" != "true" ]; then
|
|
267
|
+
docker start "$IMAGE_NAME"
|
|
268
|
+
fi
|
|
269
|
+
sync_nginx_conf
|
|
270
|
+
wait_main_port
|
|
271
|
+
fi
|
package/docker-entrypoint.sh
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
#!/bin/sh
|
|
2
|
-
set -e
|
|
3
|
-
|
|
4
|
-
HTML_ROOT=/usr/share/nginx/html
|
|
5
|
-
DIST_ROOT=/app/dist
|
|
6
|
-
# 保留最近 N 天的历史 hashed 资源,供已打开旧页面继续懒加载
|
|
7
|
-
ASSET_RETENTION_DAYS="${ASSET_RETENTION_DAYS:-14}"
|
|
8
|
-
|
|
9
|
-
mkdir -p "$HTML_ROOT"
|
|
10
|
-
|
|
11
|
-
# 合并拷贝:覆盖同名文件(如 index.html),保留卷中已有、本次构建不含的旧 chunk
|
|
12
|
-
if [ -d "$DIST_ROOT" ]; then
|
|
13
|
-
cp -a "$DIST_ROOT"/. "$HTML_ROOT"/
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
# 清理过期静态资源,避免磁盘无限增长(不影响未过期的旧入口)
|
|
17
|
-
if [ -d "$HTML_ROOT/assets" ]; then
|
|
18
|
-
find "$HTML_ROOT/assets" -type f -mtime "+${ASSET_RETENTION_DAYS}" -delete 2>/dev/null || true
|
|
19
|
-
fi
|
|
20
|
-
|
|
21
|
-
exec "$@"
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
HTML_ROOT=/usr/share/nginx/html
|
|
5
|
+
DIST_ROOT=/app/dist
|
|
6
|
+
# 保留最近 N 天的历史 hashed 资源,供已打开旧页面继续懒加载
|
|
7
|
+
ASSET_RETENTION_DAYS="${ASSET_RETENTION_DAYS:-14}"
|
|
8
|
+
|
|
9
|
+
mkdir -p "$HTML_ROOT"
|
|
10
|
+
|
|
11
|
+
# 合并拷贝:覆盖同名文件(如 index.html),保留卷中已有、本次构建不含的旧 chunk
|
|
12
|
+
if [ -d "$DIST_ROOT" ]; then
|
|
13
|
+
cp -a "$DIST_ROOT"/. "$HTML_ROOT"/
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# 清理过期静态资源,避免磁盘无限增长(不影响未过期的旧入口)
|
|
17
|
+
if [ -d "$HTML_ROOT/assets" ]; then
|
|
18
|
+
find "$HTML_ROOT/assets" -type f -mtime "+${ASSET_RETENTION_DAYS}" -delete 2>/dev/null || true
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
exec "$@"
|
package/eslint.config.mjs.txt
CHANGED
|
@@ -1,94 +1,94 @@
|
|
|
1
|
-
// 从 URL 和路径模块中导入必要的功能
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import path, { dirname } from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
|
|
6
|
-
// 从 ESLint 插件中导入推荐配置
|
|
7
|
-
import pluginJs from '@eslint/js';
|
|
8
|
-
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
9
|
-
import pluginVue from 'eslint-plugin-vue';
|
|
10
|
-
import globals from 'globals';
|
|
11
|
-
import tseslint from 'typescript-eslint';
|
|
12
|
-
|
|
13
|
-
// 使用 import.meta.url 获取当前模块的路径
|
|
14
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
-
const __dirname = dirname(__filename);
|
|
16
|
-
|
|
17
|
-
// 读取 .auto-import.json 文件的内容,并将其解析为 JSON 对象
|
|
18
|
-
const autoImportConfig = JSON.parse(
|
|
19
|
-
fs.readFileSync(path.resolve(__dirname, '.auto-import.json'), 'utf-8'),
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
export default [
|
|
23
|
-
// 指定文件匹配规则
|
|
24
|
-
{
|
|
25
|
-
files: ['**/*.{js,mjs,cjs,ts,vue}'],
|
|
26
|
-
},
|
|
27
|
-
// 指定全局变量和环境
|
|
28
|
-
{
|
|
29
|
-
languageOptions: {
|
|
30
|
-
globals: {
|
|
31
|
-
...globals.browser,
|
|
32
|
-
...globals.node,
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
// 扩展配置
|
|
37
|
-
pluginJs.configs.recommended,
|
|
38
|
-
...tseslint.configs.recommended,
|
|
39
|
-
...pluginVue.configs['flat/essential'],
|
|
40
|
-
// 自定义规则
|
|
41
|
-
{
|
|
42
|
-
// 针对所有 JavaScript、TypeScript 和 Vue 文件应用以下配置
|
|
43
|
-
files: ['**/*.{js,mjs,cjs,ts,vue}'],
|
|
44
|
-
|
|
45
|
-
languageOptions: {
|
|
46
|
-
globals: {
|
|
47
|
-
// 合并从 autoImportConfig 中读取的全局变量配置
|
|
48
|
-
...autoImportConfig.globals,
|
|
49
|
-
// TypeScript 全局命名空间
|
|
50
|
-
Api: 'readonly',
|
|
51
|
-
Dict: 'readonly',
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
rules: {
|
|
55
|
-
'no-undef': 'off', // TypeScript 自身已处理未定义变量检查
|
|
56
|
-
quotes: ['error', 'single'], // 使用单引号
|
|
57
|
-
semi: ['error', 'never'], // 语句末尾不加分号
|
|
58
|
-
'no-var': 'error', // 要求使用 let 或 const 而不是 var
|
|
59
|
-
'@typescript-eslint/no-explicit-any': 'off', // 禁用 any 检查
|
|
60
|
-
'vue/multi-word-component-names': 'off', // 禁用对 Vue 组件名称的多词要求检查
|
|
61
|
-
'no-multiple-empty-lines': ['warn', { max: 1, maxEOF: 1 }], // 不允许多个空行
|
|
62
|
-
'no-unexpected-multiline': 'error', // 禁止空余的多行
|
|
63
|
-
'no-unused-vars': 'off', // 禁用未使用的变量检查
|
|
64
|
-
'@typescript-eslint/no-unused-vars': 'off', // 禁用 TypeScript 未使用的变量检查
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
// vue 规则
|
|
68
|
-
{
|
|
69
|
-
files: ['**/*.vue'],
|
|
70
|
-
languageOptions: {
|
|
71
|
-
parserOptions: {
|
|
72
|
-
parser: tseslint.parser,
|
|
73
|
-
ecmaFeatures: {
|
|
74
|
-
jsx: true,
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
// 忽略文件
|
|
80
|
-
{
|
|
81
|
-
ignores: [
|
|
82
|
-
'node_modules',
|
|
83
|
-
'dist',
|
|
84
|
-
'public',
|
|
85
|
-
'.vscode/**',
|
|
86
|
-
'src/assets/**',
|
|
87
|
-
'src/utils/console.ts',
|
|
88
|
-
'src/api',
|
|
89
|
-
'src/api/**',
|
|
90
|
-
],
|
|
91
|
-
},
|
|
92
|
-
// prettier 配置
|
|
93
|
-
eslintPluginPrettierRecommended,
|
|
94
|
-
];
|
|
1
|
+
// 从 URL 和路径模块中导入必要的功能
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path, { dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
// 从 ESLint 插件中导入推荐配置
|
|
7
|
+
import pluginJs from '@eslint/js';
|
|
8
|
+
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
9
|
+
import pluginVue from 'eslint-plugin-vue';
|
|
10
|
+
import globals from 'globals';
|
|
11
|
+
import tseslint from 'typescript-eslint';
|
|
12
|
+
|
|
13
|
+
// 使用 import.meta.url 获取当前模块的路径
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
|
|
17
|
+
// 读取 .auto-import.json 文件的内容,并将其解析为 JSON 对象
|
|
18
|
+
const autoImportConfig = JSON.parse(
|
|
19
|
+
fs.readFileSync(path.resolve(__dirname, '.auto-import.json'), 'utf-8'),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export default [
|
|
23
|
+
// 指定文件匹配规则
|
|
24
|
+
{
|
|
25
|
+
files: ['**/*.{js,mjs,cjs,ts,vue}'],
|
|
26
|
+
},
|
|
27
|
+
// 指定全局变量和环境
|
|
28
|
+
{
|
|
29
|
+
languageOptions: {
|
|
30
|
+
globals: {
|
|
31
|
+
...globals.browser,
|
|
32
|
+
...globals.node,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
// 扩展配置
|
|
37
|
+
pluginJs.configs.recommended,
|
|
38
|
+
...tseslint.configs.recommended,
|
|
39
|
+
...pluginVue.configs['flat/essential'],
|
|
40
|
+
// 自定义规则
|
|
41
|
+
{
|
|
42
|
+
// 针对所有 JavaScript、TypeScript 和 Vue 文件应用以下配置
|
|
43
|
+
files: ['**/*.{js,mjs,cjs,ts,vue}'],
|
|
44
|
+
|
|
45
|
+
languageOptions: {
|
|
46
|
+
globals: {
|
|
47
|
+
// 合并从 autoImportConfig 中读取的全局变量配置
|
|
48
|
+
...autoImportConfig.globals,
|
|
49
|
+
// TypeScript 全局命名空间
|
|
50
|
+
Api: 'readonly',
|
|
51
|
+
Dict: 'readonly',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
rules: {
|
|
55
|
+
'no-undef': 'off', // TypeScript 自身已处理未定义变量检查
|
|
56
|
+
quotes: ['error', 'single'], // 使用单引号
|
|
57
|
+
semi: ['error', 'never'], // 语句末尾不加分号
|
|
58
|
+
'no-var': 'error', // 要求使用 let 或 const 而不是 var
|
|
59
|
+
'@typescript-eslint/no-explicit-any': 'off', // 禁用 any 检查
|
|
60
|
+
'vue/multi-word-component-names': 'off', // 禁用对 Vue 组件名称的多词要求检查
|
|
61
|
+
'no-multiple-empty-lines': ['warn', { max: 1, maxEOF: 1 }], // 不允许多个空行
|
|
62
|
+
'no-unexpected-multiline': 'error', // 禁止空余的多行
|
|
63
|
+
'no-unused-vars': 'off', // 禁用未使用的变量检查
|
|
64
|
+
'@typescript-eslint/no-unused-vars': 'off', // 禁用 TypeScript 未使用的变量检查
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
// vue 规则
|
|
68
|
+
{
|
|
69
|
+
files: ['**/*.vue'],
|
|
70
|
+
languageOptions: {
|
|
71
|
+
parserOptions: {
|
|
72
|
+
parser: tseslint.parser,
|
|
73
|
+
ecmaFeatures: {
|
|
74
|
+
jsx: true,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
// 忽略文件
|
|
80
|
+
{
|
|
81
|
+
ignores: [
|
|
82
|
+
'node_modules',
|
|
83
|
+
'dist',
|
|
84
|
+
'public',
|
|
85
|
+
'.vscode/**',
|
|
86
|
+
'src/assets/**',
|
|
87
|
+
'src/utils/console.ts',
|
|
88
|
+
'src/api',
|
|
89
|
+
'src/api/**',
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
// prettier 配置
|
|
93
|
+
eslintPluginPrettierRecommended,
|
|
94
|
+
];
|
package/nginx_dev.conf
CHANGED
|
@@ -1,85 +1,85 @@
|
|
|
1
|
-
# 自定义日志格式:包含实际转发的后端地址
|
|
2
|
-
log_format proxy_log
|
|
3
|
-
'[$time_local] $status $request | '
|
|
4
|
-
'client=$remote_addr upstream=$upstream_addr | '
|
|
5
|
-
'upstream_status=$upstream_status upstream_time=${upstream_response_time}s | '
|
|
6
|
-
'referer="$http_referer" agent="$http_user_agent"';
|
|
7
|
-
|
|
8
|
-
# 根据请求IP分配后端地址(公网IP走公网,默认走内网)
|
|
9
|
-
map $host $dev_backend_addr {
|
|
10
|
-
# fulfillment-dev.tiantai-jp.com -> 192.168.128.215
|
|
11
|
-
default fulfillment-dev.tiantai-jp.com:11000; # 默认走内网
|
|
12
|
-
# ly-dev.tiantai-jp.com -> 14.127.204.225
|
|
13
|
-
#
|
|
14
|
-
120.76.61.203 ly-dev.tiantai-jp.com:11000; # 公网IP走公网
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
# HTTP 服务器 - 保持原有配置
|
|
18
|
-
server {
|
|
19
|
-
<<<listen>>>
|
|
20
|
-
server_name localhost;
|
|
21
|
-
charset utf-8;
|
|
22
|
-
|
|
23
|
-
resolver 8.8.8.8 1.1.1.1 ipv6=off;
|
|
24
|
-
|
|
25
|
-
# 使用自定义日志格式,记录后端转发地址
|
|
26
|
-
access_log /var/log/nginx/access.log proxy_log;
|
|
27
|
-
|
|
28
|
-
root /usr/share/nginx/html;
|
|
29
|
-
index index.html;
|
|
30
|
-
|
|
31
|
-
# 带 hash 的构建资源:长缓存;缺失时返回 404(禁止回退成 index.html,避免把 HTML 当 JS)
|
|
32
|
-
location /assets/ {
|
|
33
|
-
try_files $uri =404;
|
|
34
|
-
expires 1y;
|
|
35
|
-
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
|
36
|
-
|
|
37
|
-
# CORS 配置
|
|
38
|
-
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
39
|
-
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
40
|
-
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
41
|
-
<<<assets_CORS配置>>>
|
|
42
|
-
gzip_static on;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
# 入口 HTML:禁止缓存,确保新开/刷新拿到最新入口
|
|
46
|
-
location = /index.html {
|
|
47
|
-
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
|
|
48
|
-
add_header Pragma "no-cache" always;
|
|
49
|
-
add_header Expires "0" always;
|
|
50
|
-
|
|
51
|
-
# CORS 配置
|
|
52
|
-
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
53
|
-
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
54
|
-
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
55
|
-
<<<index_html_CORS配置>>>
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
location / {
|
|
59
|
-
try_files $uri $uri/ /index.html;
|
|
60
|
-
add_header Cache-Control "no-cache" always;
|
|
61
|
-
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
62
|
-
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
63
|
-
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
64
|
-
<<<index_CORS配置>>>
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
location /dev-api/ {
|
|
68
|
-
proxy_set_header Host $host;
|
|
69
|
-
proxy_set_header X-Real-Ip $remote_addr;
|
|
70
|
-
proxy_set_header X-Forwarded-For $remote_addr;
|
|
71
|
-
|
|
72
|
-
# 与前端 TtHttp REQUEST_TIMEOUT 对齐:10 分钟
|
|
73
|
-
proxy_connect_timeout 600s;
|
|
74
|
-
proxy_send_timeout 600s;
|
|
75
|
-
proxy_read_timeout 600s;
|
|
76
|
-
|
|
77
|
-
rewrite ^/dev-api/(.*) /$1 break;
|
|
78
|
-
proxy_pass http://$dev_backend_addr;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
error_page 500 502 503 504 /50x.html;
|
|
82
|
-
location = /50x.html {
|
|
83
|
-
root html;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
1
|
+
# 自定义日志格式:包含实际转发的后端地址
|
|
2
|
+
log_format proxy_log
|
|
3
|
+
'[$time_local] $status $request | '
|
|
4
|
+
'client=$remote_addr upstream=$upstream_addr | '
|
|
5
|
+
'upstream_status=$upstream_status upstream_time=${upstream_response_time}s | '
|
|
6
|
+
'referer="$http_referer" agent="$http_user_agent"';
|
|
7
|
+
|
|
8
|
+
# 根据请求IP分配后端地址(公网IP走公网,默认走内网)
|
|
9
|
+
map $host $dev_backend_addr {
|
|
10
|
+
# fulfillment-dev.tiantai-jp.com -> 192.168.128.215
|
|
11
|
+
default fulfillment-dev.tiantai-jp.com:11000; # 默认走内网
|
|
12
|
+
# ly-dev.tiantai-jp.com -> 14.127.204.225
|
|
13
|
+
#
|
|
14
|
+
120.76.61.203 ly-dev.tiantai-jp.com:11000; # 公网IP走公网
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# HTTP 服务器 - 保持原有配置
|
|
18
|
+
server {
|
|
19
|
+
<<<listen>>>
|
|
20
|
+
server_name localhost;
|
|
21
|
+
charset utf-8;
|
|
22
|
+
|
|
23
|
+
resolver 8.8.8.8 1.1.1.1 ipv6=off;
|
|
24
|
+
|
|
25
|
+
# 使用自定义日志格式,记录后端转发地址
|
|
26
|
+
access_log /var/log/nginx/access.log proxy_log;
|
|
27
|
+
|
|
28
|
+
root /usr/share/nginx/html;
|
|
29
|
+
index index.html;
|
|
30
|
+
|
|
31
|
+
# 带 hash 的构建资源:长缓存;缺失时返回 404(禁止回退成 index.html,避免把 HTML 当 JS)
|
|
32
|
+
location /assets/ {
|
|
33
|
+
try_files $uri =404;
|
|
34
|
+
expires 1y;
|
|
35
|
+
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
|
36
|
+
|
|
37
|
+
# CORS 配置
|
|
38
|
+
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
39
|
+
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
40
|
+
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
41
|
+
<<<assets_CORS配置>>>
|
|
42
|
+
gzip_static on;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# 入口 HTML:禁止缓存,确保新开/刷新拿到最新入口
|
|
46
|
+
location = /index.html {
|
|
47
|
+
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
|
|
48
|
+
add_header Pragma "no-cache" always;
|
|
49
|
+
add_header Expires "0" always;
|
|
50
|
+
|
|
51
|
+
# CORS 配置
|
|
52
|
+
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
53
|
+
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
54
|
+
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
55
|
+
<<<index_html_CORS配置>>>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
location / {
|
|
59
|
+
try_files $uri $uri/ /index.html;
|
|
60
|
+
add_header Cache-Control "no-cache" always;
|
|
61
|
+
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
62
|
+
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
63
|
+
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
64
|
+
<<<index_CORS配置>>>
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
location /dev-api/ {
|
|
68
|
+
proxy_set_header Host $host;
|
|
69
|
+
proxy_set_header X-Real-Ip $remote_addr;
|
|
70
|
+
proxy_set_header X-Forwarded-For $remote_addr;
|
|
71
|
+
|
|
72
|
+
# 与前端 TtHttp REQUEST_TIMEOUT 对齐:10 分钟
|
|
73
|
+
proxy_connect_timeout 600s;
|
|
74
|
+
proxy_send_timeout 600s;
|
|
75
|
+
proxy_read_timeout 600s;
|
|
76
|
+
|
|
77
|
+
rewrite ^/dev-api/(.*) /$1 break;
|
|
78
|
+
proxy_pass http://$dev_backend_addr;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
error_page 500 502 503 504 /50x.html;
|
|
82
|
+
location = /50x.html {
|
|
83
|
+
root html;
|
|
84
|
+
}
|
|
85
|
+
}
|
package/nginx_test.conf
CHANGED
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
# 自定义日志格式:包含实际转发的后端地址
|
|
2
|
-
log_format proxy_log
|
|
3
|
-
'[$time_local] $status $request | '
|
|
4
|
-
'client=$remote_addr upstream=$upstream_addr | '
|
|
5
|
-
'upstream_status=$upstream_status upstream_time=${upstream_response_time}s | '
|
|
6
|
-
'referer="$http_referer" agent="$http_user_agent"';
|
|
7
|
-
|
|
8
|
-
# 根据请求IP分配后端地址(公网IP走公网,默认走内网)
|
|
9
|
-
map $host $test_backend_addr {
|
|
10
|
-
# fulfillment-test.tiantai-jp.com -> 192.168.127.73
|
|
11
|
-
default fulfillment-test.tiantai-jp.com:11000; # 默认走内网
|
|
12
|
-
# ly-test.tiantai-jp.com -> 116.6.49.180
|
|
13
|
-
120.76.61.203 ly-test.tiantai-jp.com:11000; # 公网IP走公网
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
# HTTP 服务器 - 保持原有配置
|
|
17
|
-
server {
|
|
18
|
-
<<<listen>>>
|
|
19
|
-
server_name localhost;
|
|
20
|
-
charset utf-8;
|
|
21
|
-
|
|
22
|
-
resolver 8.8.8.8 1.1.1.1 ipv6=off;
|
|
23
|
-
|
|
24
|
-
# 使用自定义日志格式,记录后端转发地址
|
|
25
|
-
access_log /var/log/nginx/access.log proxy_log;
|
|
26
|
-
|
|
27
|
-
root /usr/share/nginx/html;
|
|
28
|
-
index index.html;
|
|
29
|
-
|
|
30
|
-
# 带 hash 的构建资源:长缓存;缺失时返回 404(禁止回退成 index.html,避免把 HTML 当 JS)
|
|
31
|
-
location /assets/ {
|
|
32
|
-
try_files $uri =404;
|
|
33
|
-
expires 1y;
|
|
34
|
-
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
|
35
|
-
|
|
36
|
-
# CORS 配置
|
|
37
|
-
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
38
|
-
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
39
|
-
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
40
|
-
<<<assets_CORS配置>>>
|
|
41
|
-
gzip_static on;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
# 入口 HTML:禁止缓存,确保新开/刷新拿到最新入口
|
|
45
|
-
location = /index.html {
|
|
46
|
-
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
|
|
47
|
-
add_header Pragma "no-cache" always;
|
|
48
|
-
add_header Expires "0" always;
|
|
49
|
-
|
|
50
|
-
# CORS 配置
|
|
51
|
-
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
52
|
-
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
53
|
-
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
54
|
-
<<<index_html_CORS配置>>>
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
location / {
|
|
58
|
-
try_files $uri $uri/ /index.html;
|
|
59
|
-
add_header Cache-Control "no-cache" always;
|
|
60
|
-
|
|
61
|
-
# CORS 配置
|
|
62
|
-
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
63
|
-
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
64
|
-
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
65
|
-
<<<index_CORS配置>>>
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
location /test-api/ {
|
|
69
|
-
proxy_set_header Host $host;
|
|
70
|
-
proxy_set_header X-Real-Ip $remote_addr;
|
|
71
|
-
proxy_set_header X-Forwarded-For $remote_addr;
|
|
72
|
-
|
|
73
|
-
# 与前端 TtHttp REQUEST_TIMEOUT 对齐:10 分钟
|
|
74
|
-
proxy_connect_timeout 600s;
|
|
75
|
-
proxy_send_timeout 600s;
|
|
76
|
-
proxy_read_timeout 600s;
|
|
77
|
-
|
|
78
|
-
rewrite ^/test-api/(.*) /$1 break;
|
|
79
|
-
proxy_pass http://$test_backend_addr;
|
|
80
|
-
}
|
|
81
|
-
<<<其他location配置>>>
|
|
82
|
-
|
|
83
|
-
error_page 500 502 503 504 /50x.html;
|
|
84
|
-
location = /50x.html {
|
|
85
|
-
root html;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
1
|
+
# 自定义日志格式:包含实际转发的后端地址
|
|
2
|
+
log_format proxy_log
|
|
3
|
+
'[$time_local] $status $request | '
|
|
4
|
+
'client=$remote_addr upstream=$upstream_addr | '
|
|
5
|
+
'upstream_status=$upstream_status upstream_time=${upstream_response_time}s | '
|
|
6
|
+
'referer="$http_referer" agent="$http_user_agent"';
|
|
7
|
+
|
|
8
|
+
# 根据请求IP分配后端地址(公网IP走公网,默认走内网)
|
|
9
|
+
map $host $test_backend_addr {
|
|
10
|
+
# fulfillment-test.tiantai-jp.com -> 192.168.127.73
|
|
11
|
+
default fulfillment-test.tiantai-jp.com:11000; # 默认走内网
|
|
12
|
+
# ly-test.tiantai-jp.com -> 116.6.49.180
|
|
13
|
+
120.76.61.203 ly-test.tiantai-jp.com:11000; # 公网IP走公网
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
# HTTP 服务器 - 保持原有配置
|
|
17
|
+
server {
|
|
18
|
+
<<<listen>>>
|
|
19
|
+
server_name localhost;
|
|
20
|
+
charset utf-8;
|
|
21
|
+
|
|
22
|
+
resolver 8.8.8.8 1.1.1.1 ipv6=off;
|
|
23
|
+
|
|
24
|
+
# 使用自定义日志格式,记录后端转发地址
|
|
25
|
+
access_log /var/log/nginx/access.log proxy_log;
|
|
26
|
+
|
|
27
|
+
root /usr/share/nginx/html;
|
|
28
|
+
index index.html;
|
|
29
|
+
|
|
30
|
+
# 带 hash 的构建资源:长缓存;缺失时返回 404(禁止回退成 index.html,避免把 HTML 当 JS)
|
|
31
|
+
location /assets/ {
|
|
32
|
+
try_files $uri =404;
|
|
33
|
+
expires 1y;
|
|
34
|
+
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
|
35
|
+
|
|
36
|
+
# CORS 配置
|
|
37
|
+
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
38
|
+
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
39
|
+
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
40
|
+
<<<assets_CORS配置>>>
|
|
41
|
+
gzip_static on;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# 入口 HTML:禁止缓存,确保新开/刷新拿到最新入口
|
|
45
|
+
location = /index.html {
|
|
46
|
+
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
|
|
47
|
+
add_header Pragma "no-cache" always;
|
|
48
|
+
add_header Expires "0" always;
|
|
49
|
+
|
|
50
|
+
# CORS 配置
|
|
51
|
+
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
52
|
+
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
53
|
+
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
54
|
+
<<<index_html_CORS配置>>>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
location / {
|
|
58
|
+
try_files $uri $uri/ /index.html;
|
|
59
|
+
add_header Cache-Control "no-cache" always;
|
|
60
|
+
|
|
61
|
+
# CORS 配置
|
|
62
|
+
add_header 'Access-Control-Allow-Origin' '*' always;
|
|
63
|
+
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
|
|
64
|
+
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
|
|
65
|
+
<<<index_CORS配置>>>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
location /test-api/ {
|
|
69
|
+
proxy_set_header Host $host;
|
|
70
|
+
proxy_set_header X-Real-Ip $remote_addr;
|
|
71
|
+
proxy_set_header X-Forwarded-For $remote_addr;
|
|
72
|
+
|
|
73
|
+
# 与前端 TtHttp REQUEST_TIMEOUT 对齐:10 分钟
|
|
74
|
+
proxy_connect_timeout 600s;
|
|
75
|
+
proxy_send_timeout 600s;
|
|
76
|
+
proxy_read_timeout 600s;
|
|
77
|
+
|
|
78
|
+
rewrite ^/test-api/(.*) /$1 break;
|
|
79
|
+
proxy_pass http://$test_backend_addr;
|
|
80
|
+
}
|
|
81
|
+
<<<其他location配置>>>
|
|
82
|
+
|
|
83
|
+
error_page 500 502 503 504 /50x.html;
|
|
84
|
+
location = /50x.html {
|
|
85
|
+
root html;
|
|
86
|
+
}
|
|
87
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dazhicheng/common",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.47",
|
|
4
4
|
"description": "共享配置文件",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./src/types/index.d.ts",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"tsconfig.json",
|
|
26
26
|
"Dockerfile",
|
|
27
27
|
"docker-entrypoint.sh",
|
|
28
|
+
"deploy-docker.sh",
|
|
28
29
|
"nginx_dev.conf",
|
|
29
30
|
"nginx_test.conf",
|
|
30
31
|
"sync-common.mjs",
|
package/pnpm-workspace.yaml
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
minimumReleaseAge: 0
|
|
2
|
-
|
|
3
|
-
allowBuilds:
|
|
4
|
-
'@parcel/watcher': true
|
|
5
|
-
core-js: true
|
|
6
|
-
es5-ext: true
|
|
7
|
-
esbuild: true
|
|
8
|
-
vue-demi: true
|
|
9
|
-
'@tailwindcss/oxide': true
|
|
1
|
+
minimumReleaseAge: 0
|
|
2
|
+
|
|
3
|
+
allowBuilds:
|
|
4
|
+
'@parcel/watcher': true
|
|
5
|
+
core-js: true
|
|
6
|
+
es5-ext: true
|
|
7
|
+
esbuild: true
|
|
8
|
+
vue-demi: true
|
|
9
|
+
'@tailwindcss/oxide': true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|