修改ci配置流程
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
# 本地构建推送 / 远端部署 用的环境变量示例
|
||||
# 用法:复制为 .env 或直接 export 后执行脚本
|
||||
|
||||
# ---- ACR 阿里云镜像仓库 ----
|
||||
ACR_REGISTRY=registry.cn-hangzhou.aliyuncs.com
|
||||
ACR_NAMESPACE=damaomi_docker
|
||||
ACR_REPO=yunhai
|
||||
ACR_USERNAME=your-acr-username
|
||||
ACR_PASSWORD=your-acr-password
|
||||
|
||||
# ---- 镜像 tag ----
|
||||
APP_VERSION=userbase-v1.0
|
||||
FRONT_VERSION=userbase-front-v1.0
|
||||
|
||||
# ---- 部署到远端 ----
|
||||
DEPLOY_HOST=192.168.10.157
|
||||
DEPLOY_PORT=22
|
||||
DEPLOY_USER=yunhai
|
||||
REMOTE_DIR=/opt/docker/userbase
|
||||
SSHPASS=your-ssh-password
|
||||
@@ -1,6 +0,0 @@
|
||||
docker run --rm --network infra-infra-net --entrypoint java registry.cn-hangzhou.aliyuncs.com/damaomi_docker/yunhai:userbase-v1.0 -jar /app/userbase.jar --spring.profiles.active=prod --debug > /tmp/ub_debug.log 2>&1
|
||||
echo "EXIT=$?"
|
||||
echo "=== conditions / binding matches ==="
|
||||
grep -nE "YhAuthAutoConfiguration|CasSsoConfiguration|Failed to bind|Binding to|YhSsoProperties|Exception starting filter|serverUrlPrefix|NoSuchBean" /tmp/ub_debug.log | head -60
|
||||
echo "=== tail (conditions report) ==="
|
||||
tail -50 /tmp/ub_debug.log
|
||||
@@ -1 +0,0 @@
|
||||
EOF
|
||||
@@ -1,17 +0,0 @@
|
||||
set -e
|
||||
echo "== wait for health (up to 180s) =="
|
||||
for i in $(seq 1 36); do
|
||||
st=$(docker inspect -f '{{.State.Health.Status}}' userbase-app 2>/dev/null || echo unknown)
|
||||
echo " [$i] health=$st"
|
||||
if [ "$st" = "healthy" ]; then break; fi
|
||||
sleep 5
|
||||
done
|
||||
echo "== container status =="
|
||||
docker ps --filter name=userbase --format "table {{.Names}}\t{{.Status}}"
|
||||
echo "== nacos reachability (host) =="
|
||||
curl -s -m 5 -o /dev/null -w "host nacos http=%{http_code}\n" "http://nacos.yunhaixingtong.com:8848/nacos/v1/cs/configs?dataId=userbase.yaml&group=USERBASE&tenant=" || echo "host cannot reach nacos"
|
||||
echo "== nacos reachability (container) =="
|
||||
docker exec userbase-app sh -c "curl -s -m 5 -o /dev/null -w 'container nacos http=%{http_code}\n' 'http://nacos.yunhaixingtong.com:8848/nacos/v1/cs/configs?dataId=userbase.yaml&group=USERBASE&tenant='" || echo "container cannot reach nacos"
|
||||
echo "== app logs: nacos / config / errors (tail) =="
|
||||
docker logs --tail 200 userbase-app 2>&1 | grep -iE "Loading nacos data|userbase.yaml|locate Nacos|NacosConfig|get config from nacos|ERROR|Exception|connect.*refused|UnknownHost|403|401|拒绝|超时|Started " | tail -40 || true
|
||||
echo "== done =="
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# 本地构建 UserBase 前后端镜像并推送到阿里云 ACR
|
||||
# 用法: ./build-push.sh [app_tag] [front_tag]
|
||||
# 前置环境变量: ACR_NAMESPACE, ACR_USERNAME, ACR_PASSWORD
|
||||
# 前置产物: service/target/userbase.jar、front/dist/prod/
|
||||
set -euo pipefail
|
||||
|
||||
APP_VERSION="${1:-${APP_VERSION:-userbase-v1.0}}"
|
||||
FRONT_VERSION="${2:-${FRONT_VERSION:-userbase-front-v1.0}}"
|
||||
REGISTRY="${ACR_REGISTRY:-registry.cn-hangzhou.aliyuncs.com}"
|
||||
NAMESPACE="${ACR_NAMESPACE:?请设置 ACR_NAMESPACE}"
|
||||
REPO="${ACR_REPO:-yunhai}"
|
||||
|
||||
APP_IMG="$REGISTRY/$NAMESPACE/$REPO:$APP_VERSION"
|
||||
FRONT_IMG="$REGISTRY/$NAMESPACE/$REPO:$FRONT_VERSION"
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
[ -f "$ROOT/service/target/userbase.jar" ] || { echo "缺少 service/target/userbase.jar,请先 mvn package"; exit 1; }
|
||||
[ -f "$ROOT/front/dist/prod/index.html" ] || { echo "缺少 front/dist/prod,请先 npm run build:prod"; exit 1; }
|
||||
|
||||
echo "== login $REGISTRY =="
|
||||
echo "${ACR_PASSWORD:?请设置 ACR_PASSWORD}" | docker login --username="${ACR_USERNAME:?请设置 ACR_USERNAME}" --password-stdin "$REGISTRY"
|
||||
|
||||
echo "== build $APP_IMG =="
|
||||
docker build -f "$ROOT/service/docker/Dockerfile" -t "$APP_IMG" "$ROOT/service"
|
||||
|
||||
echo "== build $FRONT_IMG =="
|
||||
docker build -f "$ROOT/front/docker/Dockerfile" -t "$FRONT_IMG" "$ROOT/front"
|
||||
|
||||
echo "== push =="
|
||||
docker push "$APP_IMG"
|
||||
docker push "$FRONT_IMG"
|
||||
|
||||
echo "PUSHED $APP_IMG"
|
||||
echo "PUSHED $FRONT_IMG"
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
MC=mysql8
|
||||
echo "MYSQL_CONTAINER=$MC"
|
||||
|
||||
docker exec userbase-app cat /app/config/application-prod.yml > /tmp/cfg.yml
|
||||
PW=$(awk '/username: root/{f=1} f&&/^ *password:/{sub(/^ *password: */,""); gsub(/\r/,""); print; exit}' /tmp/cfg.yml)
|
||||
echo "PW_LEN=${#PW}"
|
||||
|
||||
printf '[client]\nuser=root\npassword=%s\n' "$PW" > /tmp/my.cnf
|
||||
docker cp /tmp/my.cnf "$MC":/tmp/my.cnf
|
||||
|
||||
echo "== check sys_user.type column (via TCP 127.0.0.1) =="
|
||||
docker exec "$MC" sh -c "mysql --defaults-extra-file=/tmp/my.cnf -h 127.0.0.1 -N -e \"SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_DEFAULT, IS_NULLABLE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='bsch_userbase' AND TABLE_NAME='sys_user' AND COLUMN_NAME='type';\""
|
||||
echo "QUERY_EXIT=$?"
|
||||
|
||||
echo "== also confirm DB reachable & table exists =="
|
||||
docker exec "$MC" sh -c "mysql --defaults-extra-file=/tmp/my.cnf -h 127.0.0.1 -N -e \"SHOW TABLES FROM bsch_userbase LIKE 'sys_user';\""
|
||||
|
||||
docker exec "$MC" rm -f /tmp/my.cnf
|
||||
rm -f /tmp/my.cnf /tmp/cfg.yml
|
||||
echo "CLEANED"
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# 一键部署:上传编排文件 -> SSH 到服务器拉取镜像并启动
|
||||
# 用法: ./deploy.sh [app_tag] [front_tag]
|
||||
# 前置环境变量: DEPLOY_HOST, SSHPASS(可选 ACR_NAMESPACE / DEPLOY_USER / REMOTE_DIR)
|
||||
# 注:服务器需已 docker login registry.cn-hangzhou.aliyuncs.com
|
||||
set -euo pipefail
|
||||
|
||||
APP_VERSION="${1:-${APP_VERSION:-userbase-v1.0}}"
|
||||
FRONT_VERSION="${2:-${FRONT_VERSION:-userbase-front-v1.0}}"
|
||||
HOST="${DEPLOY_HOST:?请设置 DEPLOY_HOST}"
|
||||
PORT="${DEPLOY_PORT:-22}"
|
||||
USER="${DEPLOY_USER:-yunhai}"
|
||||
REMOTE_DIR="${REMOTE_DIR:-/opt/docker/userbase}"
|
||||
NAMESPACE="${ACR_NAMESPACE:-damaomi_docker}"
|
||||
REGISTRY="${ACR_REGISTRY:-registry.cn-hangzhou.aliyuncs.com}"
|
||||
PY="${PYTHON_BIN:-python3}"
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
echo "== 1/2 上传 compose 到 $HOST:$REMOTE_DIR =="
|
||||
"$PY" upload.py "$HOST" "$PORT" "$USER" \
|
||||
docker-compose.userbase.yml "$REMOTE_DIR/docker-compose.userbase.yml"
|
||||
|
||||
echo "== 2/2 远端拉取镜像并启动 =="
|
||||
"$PY" remote_run.py "$HOST" "$PORT" "$USER" <<EOF
|
||||
set -e
|
||||
mkdir -p "$REMOTE_DIR/upload-data"
|
||||
cd "$REMOTE_DIR"
|
||||
cat > .env <<ENV
|
||||
ACR_REGISTRY=$REGISTRY
|
||||
ACR_NAMESPACE=$NAMESPACE
|
||||
APP_VERSION=$APP_VERSION
|
||||
FRONT_VERSION=$FRONT_VERSION
|
||||
ENV
|
||||
docker compose -f docker-compose.userbase.yml pull
|
||||
docker compose -f docker-compose.userbase.yml up -d
|
||||
docker image prune -f
|
||||
docker ps --filter name=userbase --format "table {{.Names}}\t{{.Status}}"
|
||||
echo DEPLOY_OK
|
||||
EOF
|
||||
@@ -1,23 +0,0 @@
|
||||
set -u
|
||||
CP=/opt/docker/infra/Caddyfile
|
||||
echo "===== 1. Caddyfile /userbase blocks (line numbers) ====="
|
||||
grep -n "userbase" "$CP"
|
||||
echo
|
||||
echo "===== 2. Full /userbase route region (lines 40-80) ====="
|
||||
sed -n '40,80p' "$CP"
|
||||
echo
|
||||
echo "===== 3. Caddy mounts in container ====="
|
||||
docker inspect caddy --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'
|
||||
echo
|
||||
echo "===== 4. Running caddy admin config: does it contain 'userbase'? ====="
|
||||
docker exec caddy curl -s localhost:2019/config/ | grep -i "userbase" | head -n 20
|
||||
echo "--- end grep ---"
|
||||
echo
|
||||
echo "===== 5. Caddy container status ====="
|
||||
docker ps --filter name=caddy --format '{{.Names}} {{.Status}}'
|
||||
echo
|
||||
echo "===== 6. Direct test via 127.0.0.1 with Host header ====="
|
||||
for p in /userbase/ /userbase/index.html /userbase/static/js/ /userbase/api/x; do
|
||||
code=$(curl -s -o /tmp/ub_body -w "%{http_code}" -H "Host: www.yunhaixingtong.com" "http://127.0.0.1$p")
|
||||
echo "$p -> $code | $(head -c 80 /tmp/ub_body | tr -d '\n')"
|
||||
done
|
||||
@@ -1,24 +0,0 @@
|
||||
set -u
|
||||
echo "===== 1. In-container view of /etc/caddy/Caddyfile: userbase count ====="
|
||||
docker exec caddy grep -c "userbase" /etc/caddy/Caddyfile
|
||||
echo
|
||||
echo "===== 2. Container start command ====="
|
||||
docker inspect caddy --format 'CMD={{.Config.Cmd}} ENTRY={{.Config.Entrypoint}}'
|
||||
echo
|
||||
echo "===== 3. Admin API reachable? top-level keys ====="
|
||||
docker exec caddy curl -s localhost:2019/
|
||||
echo
|
||||
echo "===== 4. Running HTTP servers + route matchers in config ====="
|
||||
docker exec caddy curl -s localhost:2019/config/ > /tmp/running.json
|
||||
echo "bytes: $(wc -c < /tmp/running.json)"
|
||||
echo "occurrences:"
|
||||
for s in "userbase" "Welcome to Base Platform" "handle_path" "reverse_proxy" "www.yunhaixingtong.com"; do
|
||||
echo " $s : $(grep -o "$s" /tmp/running.json | wc -l)"
|
||||
done
|
||||
echo
|
||||
echo "===== 5. Do a manual POST /load from inside container and show result ====="
|
||||
docker exec caddy caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile 2>&1
|
||||
echo "exit=$?"
|
||||
sleep 1
|
||||
echo "--- after reload, recount ---"
|
||||
docker exec caddy curl -s localhost:2019/config/ | grep -c "userbase"
|
||||
@@ -1,74 +0,0 @@
|
||||
# UserBase 编排:后端(Spring Boot 8060) + 前端(nginx 80)
|
||||
# 两者都挂在 infra-infra-net,由 caddy 按服务名反代到 www.yunhaixingtong.com/userbase
|
||||
# 启动:cd /opt/docker/userbase && docker compose -f docker-compose.userbase.yml up -d
|
||||
services:
|
||||
userbase-app:
|
||||
image: ${ACR_REGISTRY:-registry.cn-hangzhou.aliyuncs.com}/${ACR_NAMESPACE:-damaomi_docker}/yunhai:${APP_VERSION:-userbase-v1.0}
|
||||
container_name: userbase-app
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- infra-infra-net
|
||||
# SSO 回调后容器内要做 CAS serviceValidate 回调 sso.yunhaixingtong.com,
|
||||
# 该虚拟主机由宿主机 caddy 按 vhost 路由,但容器内无 DNS 记录会 UnknownHostException。
|
||||
# host-gateway 指向宿主机网关(caddy 监听 :80/:443 全接口),无需硬编码 IP;
|
||||
# 旧版 Docker(<20.10)不支持 host-gateway,可改为宿主机桥接网关 IP,如 172.17.0.1。
|
||||
extra_hosts:
|
||||
- "sso.yunhaixingtong.com:host-gateway"
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
# 配置中心:由 bootstrap.yml 中 spring.cloud.nacos.config 在 bootstrap 阶段自动拉取
|
||||
# 容器内通过 infra-infra-net 解析内网别名 nacos:8848(宿主机 shell 无法直接解析),
|
||||
# 公网域名 nacos.yunhaixingtong.com 仅用于控制台管理,生产容器走内网。
|
||||
NACOS_ADDR: nacos:8848
|
||||
# Nacos 已开启鉴权,config 客户端需携带账号(与旧 run-app.sh 的 nacos/nacos 一致)
|
||||
SPRING_CLOUD_NACOS_USERNAME: nacos
|
||||
SPRING_CLOUD_NACOS_PASSWORD: nacos
|
||||
SPRING_CLOUD_NACOS_CONFIG_USERNAME: nacos
|
||||
SPRING_CLOUD_NACOS_CONFIG_PASSWORD: nacos
|
||||
JAVA_OPTS: "-Xms512m -Xmx1024m"
|
||||
volumes:
|
||||
- ./upload-data:/data/upload
|
||||
expose:
|
||||
- "8060"
|
||||
healthcheck:
|
||||
# 只要能返回 HTTP 状态码就认为进程已起(业务接口普遍需要鉴权,不能用 -f)
|
||||
test: ["CMD-SHELL", "curl -s -o /dev/null -w '%{http_code}' http://localhost:8060/userbase/ | grep -qE '^[234]'"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 90s
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1.5'
|
||||
memory: 1536M
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
userbase-front:
|
||||
image: ${ACR_REGISTRY:-registry.cn-hangzhou.aliyuncs.com}/${ACR_NAMESPACE:-damaomi_docker}/yunhai:${FRONT_VERSION:-userbase-front-v1.0}
|
||||
container_name: userbase-front
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- infra-infra-net
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
expose:
|
||||
- "80"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
infra-infra-net:
|
||||
external: true
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
echo "== all containers (name + image) =="
|
||||
docker ps -a --format '{{.Names}}\t{{.Image}}'
|
||||
echo "== which containers are on infra-infra-net =="
|
||||
docker network inspect infra-infra-net --format '{{range .Containers}}{{.Name}} ({{.IPv4Address}}){{"\n"}}{{end}}' 2>/dev/null || echo "no such network"
|
||||
echo "== resolve mysql8 from userbase-app =="
|
||||
docker exec userbase-app getent hosts mysql8 2>/dev/null || echo "getent failed"
|
||||
@@ -1,15 +0,0 @@
|
||||
set -u
|
||||
echo "===== A. Reload caddy from inside the container (reads mounted /etc/caddy/Caddyfile) ====="
|
||||
docker exec caddy caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
echo "reload exit: $?"
|
||||
sleep 2
|
||||
echo
|
||||
echo "===== B. Verify running config now contains userbase ====="
|
||||
docker exec caddy curl -s localhost:2019/config/ | grep -c "userbase"
|
||||
echo "(count above; 0 means still missing)"
|
||||
echo
|
||||
echo "===== C. Test routes via 127.0.0.1 with Host header ====="
|
||||
for p in /userbase/ /userbase/index.html /userbase/static/js/ /userbase/api/x /userbase/login; do
|
||||
code=$(curl -s -o /tmp/ub_body -w "%{http_code}" -H "Host: www.yunhaixingtong.com" "http://127.0.0.1$p")
|
||||
echo "$p -> $code | $(head -c 90 /tmp/ub_body | tr -d '\n')"
|
||||
done
|
||||
@@ -1,19 +0,0 @@
|
||||
set -u
|
||||
echo "===== host file (has userbase) ====="
|
||||
grep -c "userbase" /opt/docker/infra/Caddyfile
|
||||
echo "===== container file BEFORE restart (expect 0) ====="
|
||||
docker exec caddy grep -c "userbase" /etc/caddy/Caddyfile
|
||||
echo
|
||||
echo "===== restart caddy to re-bind the edited file ====="
|
||||
docker restart caddy
|
||||
echo "restart exit: $?"
|
||||
sleep 6
|
||||
echo
|
||||
echo "===== container file AFTER restart (expect >0) ====="
|
||||
docker exec caddy grep -c "userbase" /etc/caddy/Caddyfile
|
||||
echo
|
||||
echo "===== test routes ====="
|
||||
for p in /userbase/ /userbase/index.html /userbase/static/js/ /userbase/api/x /userbase/login; do
|
||||
code=$(curl -s -o /tmp/ub_body -w "%{http_code}" -H "Host: www.yunhaixingtong.com" "http://127.0.0.1$p")
|
||||
echo "$p -> $code | $(head -c 100 /tmp/ub_body | tr -d '\n')"
|
||||
done
|
||||
@@ -1,33 +0,0 @@
|
||||
set -u
|
||||
CP=/opt/docker/infra/Caddyfile
|
||||
echo "===== before: line 70 ====="
|
||||
sed -n '70p' "$CP"
|
||||
echo "===== apply wildcard matcher (inode will change; restart re-binds) ====="
|
||||
sed -i 's#handle_path /userbase/ {#handle_path /userbase* {#' "$CP"
|
||||
echo "after:"; sed -n '70p' "$CP"
|
||||
echo
|
||||
echo "===== restart caddy to re-bind edited file ====="
|
||||
docker restart caddy
|
||||
echo "restart exit: $?"
|
||||
sleep 6
|
||||
echo "===== container sees fix? ====="
|
||||
docker exec caddy grep -n "handle_path /userbase" /etc/caddy/Caddyfile
|
||||
echo
|
||||
echo "===== extract a real JS asset from SPA and test it ====="
|
||||
html=$(curl -s -H "Host: www.yunhaixingtong.com" http://127.0.0.1/userbase/)
|
||||
echo "$html" | grep -oE 'src="[^"]+\.js"' | head -3
|
||||
asset=$(echo "$html" | grep -oE '/[A-Za-z0-9_./-]+\.js' | head -1)
|
||||
echo "first asset path: $asset"
|
||||
if [ -n "$asset" ]; then
|
||||
code=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: www.yunhaixingtong.com" "http://127.0.0.1/userbase${asset}")
|
||||
echo "/userbase${asset} -> $code"
|
||||
fi
|
||||
echo
|
||||
echo "===== route tests ====="
|
||||
for p in /userbase/ /userbase/index.html "/userbase${asset:-/static/js/}"; do
|
||||
code=$(curl -s -o /tmp/b -w "%{http_code}" -H "Host: www.yunhaixingtong.com" "http://127.0.0.1$p")
|
||||
echo "$p -> $code | $(head -c 70 /tmp/b|tr -d '\n')"
|
||||
done
|
||||
echo
|
||||
echo "===== backend test ====="
|
||||
curl -s -o /dev/null -w "/userbase/api/x -> %{http_code}\n" -H "Host: www.yunhaixingtong.com" http://127.0.0.1/userbase/api/x
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/bin/sh
|
||||
# 探测内网 nacos:8848 是否可达、public 空间下 userbase.yaml 是否存在、是否需要鉴权
|
||||
echo "== resolve nacos =="
|
||||
getent hosts nacos || echo "cannot resolve nacos"
|
||||
echo "== fetch userbase.yaml (public, no auth) =="
|
||||
curl -s -m 8 -o /tmp/nc.txt -w "http=%{http_code}\n" "http://nacos:8848/nacos/v1/cs/configs?dataId=userbase.yaml&group=USERBASE&tenant="
|
||||
echo "--- body (first 200 chars) ---"
|
||||
head -c 200 /tmp/nc.txt; echo
|
||||
if grep -q "unknown user" /tmp/nc.txt || grep -q "403" /tmp/nc.txt || [ "$(head -c 1 /tmp/nc.txt)" = "" ]; then
|
||||
echo "== needs auth, try login nacos/nacos =="
|
||||
TOKEN=$(curl -s -m 8 -X POST "http://nacos:8848/nacos/v1/auth/login?username=nacos&password=nacos" | sed -n 's/.*"accessToken":"\([^"]*\)".*/\1/p')
|
||||
echo "token_len=${#TOKEN}"
|
||||
if [ -n "$TOKEN" ]; then
|
||||
curl -s -m 8 -o /tmp/nc2.txt -w "http=%{http_code}\n" "http://nacos:8848/nacos/v1/cs/configs?dataId=userbase.yaml&group=USERBASE&tenant=&accessToken=$TOKEN"
|
||||
echo "--- authed body (first 200 chars) ---"
|
||||
head -c 200 /tmp/nc2.txt; echo
|
||||
fi
|
||||
fi
|
||||
echo "== done =="
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/sh
|
||||
echo "== containers on host =="
|
||||
docker ps --format '{{.Names}}\t{{.Networks}}' | head -40
|
||||
echo "== infra-infra-net members =="
|
||||
docker network inspect infra-infra-net --format '{{range .Containers}}{{.Name}} {{end}}' 2>/dev/null
|
||||
echo "== resolve nacos from inside userbase-app =="
|
||||
docker exec userbase-app sh -c "getent hosts nacos || echo 'no nacos in container DNS'"
|
||||
echo "== curl nacos:8848 from inside userbase-app (no auth) =="
|
||||
docker exec userbase-app sh -c "curl -s -m 8 -o /tmp/nc.txt -w 'http=%{http_code}\n' 'http://nacos:8848/nacos/v1/cs/configs?dataId=userbase.yaml&group=USERBASE&tenant=' ; head -c 200 /tmp/nc.txt; echo" || echo "curl failed"
|
||||
echo "== done =="
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/sh
|
||||
# 重新部署 userbase-app:拉取新镜像、重建、等待健康,并核对是否真正从 Nacos 拉到了配置
|
||||
echo "== login ACR =="
|
||||
echo '59qtJRqlT7' | docker login --username=15121037482 --password-stdin registry.cn-hangzhou.aliyuncs.com
|
||||
|
||||
cd /opt/docker/userbase
|
||||
echo "== pull userbase-app =="
|
||||
docker compose -f docker-compose.userbase.yml pull userbase-app
|
||||
echo "== up -d userbase-app =="
|
||||
docker compose -f docker-compose.userbase.yml up -d userbase-app
|
||||
|
||||
echo "== wait for health (up to 150s) =="
|
||||
for i in $(seq 1 30); do
|
||||
st=$(docker inspect -f '{{.State.Health.Status}}' userbase-app 2>/dev/null || echo unknown)
|
||||
echo " [$i] health=$st"
|
||||
if [ "$st" = "healthy" ]; then break; fi
|
||||
sleep 5
|
||||
done
|
||||
|
||||
echo "== nacos reachability (host) =="
|
||||
curl -s -m 5 -o /dev/null -w "host nacos http=%{http_code}\n" "http://nacos.yunhaixingtong.com:8848/nacos/v1/cs/configs?dataId=userbase.yaml&group=USERBASE&tenant=" || echo "host cannot reach nacos"
|
||||
|
||||
echo "== nacos reachability (container) =="
|
||||
docker exec userbase-app sh -c "curl -s -m 5 -o /dev/null -w 'container nacos http=%{http_code}\n' 'http://nacos.yunhaixingtong.com:8848/nacos/v1/cs/configs?dataId=userbase.yaml&group=USERBASE&tenant='" || echo "container cannot reach nacos"
|
||||
|
||||
echo "== app logs: nacos / config / errors =="
|
||||
docker logs --tail 150 userbase-app 2>&1 | grep -iE "Loading nacos data|userbase.yaml|locate Nacos|NacosConfig|get config from nacos|ERROR|Exception|connect.*refused|UnknownHost|403|401|拒绝|超时" | tail -50 || true
|
||||
echo "== done =="
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 通过 SSH 在远端以 sudo 执行脚本(从 stdin 读取)
|
||||
# 用法: python3 remote_run.py <host> <port> <user> < script.sh
|
||||
# 依赖环境变量 SSHPASS
|
||||
import os, sys, paramiko
|
||||
|
||||
|
||||
def main():
|
||||
host = sys.argv[1]
|
||||
port = int(sys.argv[2]) if len(sys.argv) > 2 else 22
|
||||
user = sys.argv[3] if len(sys.argv) > 3 else "root"
|
||||
pw = os.environ.get("SSHPASS")
|
||||
if not pw:
|
||||
sys.stderr.write("ERROR: 请先设置环境变量 SSHPASS\n")
|
||||
sys.exit(1)
|
||||
script = sys.stdin.read()
|
||||
c = paramiko.SSHClient()
|
||||
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
c.connect(host, port, user, password=pw, timeout=30)
|
||||
stdin, stdout, stderr = c.exec_command("sudo -S bash -s", timeout=1800)
|
||||
stdin.write(pw + "\n")
|
||||
stdin.write(script)
|
||||
stdin.flush()
|
||||
stdin.channel.shutdown_write()
|
||||
sys.stdout.write(stdout.read().decode(errors="replace"))
|
||||
err = stderr.read().decode(errors="replace")
|
||||
if err.strip():
|
||||
sys.stderr.write("STDERR:\n" + err)
|
||||
code = stdout.channel.recv_exit_status()
|
||||
c.close()
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/sh
|
||||
# 线上冒烟:经 Caddy 访问 UserBase,确认 Nacos 配置生效、前后端可达
|
||||
echo "== SPA 首页 =="
|
||||
curl -s -m 8 -o /dev/null -w "/userbase/ -> %{http_code}\n" -H "Host: www.yunhaixingtong.com" http://127.0.0.1/userbase/
|
||||
echo "== 前端静态资源 =="
|
||||
curl -s -m 8 -o /dev/null -w "/userbase/index.html -> %{http_code}\n" -H "Host: www.yunhaixingtong.com" http://127.0.0.1/userbase/index.html
|
||||
echo "== 后端 API 可达性(CAS 重定向即代表到达后端) =="
|
||||
curl -s -m 8 -o /dev/null -w "/userbase/api/x -> %{http_code}\n" -H "Host: www.yunhaixingtong.com" http://127.0.0.1/userbase/api/x
|
||||
echo "== 容器健康 =="
|
||||
docker inspect -f 'userbase-app health={{.State.Health.Status}}' userbase-app
|
||||
echo "== 确认应用真正连的是 nacos 下发的 mysql8 数据源(日志里看 datasource url) =="
|
||||
docker logs --tail 200 userbase-app 2>&1 | grep -iE "jdbc:mysql://mysql8|HikariPool|Started UserBaseApplication" | tail -5 || true
|
||||
echo "== done =="
|
||||
@@ -1,37 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# 通过 SFTP 上传文件到远端(参数: host port user local remote [local remote ...])
|
||||
# 依赖环境变量 SSHPASS
|
||||
import os, sys, paramiko
|
||||
|
||||
|
||||
def main():
|
||||
host = sys.argv[1]
|
||||
port = int(sys.argv[2]) if len(sys.argv) > 2 else 22
|
||||
user = sys.argv[3] if len(sys.argv) > 3 else "root"
|
||||
pw = os.environ.get("SSHPASS")
|
||||
if not pw:
|
||||
sys.stderr.write("ERROR: 请先设置环境变量 SSHPASS\n")
|
||||
sys.exit(1)
|
||||
t = paramiko.Transport((host, port))
|
||||
t.connect(username=user, password=pw)
|
||||
sftp = paramiko.SFTPClient.from_transport(t)
|
||||
args = sys.argv[4:]
|
||||
for i in range(0, len(args), 2):
|
||||
local, remote = args[i], args[i + 1]
|
||||
cur = ""
|
||||
for p in [p for p in os.path.dirname(remote).split("/") if p]:
|
||||
cur += "/" + p
|
||||
try:
|
||||
sftp.stat(cur)
|
||||
except IOError:
|
||||
try:
|
||||
sftp.mkdir(cur)
|
||||
except IOError:
|
||||
pass
|
||||
sftp.put(local, remote)
|
||||
print(f"PUT {local} -> {remote}")
|
||||
t.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,98 +0,0 @@
|
||||
# ============================================================
|
||||
# 请在 Nacos 控制台新增此配置(public 命名空间)
|
||||
# Data ID : userbase.yaml
|
||||
# Group : USERBASE
|
||||
# Format : YAML
|
||||
# 说明:
|
||||
# - 本文件即应用自身配置。Nacos 解析规则为 ${spring.application.name}.${file-extension},
|
||||
# 因 bootstrap.yml 中 name=userbase、file-extension=yaml,故 Data ID 必须是 userbase.yaml。
|
||||
# - public 空间下的 common.properties 会作为共享配置优先加载(低优先级),
|
||||
# 本文件(高优先级)中的同名字段会覆盖 common.properties。
|
||||
# - 若 common.properties 已包含 datasource / redis 等公共项,下面这些行可按需删减;
|
||||
# 保留也无害(应用配置优先级更高,仍以本文件为准)。
|
||||
# - 生产启动时 profile=prod,Nacos 还会额外尝试 userbase-prod.yaml(可选,不存在也无妨)。
|
||||
# ============================================================
|
||||
|
||||
server:
|
||||
port: 8060
|
||||
servlet:
|
||||
# 对外经 caddy 暴露为 http://www.yunhaixingtong.com/userbase
|
||||
context-path: /userbase
|
||||
|
||||
spring:
|
||||
mvc:
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 50MB
|
||||
max-request-size: 200MB
|
||||
jackson:
|
||||
time-zone: GMT+8
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
|
||||
redis:
|
||||
host: redis-server
|
||||
port: 6379
|
||||
database: 0
|
||||
password: 'wv&WcDYAmLtqTIGM'
|
||||
timeout: 2000ms
|
||||
data:
|
||||
redis:
|
||||
repositories:
|
||||
enabled: false
|
||||
|
||||
datasource:
|
||||
url: jdbc:mysql://mysql8:3306/bsch_userbase?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: root
|
||||
password: 'CVf^x$sW*b6CGHxh'
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
min-idle: 10
|
||||
max-active: 30
|
||||
|
||||
jpa:
|
||||
database: MySQL
|
||||
show-sql: false
|
||||
hibernate:
|
||||
ddl-auto: update
|
||||
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
|
||||
properties:
|
||||
hibernate.order_by.default_null_ordering: last
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
|
||||
open-in-view: true
|
||||
|
||||
rabbitmq:
|
||||
host: rabbitmq
|
||||
port: 5672
|
||||
username: admin
|
||||
password: 'JdU29I#Q2E4yXoUl'
|
||||
listener:
|
||||
direct:
|
||||
auto-startup: false
|
||||
simple:
|
||||
auto-startup: false
|
||||
|
||||
# 单点登录 / 应用间签名鉴权(基础平台 yh-auth / yh-app-auth)
|
||||
yh:
|
||||
sso:
|
||||
server-url-prefix: http://sso.yunhaixingtong.com
|
||||
client-host-url: http://www.yunhaixingtong.com/userbase
|
||||
allow-headers: all
|
||||
allow-origins: all
|
||||
url-pattern: /api/*
|
||||
mobi-api-pattern: /mobiapi/*
|
||||
app-auth:
|
||||
enabled: true
|
||||
|
||||
ADMIN_TAG_ID: 290
|
||||
|
||||
# 上传文件落容器卷,compose 里挂到宿主机 ./upload-data
|
||||
FILE_PATH: /data/upload
|
||||
FILE_WEB_PATH: http://www.yunhaixingtong.com/userbase/upload/
|
||||
ZIYUAN_WEB_PATH_HTTP: http://base.bestsch.com/ziyuanku/file/
|
||||
UPLOAD_WEB_PATH_HTTP: http://www.yunhaixingtong.com/userbase/upload/file/
|
||||
ORC_SERVICE_URL: http://oa.bestsch.com/OrcService/api/Ocr/Upload
|
||||
|
||||
logging:
|
||||
level:
|
||||
root: INFO
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
echo "== app startup DDL/schema hints =="
|
||||
docker logs userbase-app 2>&1 | grep -iE "alter table|update schema|create table|ddl|column|sys_user" | head -30 || echo "(no ddl lines captured)"
|
||||
|
||||
echo "== datasource config from container =="
|
||||
docker exec userbase-app cat /app/config/application-prod.yml 2>/dev/null | grep -iE "url:|username:|password:|jdbc" | sed -E 's/(password:).*/\1 ****/' || echo "(no config)"
|
||||
|
||||
echo "== mysql client available? =="
|
||||
which mysql || echo "NO_MYSQL_CLI"
|
||||
@@ -1,19 +0,0 @@
|
||||
set -u
|
||||
echo "===== 1. Main app JS chunk from SPA ====="
|
||||
html=$(curl -s -H "Host: www.yunhaixingtong.com" http://127.0.0.1/userbase/)
|
||||
appjs=$(echo "$html" | grep -oE '/static/js/[A-Za-z0-9_./-]+\.js' | head -1)
|
||||
echo "app js: $appjs"
|
||||
curl -s -o /dev/null -w " /userbase${appjs} -> %{http_code}\n" -H "Host: www.yunhaixingtong.com" "http://127.0.0.1/userbase${appjs}"
|
||||
echo
|
||||
echo "===== 2. Ensure other sites not regressed (sso / b) ====="
|
||||
curl -s -o /dev/null -w " sso host / -> %{http_code}\n" -H "Host: sso.yunhaixingtong.com" http://127.0.0.1/
|
||||
curl -s -o /dev/null -w " b host / -> %{http_code}\n" -H "Host: www.yunhaixingtong.com" http://127.0.0.1/b/
|
||||
echo
|
||||
echo "===== 3. Caddy listening ports on host ====="
|
||||
ss -ltnp 2>/dev/null | grep -E ':80 |:443 ' | head
|
||||
echo
|
||||
echo "===== 4. Containers status ====="
|
||||
docker ps --format '{{.Names}} {{.Status}}' | grep -E 'caddy|userbase'
|
||||
echo
|
||||
echo "===== 5. userbase-front health (serve index directly) ====="
|
||||
docker exec userbase-front wget -qO- http://localhost/ 2>/dev/null | head -c 60; echo
|
||||
@@ -2,7 +2,7 @@
|
||||
server:
|
||||
port: 8060
|
||||
servlet:
|
||||
# 对外经 caddy 暴露为 http://www.yunhaixingtong.com/userbase
|
||||
# 对外经 caddy 暴露为 http://base.yunhaixingtong.com/userbase
|
||||
context-path: /userbase
|
||||
|
||||
spring:
|
||||
@@ -61,8 +61,8 @@ spring:
|
||||
# 单点登录 / 应用间签名鉴权(基础平台 yh-auth / yh-app-auth)
|
||||
yh:
|
||||
sso:
|
||||
server-url-prefix: http://sso.yunhaixingtong.com
|
||||
client-host-url: http://www.yunhaixingtong.com/userbase
|
||||
server-url-prefix: http://sso.yunhaixingtong.com/cas/
|
||||
client-host-url: http://base.yunhaixingtong.com/userbase
|
||||
allow-headers: all
|
||||
allow-origins: all
|
||||
url-pattern: /api/*
|
||||
@@ -74,10 +74,8 @@ ADMIN_TAG_ID: 290
|
||||
|
||||
# 上传文件落容器卷,compose 里挂到宿主机 ./upload-data
|
||||
FILE_PATH: /data/upload
|
||||
FILE_WEB_PATH: http://www.yunhaixingtong.com/userbase/upload/
|
||||
ZIYUAN_WEB_PATH_HTTP: http://base.bestsch.com/ziyuanku/file/
|
||||
UPLOAD_WEB_PATH_HTTP: http://www.yunhaixingtong.com/userbase/upload/file/
|
||||
ORC_SERVICE_URL: http://oa.bestsch.com/OrcService/api/Ocr/Upload
|
||||
FILE_WEB_PATH: http://base.yunhaixingtong.com/userbase/upload/
|
||||
UPLOAD_WEB_PATH_HTTP: http://base.yunhaixingtong.com/userbase/upload/file/
|
||||
|
||||
logging:
|
||||
level:
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.bestsch.openapi;
|
||||
|
||||
/**
|
||||
* 迁移兼容 shim:原 bestsch 开放接口的"当前角色"载体。
|
||||
* 仅保留 {@code Operator.of(...)} 所需的访问器,避免批量改动业务代码。
|
||||
* 后续可替换为 UserBase 自有角色模型。
|
||||
*/
|
||||
public class UserSysRole {
|
||||
|
||||
private Integer userId;
|
||||
private Integer schId;
|
||||
private Integer roleId;
|
||||
|
||||
public Integer getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Integer userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Integer getSchId() {
|
||||
return schId;
|
||||
}
|
||||
|
||||
public void setSchId(Integer schId) {
|
||||
this.schId = schId;
|
||||
}
|
||||
|
||||
public Integer getRoleId() {
|
||||
return roleId;
|
||||
}
|
||||
|
||||
public void setRoleId(Integer roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.bestsch.utils;
|
||||
|
||||
/**
|
||||
* 迁移兼容 shim:原 bestsch 框架的业务异常类。
|
||||
* UserBase 大量业务代码依赖此异常,迁移到基础平台期间保留原包名与常用构造签名,避免批量改动。
|
||||
* 后续可统一替换为 UserBase 自有异常类型。
|
||||
*/
|
||||
public class DException extends RuntimeException {
|
||||
|
||||
public DException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/** 兼容历史调用方携带错误码的场景 */
|
||||
public DException(String message, int code) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,11 @@ package com.yunhai.userbase.controller.api;
|
||||
|
||||
import com.bestsch.utils.DException;
|
||||
import com.yunhai.userbase.common.annotation.OperationLog;
|
||||
import com.yunhai.userbase.common.context.UserContext;
|
||||
import com.yunhai.userbase.common.validation.ValidationGroups;
|
||||
import com.yunhai.userbase.config.Config;
|
||||
import com.yunhai.userbase.entity.ApplicationInfo;
|
||||
import com.yunhai.userbase.enums.BookType;
|
||||
import com.yunhai.userbase.model.BaseDict;
|
||||
import com.yunhai.userbase.model.BasePageQuery;
|
||||
import com.yunhai.userbase.model.BasePageResult;
|
||||
import com.yunhai.userbase.model.Operator;
|
||||
import com.yunhai.userbase.model.appauthz.AppAuthzCategoryVo;
|
||||
import com.yunhai.userbase.model.appcategory.AppCategoryQuery;
|
||||
import com.yunhai.userbase.model.appcategory.AppCategoryRequest;
|
||||
@@ -26,27 +22,13 @@ import com.yunhai.userbase.service.IAppCategoryService;
|
||||
import com.yunhai.userbase.service.IApplicationInfoService;
|
||||
import com.yunhai.userbase.service.IAppModuleService;
|
||||
import com.yunhai.userbase.service.SignKeyCacheService;
|
||||
import com.yunhai.userbase.utils.DicUtil;
|
||||
import com.yunhai.userbase.utils.PageUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.tomcat.util.http.fileupload.util.Streams;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.web.SortDefault;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.yunhai.userbase.model;
|
||||
|
||||
import com.bestsch.openapi.UserSysRole;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
*功能描述 :操作人信息
|
||||
* @author chenx
|
||||
* @date 2026/1/5 13:31
|
||||
* @description
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class Operator {
|
||||
private Integer userId;
|
||||
private Integer schoolSid;
|
||||
private Integer roleId;
|
||||
private Date createTime=new Date();
|
||||
|
||||
public static Operator of(UserSysRole curRole){
|
||||
return new Operator(curRole.getUserId(),curRole.getSchId(),curRole.getRoleId(),new Date());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,8 +15,8 @@ spring:
|
||||
# 默认指向 prod namespace UUID,避免本地误开时连到空的 dev 命名空间
|
||||
# CI 用 SPRING_CLOUD_NACOS_CONFIG_NAMESPACE 覆盖(注意:NACOS_NAMESPACE 这个 env 名不生效,必须用 SPRING_CLOUD_NACOS_CONFIG_NAMESPACE)
|
||||
namespace: ${SPRING_CLOUD_NACOS_CONFIG_NAMESPACE:6196f0de-a28d-4894-8f10-e2982b57c6bc}
|
||||
# 主配置 group:userbase.yaml / application-infra.yaml 同组
|
||||
group: DEFAULT_GROUP
|
||||
# 主配置 group:userbase.yaml 在 USERBASE group(与下方 shared-configs 的 DEFAULT_GROUP 不同组,各自指定)
|
||||
group: USERBASE
|
||||
# 配置文件格式;最终主配置 dataId = ${spring.application.name}.${file-extension} = userbase.yaml
|
||||
file-extension: yaml
|
||||
encode: UTF-8
|
||||
|
||||
Reference in New Issue
Block a user