init
This commit is contained in:
106
01-init-env.sh
Executable file
106
01-init-env.sh
Executable file
@@ -0,0 +1,106 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# 01-init-env.sh — 生成 .env.deploy-test 配置模板(测试服务器环境)
|
||||||
|
#
|
||||||
|
# 作用:在项目根目录生成 .env.deploy-test,填写各服务的连接信息
|
||||||
|
# 后续步骤:编辑 .env.deploy-test,然后执行 02-patch-config.sh
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
init_dirs
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
header "步骤 1 / 5 — 初始化 .env.deploy-test 配置"
|
||||||
|
|
||||||
|
if [[ -f "$ENV_FILE" ]]; then
|
||||||
|
warn ".env.deploy-test 已存在,跳过创建"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}当前配置:${NC}"
|
||||||
|
grep -v '^\s*#' "$ENV_FILE" | grep -v '^\s*$' | sed 's/^/ /'
|
||||||
|
echo ""
|
||||||
|
echo -e "如需重置:${YELLOW}rm $ENV_FILE && $0${NC}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > "$ENV_FILE" <<'EOF'
|
||||||
|
# =============================================================================
|
||||||
|
# 测试服务器环境配置 — .env.deploy-test
|
||||||
|
# 部署场景:测试服务器(有公网 IP),所有服务本地运行
|
||||||
|
# 编辑完成后执行:./deploy-test/02-patch-config.sh
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# ── 测试服务器公网 IP(必填)─────────────────────────────────────────────────
|
||||||
|
# 本机(测试服务器)的公网 IP,LiveKit WebRTC 媒体流需要通过此 IP 对外暴露
|
||||||
|
# 本地 Mac 环境(deploy-local)也会引用此地址连接 LiveKit
|
||||||
|
DEPLOY_TEST_IP=54.116.29.247
|
||||||
|
|
||||||
|
# ── MongoDB(远程服务,必填)─────────────────────────────────────────────────
|
||||||
|
# open-im-server / chat / build-server 共用同一个连接,只是 DB 名不同
|
||||||
|
MONGO_HOST=47.237.103.4
|
||||||
|
MONGO_PORT=27017
|
||||||
|
MONGO_USERNAME=minio_pC5wMB
|
||||||
|
MONGO_PASSWORD=rI57PJsJhnz_qlRkfnTa0RPT
|
||||||
|
MONGO_AUTHSOURCE=openim_v3
|
||||||
|
MONGO_DATABASE=openim_v3 # open-im-server / chat 使用
|
||||||
|
BUILD_MONGO_DATABASE=build # build-server 使用
|
||||||
|
|
||||||
|
# ── Amazon S3 — open-im-server(IM 聊天文件存储,必填)──────────────────────
|
||||||
|
# 对应 open-im-server/config/openim-rpc-third.yml → object.aws
|
||||||
|
OPENIM_AWS_REGION=ap-southeast-1
|
||||||
|
OPENIM_AWS_BUCKET=im1688
|
||||||
|
OPENIM_AWS_ACCESS_KEY_ID=AKIA5TMMSZWVFYCLKJ2G
|
||||||
|
OPENIM_AWS_SECRET_ACCESS_KEY=P+slboxgk8MqqXFHBFYRxBCKNfXQVuL7n5GJS56p
|
||||||
|
# 自定义 Endpoint(CloudFlare R2 / 其他 S3 兼容服务),留空则使用 AWS 官方
|
||||||
|
OPENIM_AWS_ENDPOINT=
|
||||||
|
OPENIM_AWS_PUBLIC_READ=true
|
||||||
|
|
||||||
|
# ── Amazon S3 — build-server(App APK/IPA 构建产物存储,必填)───────────────
|
||||||
|
# 对应 build-server/config/config.yaml → aws
|
||||||
|
BUILD_AWS_REGION=ap-east-1
|
||||||
|
BUILD_AWS_BUCKET=im-hk-apk
|
||||||
|
BUILD_AWS_ACCESS_KEY=AKIASJ7PFAWCXUDC7KQV
|
||||||
|
BUILD_AWS_SECRET_KEY=BCubTUsGcYCVmb4bjCFO0BRbdGeTSwNZNK4EOWTZ
|
||||||
|
|
||||||
|
# ── Redis(Docker 本地运行)─────────────────────────────────────────────────
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=openIM123
|
||||||
|
|
||||||
|
# ── Kafka(Docker 本地运行,KRaft 模式)────────────────────────────────────
|
||||||
|
KAFKA_PORT=9092
|
||||||
|
|
||||||
|
# ── Etcd(Docker 本地运行,服务发现注册中心)───────────────────────────────
|
||||||
|
ETCD_PORT=2379
|
||||||
|
|
||||||
|
# ── LiveKit Server(Docker 本地运行,使用本机公网 IP)──────────────────────
|
||||||
|
# LiveKit 通过 Docker 启动(容器名: dev-livekit),复用 dev-redis。
|
||||||
|
# WebRTC 媒体流需要公网 IP,使用上方 DEPLOY_TEST_IP。
|
||||||
|
#
|
||||||
|
# LIVEKIT_NODE_IP: = DEPLOY_TEST_IP,WebRTC 客户端通过此 IP 直连媒体流
|
||||||
|
# LIVEKIT_URL: 后端服务连接 LiveKit 的地址(服务器内部用回环即可)
|
||||||
|
# LIVEKIT_API_KEY / LIVEKIT_API_SECRET: 来自 livekit/livekit.yaml → keys 段
|
||||||
|
LIVEKIT_NODE_IP=54.116.29.247 # 与 DEPLOY_TEST_IP 保持一致
|
||||||
|
LIVEKIT_URL=ws://127.0.0.1:7880
|
||||||
|
LIVEKIT_API_KEY=API8462dba2
|
||||||
|
LIVEKIT_API_SECRET=U0l7/3IQjWzusK2eOrWlGmLD5jSzALvV2G5tIxGQaQc=
|
||||||
|
|
||||||
|
# ── Cloudflare Stream(livestream 服务使用)──────────────────────────────────
|
||||||
|
# 来源: livestream/config.yaml → cloudflare 段(若已有值请从该文件复制)
|
||||||
|
CF_ACCOUNT_ID=
|
||||||
|
CF_API_TOKEN=
|
||||||
|
CF_EMAIL=
|
||||||
|
CF_API_KEY=
|
||||||
|
CF_CUSTOMER_CODE=
|
||||||
|
|
||||||
|
# ── 腾讯云 RTC(livecloud 服务使用)─────────────────────────────────────────
|
||||||
|
# 来源: livecloud/config/config.yml → cloud.tencent 段
|
||||||
|
TENCENT_SDK_APP_ID=20033091
|
||||||
|
TENCENT_SDK_SECRET_KEY=cceba44084aaa04f8c48a1858ffd5385875c3a5ec006d34278d9d3714b40e3b0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
success ".env.deploy-test 已创建: $ENV_FILE"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}下一步:${NC}"
|
||||||
|
echo -e " 1. 确认 DEPLOY_TEST_IP 等关键配置正确:"
|
||||||
|
echo -e " ${CYAN}vim $ENV_FILE${NC}"
|
||||||
|
echo -e " 2. 将配置写入各服务 YAML:"
|
||||||
|
echo -e " ${CYAN}./deploy-test/02-patch-config.sh${NC}"
|
||||||
483
02-patch-config.sh
Executable file
483
02-patch-config.sh
Executable file
@@ -0,0 +1,483 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# 02-patch-config.sh — 将 .env.local 中的变量写入各服务 YAML 配置文件
|
||||||
|
#
|
||||||
|
# 影响文件:
|
||||||
|
# open-im-server/config/redis.yml kafka.yml discovery.yml
|
||||||
|
# open-im-server/config/mongodb.yml minio.yml openim-rpc-third.yml
|
||||||
|
# chat/config/redis.yml discovery.yml mongodb.yml
|
||||||
|
# meetingmsg/manifest/config/config.yaml
|
||||||
|
#
|
||||||
|
# 后续步骤:03-start-infra.sh(启动 Docker 基础设施)
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
load_env
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
header "步骤 2 / 5 — 修改服务配置文件"
|
||||||
|
|
||||||
|
OPENIM_CONF="$ROOT_DIR/open-im-server/config"
|
||||||
|
CHAT_CONF="$ROOT_DIR/chat/config"
|
||||||
|
|
||||||
|
[[ ! -d "$OPENIM_CONF" ]] && { error "目录不存在: $OPENIM_CONF"; exit 1; }
|
||||||
|
[[ ! -d "$CHAT_CONF" ]] && { error "目录不存在: $CHAT_CONF"; exit 1; }
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# open-im-server
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "open-im-server 配置"
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
cat > "$OPENIM_CONF/redis.yml" <<EOF
|
||||||
|
address: [ "127.0.0.1:${REDIS_PORT}" ]
|
||||||
|
username: ''
|
||||||
|
password: '${REDIS_PASSWORD}'
|
||||||
|
enablePipeline: false
|
||||||
|
clusterMode: false
|
||||||
|
db: 0
|
||||||
|
maxRetry: 10
|
||||||
|
poolSize: 100
|
||||||
|
onlineKeyPrefix: "openim:local-dev"
|
||||||
|
onlineKeyPrefixHashTag: false
|
||||||
|
sentinelMode:
|
||||||
|
masterName: "redis-master"
|
||||||
|
sentinelsAddrs: []
|
||||||
|
routeByLatency: false
|
||||||
|
routeRandomly: false
|
||||||
|
EOF
|
||||||
|
success " redis.yml → 127.0.0.1:${REDIS_PORT}"
|
||||||
|
|
||||||
|
# Kafka
|
||||||
|
cat > "$OPENIM_CONF/kafka.yml" <<EOF
|
||||||
|
username:
|
||||||
|
password:
|
||||||
|
producerAck:
|
||||||
|
compressType: none
|
||||||
|
address: ["127.0.0.1:${KAFKA_PORT}"]
|
||||||
|
toRedisTopic: toRedis
|
||||||
|
toMongoTopic: toMongo
|
||||||
|
toPushTopic: toPush
|
||||||
|
toOfflinePushTopic: toOfflinePush
|
||||||
|
toRedisGroupID: redis
|
||||||
|
toMongoGroupID: mongo
|
||||||
|
toPushGroupID: push
|
||||||
|
toOfflinePushGroupID: offlinePush
|
||||||
|
tls:
|
||||||
|
enableTLS: false
|
||||||
|
caCrt:
|
||||||
|
clientCrt:
|
||||||
|
clientKey:
|
||||||
|
clientKeyPwd:
|
||||||
|
insecureSkipVerify: false
|
||||||
|
EOF
|
||||||
|
success " kafka.yml → 127.0.0.1:${KAFKA_PORT}"
|
||||||
|
|
||||||
|
# Etcd
|
||||||
|
cat > "$OPENIM_CONF/discovery.yml" <<EOF
|
||||||
|
enable: etcd
|
||||||
|
etcd:
|
||||||
|
rootDirectory: openim
|
||||||
|
address: ["127.0.0.1:${ETCD_PORT}"]
|
||||||
|
username:
|
||||||
|
password:
|
||||||
|
kubernetes:
|
||||||
|
namespace: default
|
||||||
|
rpcService:
|
||||||
|
user: user-rpc-service
|
||||||
|
friend: friend-rpc-service
|
||||||
|
msg: msg-rpc-service
|
||||||
|
push: push-rpc-service
|
||||||
|
messageGateway: messagegateway-rpc-service
|
||||||
|
group: group-rpc-service
|
||||||
|
auth: auth-rpc-service
|
||||||
|
conversation: conversation-rpc-service
|
||||||
|
third: third-rpc-service
|
||||||
|
EOF
|
||||||
|
success " discovery.yml → etcd 127.0.0.1:${ETCD_PORT}"
|
||||||
|
|
||||||
|
# MongoDB(远程)
|
||||||
|
cat > "$OPENIM_CONF/mongodb.yml" <<EOF
|
||||||
|
uri: "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOST}:${MONGO_PORT}/${MONGO_DATABASE}?authSource=${MONGO_AUTHSOURCE}&directConnection=true"
|
||||||
|
address: []
|
||||||
|
database: ${MONGO_DATABASE}
|
||||||
|
username:
|
||||||
|
password:
|
||||||
|
authSource:
|
||||||
|
mongoMode: "standalone"
|
||||||
|
maxPoolSize: 100
|
||||||
|
maxRetry: 10
|
||||||
|
EOF
|
||||||
|
success " mongodb.yml → ${MONGO_HOST}:${MONGO_PORT}/${MONGO_DATABASE}"
|
||||||
|
|
||||||
|
# minio.yml(标记已切换至 S3,实际由 openim-rpc-third.yml 控制)
|
||||||
|
cat > "$OPENIM_CONF/minio.yml" <<EOF
|
||||||
|
# 已切换为 Amazon S3,存储驱动由 openim-rpc-third.yml 中 object.enable: aws 控制
|
||||||
|
bucket: ${OPENIM_AWS_BUCKET}
|
||||||
|
accessKeyID: ${OPENIM_AWS_ACCESS_KEY_ID}
|
||||||
|
secretAccessKey: ${OPENIM_AWS_SECRET_ACCESS_KEY}
|
||||||
|
sessionToken:
|
||||||
|
internalAddress: s3.${OPENIM_AWS_REGION}.amazonaws.com
|
||||||
|
externalAddress: https://s3.${OPENIM_AWS_REGION}.amazonaws.com
|
||||||
|
publicRead: ${OPENIM_AWS_PUBLIC_READ}
|
||||||
|
EOF
|
||||||
|
success " minio.yml → S3 (${OPENIM_AWS_REGION})"
|
||||||
|
|
||||||
|
# openim-rpc-third.yml — 切换 enable: aws,更新凭证
|
||||||
|
THIRD_CONF="$OPENIM_CONF/openim-rpc-third.yml"
|
||||||
|
if [[ -f "$THIRD_CONF" ]]; then
|
||||||
|
cp "$THIRD_CONF" "${THIRD_CONF}.bak"
|
||||||
|
|
||||||
|
python3 - "$THIRD_CONF" <<PYEOF
|
||||||
|
import re, sys
|
||||||
|
|
||||||
|
path = sys.argv[1]
|
||||||
|
with open(path) as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
content = re.sub(r'(\benable:\s*)\S+', r'\1aws', content)
|
||||||
|
|
||||||
|
endpoint_line = ""
|
||||||
|
endpoint_val = "${OPENIM_AWS_ENDPOINT:-}"
|
||||||
|
if endpoint_val:
|
||||||
|
endpoint_line = f" endpoint: {endpoint_val}\n"
|
||||||
|
|
||||||
|
new_aws = f""" aws:
|
||||||
|
{endpoint_line} region: ${OPENIM_AWS_REGION}
|
||||||
|
bucket: ${OPENIM_AWS_BUCKET}
|
||||||
|
accessKeyID: ${OPENIM_AWS_ACCESS_KEY_ID}
|
||||||
|
secretAccessKey: ${OPENIM_AWS_SECRET_ACCESS_KEY}
|
||||||
|
sessionToken:
|
||||||
|
publicRead: ${OPENIM_AWS_PUBLIC_READ}"""
|
||||||
|
|
||||||
|
content = re.sub(
|
||||||
|
r'(\s{{2}}aws:\n(?:[ \t]+\S[^\n]*\n?)*)',
|
||||||
|
'\n' + new_aws + '\n',
|
||||||
|
content
|
||||||
|
)
|
||||||
|
|
||||||
|
with open(path, 'w') as f:
|
||||||
|
f.write(content)
|
||||||
|
PYEOF
|
||||||
|
success " openim-rpc-third.yml → enable: aws, bucket=${OPENIM_AWS_BUCKET} (备份: .bak)"
|
||||||
|
else
|
||||||
|
warn " openim-rpc-third.yml 不存在,跳过"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# chat
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "chat 配置"
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
cat > "$CHAT_CONF/redis.yml" <<EOF
|
||||||
|
address: [ "127.0.0.1:${REDIS_PORT}" ]
|
||||||
|
username: ''
|
||||||
|
password: '${REDIS_PASSWORD}'
|
||||||
|
enablePipeline: false
|
||||||
|
clusterMode: false
|
||||||
|
db: 0
|
||||||
|
maxRetry: 10
|
||||||
|
EOF
|
||||||
|
success " redis.yml → 127.0.0.1:${REDIS_PORT}"
|
||||||
|
|
||||||
|
# Etcd
|
||||||
|
cat > "$CHAT_CONF/discovery.yml" <<EOF
|
||||||
|
enable: "etcd"
|
||||||
|
etcd:
|
||||||
|
rootDirectory: openim
|
||||||
|
address: [ "127.0.0.1:${ETCD_PORT}" ]
|
||||||
|
username: ''
|
||||||
|
password: ''
|
||||||
|
kubernetes:
|
||||||
|
namespace: default
|
||||||
|
rpcService:
|
||||||
|
chat: chat-rpc-service
|
||||||
|
admin: admin-rpc-service
|
||||||
|
bot: bot-rpc-service
|
||||||
|
EOF
|
||||||
|
success " discovery.yml → etcd 127.0.0.1:${ETCD_PORT}"
|
||||||
|
|
||||||
|
# MongoDB(远程)
|
||||||
|
cat > "$CHAT_CONF/mongodb.yml" <<EOF
|
||||||
|
uri: "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOST}:${MONGO_PORT}/${MONGO_DATABASE}?authSource=${MONGO_AUTHSOURCE}&directConnection=true"
|
||||||
|
address: []
|
||||||
|
database: ${MONGO_DATABASE}
|
||||||
|
username:
|
||||||
|
password:
|
||||||
|
authSource:
|
||||||
|
mongoMode: "standalone"
|
||||||
|
maxPoolSize: 100
|
||||||
|
maxRetry: 10
|
||||||
|
EOF
|
||||||
|
success " mongodb.yml → ${MONGO_HOST}:${MONGO_PORT}/${MONGO_DATABASE}"
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# meetingmsg
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "meetingmsg 配置"
|
||||||
|
|
||||||
|
MM_CONF="$ROOT_DIR/meetingmsg/manifest/config/config.yaml"
|
||||||
|
if [[ -f "$MM_CONF" ]]; then
|
||||||
|
cat > "$MM_CONF" <<EOF
|
||||||
|
server:
|
||||||
|
address: :8000
|
||||||
|
|
||||||
|
redis:
|
||||||
|
default:
|
||||||
|
address: 127.0.0.1:${REDIS_PORT}
|
||||||
|
pass: ${REDIS_PASSWORD}
|
||||||
|
|
||||||
|
logger:
|
||||||
|
level: all
|
||||||
|
stdout: true
|
||||||
|
EOF
|
||||||
|
success " config.yaml → redis 127.0.0.1:${REDIS_PORT}"
|
||||||
|
else
|
||||||
|
warn " meetingmsg config.yaml 不存在,跳过"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# openim-server webhooks.yml — 开启 afterSendGroupMsg 回调给 meetingmsg
|
||||||
|
# meetingmsg 接收路径: /event/:command(command=callbackAfterSendGroupMsgCommand)
|
||||||
|
WH_CONF="$OPENIM_CONF/webhooks.yml"
|
||||||
|
if [[ -f "$WH_CONF" ]]; then
|
||||||
|
cp "$WH_CONF" "${WH_CONF}.bak"
|
||||||
|
# 用 sed 只修改 url 和 afterSendGroupMsg.enable,其余保持默认
|
||||||
|
sed -i.tmp \
|
||||||
|
-e 's|^url:.*|url: http://127.0.0.1:8000/event/callbackAfterSendGroupMsgCommand|' \
|
||||||
|
"$WH_CONF"
|
||||||
|
# afterSendGroupMsg 的 enable 改为 true(精准匹配该段的第一个 enable: false)
|
||||||
|
python3 - "$WH_CONF" <<'PYEOF'
|
||||||
|
import sys, re
|
||||||
|
|
||||||
|
path = sys.argv[1]
|
||||||
|
content = open(path).read()
|
||||||
|
|
||||||
|
# 只把 afterSendGroupMsg 下的 enable: false 改为 true
|
||||||
|
content = re.sub(
|
||||||
|
r'(afterSendGroupMsg:\n(?:.*\n)*? enable:) false',
|
||||||
|
r'\1 true',
|
||||||
|
content, count=1
|
||||||
|
)
|
||||||
|
|
||||||
|
open(path, 'w').write(content)
|
||||||
|
print(" patched afterSendGroupMsg.enable = true")
|
||||||
|
PYEOF
|
||||||
|
rm -f "${WH_CONF}.tmp"
|
||||||
|
success " webhooks.yml → url=http://127.0.0.1:8000/event/callbackAfterSendGroupMsgCommand, afterSendGroupMsg.enable=true (备份: .bak)"
|
||||||
|
else
|
||||||
|
warn " webhooks.yml 不存在,跳过(meetingmsg 将收不到消息推送)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# livekit server(本地 Docker,复用 dev-redis)
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "livekit 配置"
|
||||||
|
|
||||||
|
LK_CONF="$ROOT_DIR/livekit/livekit.yaml"
|
||||||
|
if [[ -f "$LK_CONF" ]]; then
|
||||||
|
cp "$LK_CONF" "${LK_CONF}.bak"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > "$LK_CONF" <<EOF
|
||||||
|
port: 7880
|
||||||
|
|
||||||
|
bind_addresses:
|
||||||
|
- 0.0.0.0
|
||||||
|
|
||||||
|
rtc:
|
||||||
|
# WebRTC 媒体流直连端口(客户端直接连 node_ip)
|
||||||
|
port_range_start: 50000
|
||||||
|
port_range_end: 51000
|
||||||
|
tcp_port: 7882
|
||||||
|
use_external_ip: true
|
||||||
|
# 公网 IP(来自 .env.local → LIVEKIT_NODE_IP)
|
||||||
|
node_ip: ${LIVEKIT_NODE_IP}
|
||||||
|
enable_loopback_candidate: false
|
||||||
|
congestion_control:
|
||||||
|
enabled: true
|
||||||
|
probing_interval: 2000
|
||||||
|
|
||||||
|
# 复用 dev-redis 容器(host.docker.internal 可从 Docker 容器访问宿主机端口)
|
||||||
|
redis:
|
||||||
|
address: host.docker.internal:${REDIS_PORT}
|
||||||
|
password: ${REDIS_PASSWORD}
|
||||||
|
db: 0
|
||||||
|
|
||||||
|
# API 密钥(来自 .env.local → LIVEKIT_API_KEY: LIVEKIT_API_SECRET)
|
||||||
|
keys:
|
||||||
|
${LIVEKIT_API_KEY}: ${LIVEKIT_API_SECRET}
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level: info
|
||||||
|
json: false
|
||||||
|
sample: false
|
||||||
|
|
||||||
|
room:
|
||||||
|
auto_create: true
|
||||||
|
empty_timeout: 300
|
||||||
|
departure_timeout: 20
|
||||||
|
enable_remote_unmute: true
|
||||||
|
max_participants: 100
|
||||||
|
|
||||||
|
development: true
|
||||||
|
EOF
|
||||||
|
success " livekit/livekit.yaml → node_ip=${LIVEKIT_NODE_IP}, redis=host.docker.internal:${REDIS_PORT} (备份: .bak)"
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# livecloud
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "livecloud 配置"
|
||||||
|
|
||||||
|
LC_CONF="$ROOT_DIR/livecloud/config/config.yml"
|
||||||
|
if [[ -f "$LC_CONF" ]]; then
|
||||||
|
cp "$LC_CONF" "${LC_CONF}.bak"
|
||||||
|
cat > "$LC_CONF" <<EOF
|
||||||
|
server:
|
||||||
|
port: "8080"
|
||||||
|
env: "dev"
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
secret_key: "your-secret-key-change-in-production"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: ${REDIS_PORT}
|
||||||
|
password: ${REDIS_PASSWORD}
|
||||||
|
|
||||||
|
cloud:
|
||||||
|
default_provider: tencent
|
||||||
|
load_balance_strategy: round_robin
|
||||||
|
|
||||||
|
aliyun:
|
||||||
|
enabled: false
|
||||||
|
app_id: your_aliyun_app_id
|
||||||
|
app_key: your_aliyun_app_key
|
||||||
|
region: cn-hangzhou
|
||||||
|
push_domain: push.example.com
|
||||||
|
play_domain: play.example.com
|
||||||
|
|
||||||
|
tencent:
|
||||||
|
enabled: true
|
||||||
|
sdk_app_id: ${TENCENT_SDK_APP_ID}
|
||||||
|
sdk_secret_key: ${TENCENT_SDK_SECRET_KEY}
|
||||||
|
region: ap-guangzhou
|
||||||
|
push_domain: push-tx.example.com
|
||||||
|
play_domain: play-tx.example.com
|
||||||
|
EOF
|
||||||
|
success " livecloud/config/config.yml → redis=127.0.0.1:${REDIS_PORT}, tencent sdk_app_id=${TENCENT_SDK_APP_ID} (备份: .bak)"
|
||||||
|
else
|
||||||
|
warn " livecloud/config/config.yml 不存在,跳过"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# livestream
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "livestream 配置"
|
||||||
|
|
||||||
|
LS_CONF="$ROOT_DIR/livestream/config.yaml"
|
||||||
|
cp "${LS_CONF}" "${LS_CONF}.bak" 2>/dev/null || true
|
||||||
|
cat > "$LS_CONF" <<EOF
|
||||||
|
# LiveKit Server(来源: deploy/livekit-config/livekit.yaml → keys 段)
|
||||||
|
livekit:
|
||||||
|
url: "${LIVEKIT_URL}"
|
||||||
|
apiKey: "${LIVEKIT_API_KEY}"
|
||||||
|
apiSecret: "${LIVEKIT_API_SECRET}"
|
||||||
|
|
||||||
|
# Cloudflare Stream(用于直播推流 CDN)
|
||||||
|
cloudflare:
|
||||||
|
accountId: "${CF_ACCOUNT_ID}"
|
||||||
|
apiToken: "${CF_API_TOKEN}"
|
||||||
|
email: "${CF_EMAIL}"
|
||||||
|
apiKey: "${CF_API_KEY}"
|
||||||
|
customerCode: "${CF_CUSTOMER_CODE}"
|
||||||
|
EOF
|
||||||
|
success " livestream/config.yaml → livekit=${LIVEKIT_URL}, key=${LIVEKIT_API_KEY} (备份: .bak)"
|
||||||
|
[[ -z "${CF_ACCOUNT_ID}" ]] && warn " Cloudflare 配置为空,推流 CDN 功能不可用,请填写 .env.local 中 CF_* 变量"
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# build-server
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "build-server 配置"
|
||||||
|
|
||||||
|
BS_CONF="$ROOT_DIR/build-server/config/config.yaml"
|
||||||
|
if [[ -f "$BS_CONF" ]]; then
|
||||||
|
cp "$BS_CONF" "${BS_CONF}.bak"
|
||||||
|
|
||||||
|
# build-server 用同一个 MongoDB 连接,只是 dbname 不同
|
||||||
|
cat > "$BS_CONF" <<EOF
|
||||||
|
mode: debug
|
||||||
|
host: 0.0.0.0
|
||||||
|
port: 8281
|
||||||
|
|
||||||
|
server:
|
||||||
|
read_timeout: 30
|
||||||
|
write_timeout: 30
|
||||||
|
|
||||||
|
database:
|
||||||
|
host: ${MONGO_HOST}
|
||||||
|
port: ${MONGO_PORT}
|
||||||
|
user: ${MONGO_USERNAME}
|
||||||
|
password: ${MONGO_PASSWORD}
|
||||||
|
dbname: ${BUILD_MONGO_DATABASE}
|
||||||
|
auth_source: ${MONGO_AUTHSOURCE}
|
||||||
|
|
||||||
|
jwt:
|
||||||
|
secret: "your-secret-key-change-in-production"
|
||||||
|
expire_time: 24
|
||||||
|
|
||||||
|
upload:
|
||||||
|
path: "./uploads"
|
||||||
|
max_size: 5242880
|
||||||
|
allow_exts: "jpg,jpeg,png,gif,webp"
|
||||||
|
base_url: "http://localhost:8281"
|
||||||
|
|
||||||
|
aws:
|
||||||
|
access_key: "${BUILD_AWS_ACCESS_KEY}"
|
||||||
|
secret_key: "${BUILD_AWS_SECRET_KEY}"
|
||||||
|
region: "${BUILD_AWS_REGION}"
|
||||||
|
bucket: "${BUILD_AWS_BUCKET}"
|
||||||
|
EOF
|
||||||
|
success " build-server/config/config.yaml → ${MONGO_HOST}/${BUILD_MONGO_DATABASE}, S3 bucket=${BUILD_AWS_BUCKET} (备份: .bak)"
|
||||||
|
else
|
||||||
|
warn " build-server/config/config.yaml 不存在,跳过"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# meetingh5 — 生成 Vite 环境变量文件(测试服务器环境)
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "meetingh5 环境变量"
|
||||||
|
|
||||||
|
MH5_ENV="$ROOT_DIR/meetingh5/.env.local"
|
||||||
|
cat > "$MH5_ENV" <<EOF
|
||||||
|
# meetingh5 本地环境变量 — 由 deploy-test/02-patch-config.sh 生成,勿手动编辑
|
||||||
|
# 对应测试服务器(DEPLOY_TEST_IP=${DEPLOY_TEST_IP})
|
||||||
|
#
|
||||||
|
# 优先级:URL参数 ?ws= ?liveApi= > 以下变量 > 代码中的生产默认值
|
||||||
|
|
||||||
|
# 弹幕 WebSocket:meetingmsg 服务(:8000)
|
||||||
|
VITE_WS_BASE_URL=ws://${DEPLOY_TEST_IP}:8000
|
||||||
|
|
||||||
|
# 直播间 API:livestream 服务(:8081)
|
||||||
|
VITE_LIVE_API_BASE_URL=http://${DEPLOY_TEST_IP}:8081
|
||||||
|
EOF
|
||||||
|
success " meetingh5/.env.local → ws=${DEPLOY_TEST_IP}:8000, liveApi=${DEPLOY_TEST_IP}:8081"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
success "所有配置文件已更新!"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}已修改配置摘要:${NC}"
|
||||||
|
echo " Redis → 127.0.0.1:${REDIS_PORT} password=${REDIS_PASSWORD} (Docker)"
|
||||||
|
echo " Kafka → 127.0.0.1:${KAFKA_PORT} (Docker)"
|
||||||
|
echo " Etcd → 127.0.0.1:${ETCD_PORT} (Docker)"
|
||||||
|
echo " MongoDB → ${MONGO_HOST}:${MONGO_PORT} DB(openim)=${MONGO_DATABASE} DB(build)=${BUILD_MONGO_DATABASE}"
|
||||||
|
echo " LiveKit → ${LIVEKIT_URL} node_ip=${LIVEKIT_NODE_IP} key=${LIVEKIT_API_KEY}"
|
||||||
|
echo " Tencent RTC → sdk_app_id=${TENCENT_SDK_APP_ID}"
|
||||||
|
echo " S3 (openim) → s3://${OPENIM_AWS_BUCKET} region=${OPENIM_AWS_REGION}"
|
||||||
|
echo " S3 (build) → s3://${BUILD_AWS_BUCKET} region=${BUILD_AWS_REGION}"
|
||||||
|
echo " MeetingMsg → webhook afterSendGroupMsg=enabled → 127.0.0.1:8000"
|
||||||
|
echo " MeetingH5 → ws=${DEPLOY_TEST_IP}:8000, liveApi=${DEPLOY_TEST_IP}:8081"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}下一步:${NC}"
|
||||||
|
echo -e " 启动 Docker 基础设施(Redis/Kafka/Etcd):"
|
||||||
|
echo -e " ${CYAN}./deploy-test/03-start-infra.sh${NC}"
|
||||||
227
03-start-infra.sh
Executable file
227
03-start-infra.sh
Executable file
@@ -0,0 +1,227 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# 03-start-infra.sh — 启动 Docker 基础设施(Redis / Kafka / Etcd / LiveKit)
|
||||||
|
#
|
||||||
|
# 数据目录: .local-dev/docker-data/<svc>/
|
||||||
|
# 容器日志: .local-dev/docker-logs/<svc>/<svc>-YYYYMMDD.log(每日一文件)
|
||||||
|
# 脚本日志: .local-dev/script-logs/03-start-infra-<ts>.log
|
||||||
|
#
|
||||||
|
# 后续步骤:04-build.sh(编译后端服务)
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
load_env
|
||||||
|
init_dirs
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
require_docker_running
|
||||||
|
|
||||||
|
# Docker 日志驱动公共参数(JSON 文件,最多 5 个 50MB 轮转)
|
||||||
|
LOG_OPTS=(
|
||||||
|
--log-driver json-file
|
||||||
|
--log-opt max-size=50m
|
||||||
|
--log-opt max-file=5
|
||||||
|
)
|
||||||
|
|
||||||
|
header "步骤 3 / 5 — 启动 Docker 基础设施"
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Redis
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "Redis"
|
||||||
|
|
||||||
|
if docker ps --format '{{.Names}}' | grep -q '^dev-redis$'; then
|
||||||
|
success "Redis 已在运行 (container=dev-redis) :${REDIS_PORT}"
|
||||||
|
elif docker ps -a --format '{{.Names}}' | grep -q '^dev-redis$'; then
|
||||||
|
info "重新启动已有容器 dev-redis..."
|
||||||
|
docker start dev-redis > /dev/null
|
||||||
|
success "Redis 已启动 :${REDIS_PORT}"
|
||||||
|
else
|
||||||
|
info "创建并启动 Redis 容器..."
|
||||||
|
docker run -d \
|
||||||
|
--name dev-redis \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p "${REDIS_PORT}:6379" \
|
||||||
|
-v "${DATA_DIR}/redis:/data" \
|
||||||
|
"${LOG_OPTS[@]}" \
|
||||||
|
redis:7-alpine \
|
||||||
|
redis-server --requirepass "${REDIS_PASSWORD:-openIM123}" --appendonly yes \
|
||||||
|
> /dev/null
|
||||||
|
success "Redis 容器已创建并启动 :${REDIS_PORT} (密码: ${REDIS_PASSWORD:-openIM123})"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
if docker exec dev-redis redis-cli -a "${REDIS_PASSWORD:-openIM123}" ping 2>/dev/null | grep -q PONG; then
|
||||||
|
success "Redis 连通性: PONG ✓"
|
||||||
|
else
|
||||||
|
warn "Redis 未响应 PING,请查看日志: ./deploy-test/logs.sh redis"
|
||||||
|
fi
|
||||||
|
start_docker_logger "dev-redis"
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Kafka(KRaft 模式)
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "Kafka (KRaft)"
|
||||||
|
|
||||||
|
if docker ps --format '{{.Names}}' | grep -q '^dev-kafka$'; then
|
||||||
|
success "Kafka 已在运行 (container=dev-kafka) :${KAFKA_PORT}"
|
||||||
|
elif docker ps -a --format '{{.Names}}' | grep -q '^dev-kafka$'; then
|
||||||
|
info "重新启动已有容器 dev-kafka..."
|
||||||
|
docker start dev-kafka > /dev/null
|
||||||
|
info "等待 Kafka 就绪 (8s)..."
|
||||||
|
sleep 8
|
||||||
|
success "Kafka 已启动 :${KAFKA_PORT}"
|
||||||
|
else
|
||||||
|
info "创建并启动 Kafka 容器(首次拉取镜像可能较慢)..."
|
||||||
|
KAFKA_CLUSTER_ID="MkU3OEVBNTcwNTJENDM2Qk"
|
||||||
|
|
||||||
|
docker run -d \
|
||||||
|
--name dev-kafka \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p "${KAFKA_PORT}:9092" \
|
||||||
|
-v "${DATA_DIR}/kafka:/bitnami/kafka" \
|
||||||
|
-e KAFKA_CFG_NODE_ID=0 \
|
||||||
|
-e KAFKA_CFG_PROCESS_ROLES=controller,broker \
|
||||||
|
-e KAFKA_CFG_LISTENERS="PLAINTEXT://:9092,CONTROLLER://:9093" \
|
||||||
|
-e KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP="CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" \
|
||||||
|
-e KAFKA_CFG_CONTROLLER_QUORUM_VOTERS="0@localhost:9093" \
|
||||||
|
-e KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER \
|
||||||
|
-e KAFKA_CFG_ADVERTISED_LISTENERS="PLAINTEXT://127.0.0.1:${KAFKA_PORT}" \
|
||||||
|
-e KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true \
|
||||||
|
-e KAFKA_KRAFT_CLUSTER_ID="$KAFKA_CLUSTER_ID" \
|
||||||
|
"${LOG_OPTS[@]}" \
|
||||||
|
bitnami/kafka:3.7 \
|
||||||
|
> /dev/null
|
||||||
|
|
||||||
|
info "等待 Kafka 就绪 (15s)..."
|
||||||
|
sleep 15
|
||||||
|
success "Kafka 容器已创建并启动 :${KAFKA_PORT}"
|
||||||
|
fi
|
||||||
|
start_docker_logger "dev-kafka"
|
||||||
|
|
||||||
|
# 初始化必要 Topics
|
||||||
|
step "Kafka Topics 初始化"
|
||||||
|
TOPICS=(toRedis toMongo toPush toOfflinePush)
|
||||||
|
for topic in "${TOPICS[@]}"; do
|
||||||
|
if docker exec dev-kafka kafka-topics.sh \
|
||||||
|
--bootstrap-server localhost:9092 \
|
||||||
|
--list 2>/dev/null | grep -q "^${topic}$"; then
|
||||||
|
info " topic 已存在: $topic"
|
||||||
|
else
|
||||||
|
docker exec dev-kafka kafka-topics.sh \
|
||||||
|
--create \
|
||||||
|
--topic "$topic" \
|
||||||
|
--bootstrap-server localhost:9092 \
|
||||||
|
--partitions 8 \
|
||||||
|
--replication-factor 1 \
|
||||||
|
2>/dev/null \
|
||||||
|
&& success " ✓ 创建 topic: $topic" \
|
||||||
|
|| warn " ✗ 创建失败: $topic(Kafka 未就绪,重试: ./deploy-test/03-start-infra.sh)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Etcd
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "Etcd"
|
||||||
|
|
||||||
|
if docker ps --format '{{.Names}}' | grep -q '^dev-etcd$'; then
|
||||||
|
success "Etcd 已在运行 (container=dev-etcd) :${ETCD_PORT}"
|
||||||
|
elif docker ps -a --format '{{.Names}}' | grep -q '^dev-etcd$'; then
|
||||||
|
info "重新启动已有容器 dev-etcd..."
|
||||||
|
docker start dev-etcd > /dev/null
|
||||||
|
sleep 2
|
||||||
|
success "Etcd 已启动 :${ETCD_PORT}"
|
||||||
|
else
|
||||||
|
info "创建并启动 Etcd 容器..."
|
||||||
|
docker run -d \
|
||||||
|
--name dev-etcd \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p "${ETCD_PORT}:2379" \
|
||||||
|
-v "${DATA_DIR}/etcd:/etcd-data" \
|
||||||
|
-e ALLOW_NONE_AUTHENTICATION=yes \
|
||||||
|
-e ETCD_DATA_DIR=/etcd-data \
|
||||||
|
"${LOG_OPTS[@]}" \
|
||||||
|
bitnami/etcd:3.5 \
|
||||||
|
> /dev/null
|
||||||
|
sleep 2
|
||||||
|
success "Etcd 容器已创建并启动 :${ETCD_PORT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if docker exec dev-etcd etcdctl endpoint health 2>/dev/null | grep -q 'is healthy'; then
|
||||||
|
success "Etcd 连通性: healthy ✓"
|
||||||
|
else
|
||||||
|
warn "Etcd 未返回健康状态,可能仍在启动中"
|
||||||
|
fi
|
||||||
|
start_docker_logger "dev-etcd"
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# LiveKit(本地 Docker 容器,复用 dev-redis)
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
step "LiveKit"
|
||||||
|
|
||||||
|
LK_CONF="$ROOT_DIR/livekit/livekit.yaml"
|
||||||
|
|
||||||
|
if [[ ! -f "$LK_CONF" ]]; then
|
||||||
|
warn "livekit/livekit.yaml 不存在,请先执行: ./deploy-test/02-patch-config.sh"
|
||||||
|
else
|
||||||
|
if docker ps --format '{{.Names}}' | grep -q '^dev-livekit$'; then
|
||||||
|
success "LiveKit 已在运行 (container=dev-livekit) :7880"
|
||||||
|
elif docker ps -a --format '{{.Names}}' | grep -q '^dev-livekit$'; then
|
||||||
|
info "重新启动已有容器 dev-livekit..."
|
||||||
|
docker start dev-livekit > /dev/null
|
||||||
|
sleep 2
|
||||||
|
success "LiveKit 已启动 :7880"
|
||||||
|
else
|
||||||
|
info "创建并启动 LiveKit 容器(首次拉取镜像需要一点时间)..."
|
||||||
|
|
||||||
|
EXTRA_HOSTS=""
|
||||||
|
[[ "$(uname -s)" == "Linux" ]] && EXTRA_HOSTS="--add-host host.docker.internal:host-gateway"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
docker run -d \
|
||||||
|
--name dev-livekit \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 7880:7880 \
|
||||||
|
-p 7882:7882/tcp \
|
||||||
|
-p 7882:7882/udp \
|
||||||
|
-p 50000-51000:50000-51000/udp \
|
||||||
|
-v "${LK_CONF}:/etc/livekit.yaml:ro" \
|
||||||
|
"${LOG_OPTS[@]}" \
|
||||||
|
$EXTRA_HOSTS \
|
||||||
|
livekit/livekit-server:latest \
|
||||||
|
--config /etc/livekit.yaml \
|
||||||
|
> /dev/null
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
if docker ps --format '{{.Names}}' | grep -q '^dev-livekit$'; then
|
||||||
|
success "LiveKit 容器已创建并启动 :7880"
|
||||||
|
info " 公网 IP: ${LIVEKIT_NODE_IP} (WebRTC 媒体流直连)"
|
||||||
|
info " API Key: ${LIVEKIT_API_KEY}"
|
||||||
|
else
|
||||||
|
error "LiveKit 启动失败,查看日志: ./deploy-test/logs.sh livekit"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
start_docker_logger "dev-livekit"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 汇总
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}基础设施状态:${NC}"
|
||||||
|
print_container_status "Redis" "dev-redis" "${REDIS_PORT}"
|
||||||
|
print_container_status "Kafka" "dev-kafka" "${KAFKA_PORT}"
|
||||||
|
print_container_status "Etcd" "dev-etcd" "${ETCD_PORT}"
|
||||||
|
print_container_status "LiveKit" "dev-livekit" "7880"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}日志目录:${NC}"
|
||||||
|
echo " Docker 容器日志: $DOCKER_LOG_DIR/"
|
||||||
|
echo " 本脚本执行日志: $_CURRENT_SCRIPT_LOG"
|
||||||
|
echo ""
|
||||||
|
echo -e " LiveKit 公网: ${LIVEKIT_NODE_IP}:50000-51000/udp (WebRTC 媒体流)"
|
||||||
|
echo ""
|
||||||
|
success "Docker 基础设施已就绪!"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}下一步:${NC}"
|
||||||
|
echo -e " 编译所有后端 Go 服务:"
|
||||||
|
echo -e " ${CYAN}./deploy-test/04-build.sh${NC}"
|
||||||
104
04-build.sh
Executable file
104
04-build.sh
Executable file
@@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# 04-build.sh — 编译所有后端 Go 服务
|
||||||
|
#
|
||||||
|
# 编译产物输出至 .local-dev/bin/
|
||||||
|
# 支持只编译单个服务:./04-build.sh [service-name]
|
||||||
|
#
|
||||||
|
# 可用服务名: openim-server, chat-rpc, admin-rpc, chat-api, admin-api,
|
||||||
|
# meetingmsg, livecloud, livestream
|
||||||
|
#
|
||||||
|
# 后续步骤:05-start.sh(启动后端服务)
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
init_dirs
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
require_tools go
|
||||||
|
|
||||||
|
header "步骤 4 / 5 — 编译后端 Go 服务"
|
||||||
|
|
||||||
|
# 编译单个服务的函数
|
||||||
|
# 用法: _build <output-name> <source-dir> <package>
|
||||||
|
_build() {
|
||||||
|
local name="$1" dir="$ROOT_DIR/$2" pkg="$3"
|
||||||
|
local out="$BUILD_DIR/$name"
|
||||||
|
|
||||||
|
info "编译 ${BOLD}$name${NC} ..."
|
||||||
|
local start_ts=$SECONDS
|
||||||
|
|
||||||
|
if (cd "$dir" && go build -o "$out" "$pkg"); then
|
||||||
|
local elapsed=$(( SECONDS - start_ts ))
|
||||||
|
success " ✓ $name → $out (${elapsed}s)"
|
||||||
|
else
|
||||||
|
error " ✗ $name 编译失败"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 服务列表:名称 | 源码目录(相对 ROOT) | 包路径
|
||||||
|
declare -A SVC_DIR=(
|
||||||
|
[openim-server]="open-im-server"
|
||||||
|
[chat-rpc]="chat"
|
||||||
|
[admin-rpc]="chat"
|
||||||
|
[chat-api]="chat"
|
||||||
|
[admin-api]="chat"
|
||||||
|
[meetingmsg]="meetingmsg"
|
||||||
|
[livecloud]="livecloud"
|
||||||
|
[livestream]="livestream"
|
||||||
|
[build-server]="build-server"
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A SVC_PKG=(
|
||||||
|
[openim-server]="./cmd/main.go"
|
||||||
|
[chat-rpc]="./cmd/rpc/chat-rpc/"
|
||||||
|
[admin-rpc]="./cmd/rpc/admin-rpc/"
|
||||||
|
[chat-api]="./cmd/api/chat-api/"
|
||||||
|
[admin-api]="./cmd/api/admin-api/"
|
||||||
|
[meetingmsg]="."
|
||||||
|
[livecloud]="."
|
||||||
|
[livestream]="."
|
||||||
|
[build-server]="."
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── 判断是编译单个还是全部 ────────────────────────────────────────────────────
|
||||||
|
TARGET="${1:-all}"
|
||||||
|
|
||||||
|
if [[ "$TARGET" == "all" ]]; then
|
||||||
|
step "编译全部服务(共 ${#SVC_DIR[@]} 个)"
|
||||||
|
FAILED=()
|
||||||
|
for svc in openim-server chat-rpc admin-rpc chat-api admin-api meetingmsg livecloud livestream build-server; do
|
||||||
|
_build "$svc" "${SVC_DIR[$svc]}" "${SVC_PKG[$svc]}" || FAILED+=("$svc")
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [[ ${#FAILED[@]} -eq 0 ]]; then
|
||||||
|
success "所有服务编译完成!"
|
||||||
|
ls -lh "$BUILD_DIR/" | awk 'NR>1 {printf " %-20s %s\n", $NF, $5}'
|
||||||
|
else
|
||||||
|
error "以下服务编译失败: ${FAILED[*]}"
|
||||||
|
echo ""
|
||||||
|
echo "排查建议:"
|
||||||
|
echo " 1. cd $ROOT_DIR/<service-dir> && go mod tidy"
|
||||||
|
echo " 2. 检查 Go 版本: go version(推荐 1.21+)"
|
||||||
|
echo " 3. 检查模块依赖: go mod download"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# 编译单个服务
|
||||||
|
if [[ -z "${SVC_DIR[$TARGET]:-}" ]]; then
|
||||||
|
error "未知服务: $TARGET"
|
||||||
|
echo "可用: ${!SVC_DIR[*]}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
step "编译单个服务: $TARGET"
|
||||||
|
_build "$TARGET" "${SVC_DIR[$TARGET]}" "${SVC_PKG[$TARGET]}"
|
||||||
|
success "$TARGET 编译完成"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}下一步:${NC}"
|
||||||
|
echo -e " 启动所有后端服务:"
|
||||||
|
echo -e " ${CYAN}./deploy-test/05-start.sh${NC}"
|
||||||
|
echo -e " 或只启动单个服务:"
|
||||||
|
echo -e " ${CYAN}./deploy-test/05-start.sh openim-server${NC}"
|
||||||
138
05-start.sh
Executable file
138
05-start.sh
Executable file
@@ -0,0 +1,138 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# 05-start.sh — 启动后端 Go 服务
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# ./05-start.sh # 按依赖顺序启动全部服务
|
||||||
|
# ./05-start.sh <svc> # 只启动指定服务
|
||||||
|
#
|
||||||
|
# 启动顺序(有依赖关系):
|
||||||
|
# openim-server → chat-rpc / admin-rpc → chat-api / admin-api
|
||||||
|
# → meetingmsg / livecloud / livestream
|
||||||
|
#
|
||||||
|
# 日志文件: .local-dev/logs/<service>.log
|
||||||
|
# PID 文件: .local-dev/pids/<service>.pid
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
load_env
|
||||||
|
init_dirs
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
header "步骤 5 / 5 — 启动后端服务"
|
||||||
|
|
||||||
|
TARGET="${1:-all}"
|
||||||
|
|
||||||
|
# ── 服务启动配置 ─────────────────────────────────────────────────────────────
|
||||||
|
# 格式: svc_workdir[name] svc_args[name]
|
||||||
|
declare -A svc_workdir=(
|
||||||
|
[openim-server]="$ROOT_DIR/open-im-server"
|
||||||
|
[chat-rpc]="$ROOT_DIR/chat"
|
||||||
|
[admin-rpc]="$ROOT_DIR/chat"
|
||||||
|
[chat-api]="$ROOT_DIR/chat"
|
||||||
|
[admin-api]="$ROOT_DIR/chat"
|
||||||
|
[meetingmsg]="$ROOT_DIR/meetingmsg"
|
||||||
|
[livecloud]="$ROOT_DIR/livecloud"
|
||||||
|
[livestream]="$ROOT_DIR/livestream"
|
||||||
|
[build-server]="$ROOT_DIR/build-server"
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A svc_args=(
|
||||||
|
[openim-server]="-c $ROOT_DIR/open-im-server/config"
|
||||||
|
[chat-rpc]="-c $ROOT_DIR/chat/config"
|
||||||
|
[admin-rpc]="-c $ROOT_DIR/chat/config"
|
||||||
|
[chat-api]="-c $ROOT_DIR/chat/config"
|
||||||
|
[admin-api]="-c $ROOT_DIR/chat/config"
|
||||||
|
[meetingmsg]=""
|
||||||
|
[livecloud]=""
|
||||||
|
[livestream]=""
|
||||||
|
[build-server]=""
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A svc_desc=(
|
||||||
|
[openim-server]=":10002 (API) :10001 (MsgGateway WS)"
|
||||||
|
[chat-rpc]="内部 RPC"
|
||||||
|
[admin-rpc]="内部 RPC"
|
||||||
|
[chat-api]=":10008"
|
||||||
|
[admin-api]=":10009"
|
||||||
|
[meetingmsg]=":8000 (WS)"
|
||||||
|
[livecloud]=":8080"
|
||||||
|
[livestream]=":8081"
|
||||||
|
[build-server]=":8281"
|
||||||
|
)
|
||||||
|
|
||||||
|
_start_one() {
|
||||||
|
local svc="$1"
|
||||||
|
start_svc "$svc" \
|
||||||
|
"$BUILD_DIR/$svc" \
|
||||||
|
"${svc_args[$svc]}" \
|
||||||
|
"${svc_workdir[$svc]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 启动全部(有序) ──────────────────────────────────────────────────────────
|
||||||
|
_start_all() {
|
||||||
|
step "第 1 组: openim-server(核心 IM 服务)"
|
||||||
|
_start_one openim-server
|
||||||
|
|
||||||
|
info "等待 openim-server 将 RPC 注册到 Etcd... (8s)"
|
||||||
|
sleep 8
|
||||||
|
|
||||||
|
step "第 2 组: chat RPC 服务"
|
||||||
|
_start_one chat-rpc
|
||||||
|
_start_one admin-rpc
|
||||||
|
|
||||||
|
info "等待 chat RPC 注册到 Etcd... (4s)"
|
||||||
|
sleep 4
|
||||||
|
|
||||||
|
step "第 3 组: chat API 服务"
|
||||||
|
_start_one chat-api
|
||||||
|
_start_one admin-api
|
||||||
|
|
||||||
|
step "第 4 组: 业务服务"
|
||||||
|
_start_one meetingmsg
|
||||||
|
_start_one livecloud
|
||||||
|
_start_one livestream
|
||||||
|
_start_one build-server
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}服务汇总:${NC}"
|
||||||
|
for svc in openim-server chat-rpc admin-rpc chat-api admin-api meetingmsg livecloud livestream build-server; do
|
||||||
|
print_svc_status "$svc" "${svc_desc[$svc]}"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}常用地址:${NC}"
|
||||||
|
echo " IM API: http://localhost:10002"
|
||||||
|
echo " IM WebSocket: ws://localhost:10001"
|
||||||
|
echo " Chat API: http://localhost:10008"
|
||||||
|
echo " Admin API: http://localhost:10009"
|
||||||
|
echo " MeetingMsg WS: ws://localhost:8000"
|
||||||
|
echo " Livecloud: http://localhost:8080"
|
||||||
|
echo " Livestream: http://localhost:8081"
|
||||||
|
echo " Build Server: http://localhost:8281"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}查看日志:${NC}"
|
||||||
|
echo -e " ${CYAN}./deploy-test/logs.sh openim-server${NC}"
|
||||||
|
echo -e " ${CYAN}./deploy-test/logs.sh chat-api${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 启动单个 ──────────────────────────────────────────────────────────────────
|
||||||
|
_start_single() {
|
||||||
|
local svc="$1"
|
||||||
|
if [[ -z "${svc_workdir[$svc]:-}" ]]; then
|
||||||
|
error "未知服务: $svc"
|
||||||
|
echo "可用: ${!svc_workdir[*]}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
step "启动: $svc"
|
||||||
|
_start_one "$svc"
|
||||||
|
echo ""
|
||||||
|
print_svc_status "$svc" "${svc_desc[$svc]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 入口 ─────────────────────────────────────────────────────────────────────
|
||||||
|
if [[ "$TARGET" == "all" ]]; then
|
||||||
|
_start_all
|
||||||
|
else
|
||||||
|
_start_single "$TARGET"
|
||||||
|
fi
|
||||||
95
06-install-frontend.sh
Executable file
95
06-install-frontend.sh
Executable file
@@ -0,0 +1,95 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# 06-install-frontend.sh — 安装前端项目依赖
|
||||||
|
#
|
||||||
|
# 首次使用或依赖变更后运行(对应后端的 04-build.sh)
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# ./06-install-frontend.sh # 安装全部前端项目依赖
|
||||||
|
# ./06-install-frontend.sh <project> # 只安装指定项目
|
||||||
|
#
|
||||||
|
# 可用项目: pc, meetingh5, h5, cms, build-cms, build-down
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
init_dirs
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
header "前端依赖安装"
|
||||||
|
|
||||||
|
# ── 工具检查 ──────────────────────────────────────────────────────────────────
|
||||||
|
_check_pm() {
|
||||||
|
local pm="$1"
|
||||||
|
if ! command -v "$pm" &>/dev/null; then
|
||||||
|
error "$pm 未安装"
|
||||||
|
case "$pm" in
|
||||||
|
yarn) echo " → npm install -g yarn" ;;
|
||||||
|
pnpm) echo " → npm install -g pnpm" ;;
|
||||||
|
npm) echo " → https://nodejs.org/" ;;
|
||||||
|
esac
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 安装单个项目 ──────────────────────────────────────────────────────────────
|
||||||
|
# 用法: _install <显示名> <目录> <包管理器>
|
||||||
|
_install() {
|
||||||
|
local name="$1" dir="$ROOT_DIR/$2" pm="$3"
|
||||||
|
|
||||||
|
if [[ ! -d "$dir" ]]; then
|
||||||
|
warn " 目录不存在,跳过: $dir"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
_check_pm "$pm" || return 1
|
||||||
|
|
||||||
|
local logfile="$LOG_DIR/install-${name}.log"
|
||||||
|
info "安装 ${BOLD}$name${NC} 依赖 ($pm install) ..."
|
||||||
|
|
||||||
|
if [[ -d "$dir/node_modules" ]]; then
|
||||||
|
warn " node_modules 已存在,执行增量安装(如需全量重装请先 rm -rf $dir/node_modules)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local start_ts=$SECONDS
|
||||||
|
(cd "$dir" && "$pm" install 2>&1) | tee -a "$logfile" | tail -5
|
||||||
|
local elapsed=$(( SECONDS - start_ts ))
|
||||||
|
|
||||||
|
success " ✓ $name 安装完成 (${elapsed}s) → 日志: $logfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 项目列表 ──────────────────────────────────────────────────────────────────
|
||||||
|
TARGET="${1:-all}"
|
||||||
|
|
||||||
|
case "$TARGET" in
|
||||||
|
all)
|
||||||
|
step "安装全部前端项目依赖(共 6 个)"
|
||||||
|
_install "pc" "pc" "yarn"
|
||||||
|
_install "meetingh5" "meetingh5" "npm"
|
||||||
|
_install "h5" "h5" "npm"
|
||||||
|
_install "cms" "cms" "pnpm"
|
||||||
|
_install "build-cms" "build-cms" "pnpm"
|
||||||
|
_install "build-down" "build-down" "npm"
|
||||||
|
echo ""
|
||||||
|
success "所有前端依赖安装完成!"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}下一步:${NC}"
|
||||||
|
echo -e " 启动前端开发服务器:"
|
||||||
|
echo -e " ${CYAN}./deploy-test/07-start-frontend.sh${NC}"
|
||||||
|
;;
|
||||||
|
pc|meetingh5|h5|cms|build-cms|build-down)
|
||||||
|
local PM
|
||||||
|
case "$TARGET" in
|
||||||
|
pc) PM="yarn" ;;
|
||||||
|
cms|build-cms) PM="pnpm" ;;
|
||||||
|
*) PM="npm" ;;
|
||||||
|
esac
|
||||||
|
step "安装 $TARGET 依赖"
|
||||||
|
_install "$TARGET" "$TARGET" "$PM"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error "未知项目: $TARGET"
|
||||||
|
echo "可用: pc, meetingh5, h5, cms, build-cms, build-down"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
196
07-start-frontend.sh
Executable file
196
07-start-frontend.sh
Executable file
@@ -0,0 +1,196 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# 07-start-frontend.sh — 启动前端开发服务器
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# ./07-start-frontend.sh # 启动全部前端项目
|
||||||
|
# ./07-start-frontend.sh <project> # 只启动指定项目
|
||||||
|
#
|
||||||
|
# 项目与端口:
|
||||||
|
# pc → Electron + Vite :7777
|
||||||
|
# meetingh5 → React + Vite :5188
|
||||||
|
# h5 → Vue + Vite :3003
|
||||||
|
# cms → UMI Max :8001
|
||||||
|
# build-cms → UMI Max :8002
|
||||||
|
# build-down → UMI v3 :8003
|
||||||
|
#
|
||||||
|
# 日志文件: .local-dev/logs/fe-<project>.log
|
||||||
|
# PID 文件: .local-dev/pids/fe-<project>.pid
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
init_dirs
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
header "启动前端开发服务器"
|
||||||
|
|
||||||
|
# ── 前端服务配置 ──────────────────────────────────────────────────────────────
|
||||||
|
# name → (目录, 包管理器, 启动命令, 环境变量, 端口描述)
|
||||||
|
declare -A FE_DIR=(
|
||||||
|
[pc]="pc"
|
||||||
|
[meetingh5]="meetingh5"
|
||||||
|
[h5]="h5"
|
||||||
|
[cms]="cms"
|
||||||
|
[build-cms]="build-cms"
|
||||||
|
[build-down]="build-down"
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A FE_PM=(
|
||||||
|
[pc]="yarn"
|
||||||
|
[meetingh5]="npm"
|
||||||
|
[h5]="npm"
|
||||||
|
[cms]="pnpm"
|
||||||
|
[build-cms]="pnpm"
|
||||||
|
[build-down]="npm"
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A FE_CMD=(
|
||||||
|
[pc]="yarn dev"
|
||||||
|
[meetingh5]="npm run dev"
|
||||||
|
[h5]="npm run dev"
|
||||||
|
[cms]="pnpm run dev"
|
||||||
|
[build-cms]="pnpm run dev"
|
||||||
|
[build-down]="npm run dev"
|
||||||
|
)
|
||||||
|
|
||||||
|
# cms/build-cms/build-down 不配置端口时默认都是 8000,需手动指定
|
||||||
|
declare -A FE_ENV=(
|
||||||
|
[pc]=""
|
||||||
|
[meetingh5]=""
|
||||||
|
[h5]=""
|
||||||
|
[cms]="PORT=8001"
|
||||||
|
[build-cms]="PORT=8002"
|
||||||
|
[build-down]="PORT=8003"
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A FE_PORT=(
|
||||||
|
[pc]=":7777 (Electron Vite 调试服务器)"
|
||||||
|
[meetingh5]=":5188"
|
||||||
|
[h5]=":3003"
|
||||||
|
[cms]=":8001"
|
||||||
|
[build-cms]=":8002"
|
||||||
|
[build-down]=":8003"
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── 启动单个前端服务 ──────────────────────────────────────────────────────────
|
||||||
|
_start_fe() {
|
||||||
|
local name="$1"
|
||||||
|
local dir="$ROOT_DIR/${FE_DIR[$name]}"
|
||||||
|
local pm="${FE_PM[$name]}"
|
||||||
|
local cmd="${FE_CMD[$name]}"
|
||||||
|
local env_prefix="${FE_ENV[$name]}"
|
||||||
|
local pidfile="$PID_DIR/fe-${name}.pid"
|
||||||
|
local logfile="$LOG_DIR/fe-${name}.log"
|
||||||
|
|
||||||
|
# 已在运行
|
||||||
|
if [[ -f "$pidfile" ]] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then
|
||||||
|
warn "$name 已在运行 (PID=$(cat "$pidfile")),跳过"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 目录检查
|
||||||
|
if [[ ! -d "$dir" ]]; then
|
||||||
|
warn "$name 目录不存在 ($dir),跳过"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 依赖检查
|
||||||
|
if [[ ! -d "$dir/node_modules" ]]; then
|
||||||
|
warn "$name node_modules 不存在,请先执行: ./deploy-test/06-install-frontend.sh $name"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 包管理器检查
|
||||||
|
if ! command -v "$pm" &>/dev/null; then
|
||||||
|
error "$name 需要 $pm,未安装"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "启动 ${BOLD}$name${NC} ..."
|
||||||
|
|
||||||
|
# 写日志分隔符
|
||||||
|
{
|
||||||
|
echo ""
|
||||||
|
echo "──── 启动 $(date '+%Y-%m-%d %H:%M:%S') ────"
|
||||||
|
} >> "$logfile"
|
||||||
|
|
||||||
|
# 后台启动(带环境变量前缀)
|
||||||
|
(
|
||||||
|
cd "$dir"
|
||||||
|
if [[ -n "$env_prefix" ]]; then
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
nohup env $env_prefix $cmd >> "$logfile" 2>&1 &
|
||||||
|
else
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
nohup $cmd >> "$logfile" 2>&1 &
|
||||||
|
fi
|
||||||
|
echo $! > "$pidfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
if [[ -f "$pidfile" ]] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then
|
||||||
|
success " ✓ $name (PID=$(cat "$pidfile")) ${FE_PORT[$name]} → $logfile"
|
||||||
|
else
|
||||||
|
error " ✗ $name 启动失败,查看日志:"
|
||||||
|
tail -20 "$logfile" 2>/dev/null || true
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 入口 ─────────────────────────────────────────────────────────────────────
|
||||||
|
TARGET="${1:-all}"
|
||||||
|
FE_PROJECTS=(pc meetingh5 h5 cms build-cms build-down)
|
||||||
|
|
||||||
|
_all_valid() {
|
||||||
|
for p in "${FE_PROJECTS[@]}"; do
|
||||||
|
[[ "$p" == "$1" ]] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$TARGET" == "all" ]]; then
|
||||||
|
step "启动全部前端开发服务器"
|
||||||
|
FAILED=()
|
||||||
|
for proj in "${FE_PROJECTS[@]}"; do
|
||||||
|
_start_fe "$proj" || FAILED+=("$proj")
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}前端服务汇总:${NC}"
|
||||||
|
for proj in "${FE_PROJECTS[@]}"; do
|
||||||
|
local_pidfile="$PID_DIR/fe-${proj}.pid"
|
||||||
|
if [[ -f "$local_pidfile" ]] && kill -0 "$(cat "$local_pidfile")" 2>/dev/null; then
|
||||||
|
printf " ${GREEN}●${NC} %-14s PID=%-7s %s\n" "$proj" "$(cat "$local_pidfile")" "${FE_PORT[$proj]}"
|
||||||
|
else
|
||||||
|
printf " ${RED}○${NC} %-14s 未运行 %s\n" "$proj" "${FE_PORT[$proj]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}访问地址:${NC}"
|
||||||
|
echo " PC (Electron): yarn dev 启动后自动打开窗口"
|
||||||
|
echo " H5: http://${DEPLOY_TEST_IP}:3003"
|
||||||
|
echo " CMS: http://${DEPLOY_TEST_IP}:8001"
|
||||||
|
echo " Build CMS: http://${DEPLOY_TEST_IP}:8002"
|
||||||
|
echo " Build Download: http://${DEPLOY_TEST_IP}:8003"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}MeetingH5 访问地址(后端 URL 由 .env.local 默认设置,也可通过 URL 参数覆盖):${NC}"
|
||||||
|
echo " 默认: http://${DEPLOY_TEST_IP}:5188"
|
||||||
|
echo " 显式指定后端: http://${DEPLOY_TEST_IP}:5188?ws=ws://${DEPLOY_TEST_IP}:8000&liveApi=http://${DEPLOY_TEST_IP}:8081"
|
||||||
|
echo " 说明: ws → meetingmsg 弹幕 WebSocket (:8000)"
|
||||||
|
echo " liveApi → livestream 直播间 API (:8081)"
|
||||||
|
|
||||||
|
if [[ ${#FAILED[@]} -gt 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
warn "以下项目启动失败: ${FAILED[*]}"
|
||||||
|
echo " 可能原因: node_modules 未安装,执行: ./deploy-test/06-install-frontend.sh"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if ! _all_valid "$TARGET"; then
|
||||||
|
error "未知项目: $TARGET"
|
||||||
|
echo "可用: ${FE_PROJECTS[*]}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
step "启动前端项目: $TARGET"
|
||||||
|
_start_fe "$TARGET"
|
||||||
|
fi
|
||||||
280
README.md
Normal file
280
README.md
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
# deploy-test — 测试服务器部署脚本集
|
||||||
|
|
||||||
|
> **适用场景**:部署在**有公网 IP 的测试服务器**上。所有服务(后端、前端、Docker 基础设施、LiveKit)均在本机运行。
|
||||||
|
>
|
||||||
|
> 如果你在本机 Mac 开发,请使用 `deploy-local/` 目录。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 两套环境对比
|
||||||
|
|
||||||
|
| 项目 | deploy-test(本目录)| deploy-local/ |
|
||||||
|
|------|---------------------|----------------|
|
||||||
|
| 适用机器 | 测试服务器(有公网 IP) | 本机 Mac(无公网 IP) |
|
||||||
|
| 配置文件 | `.env.deploy-test` | `.env.deploy-local` |
|
||||||
|
| 运行时目录 | `.deploy-test/` | `.deploy-local/` |
|
||||||
|
| LiveKit | 本机 Docker 启动,使用公网 IP | 指向本目录服务器的 LiveKit |
|
||||||
|
| Redis/Kafka/Etcd | 本机 Docker | 本机 Docker |
|
||||||
|
| 后端服务 | 本机进程 | 本机进程 |
|
||||||
|
| 前端服务 | 本机进程(可选) | 本机进程 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
deploy-test/
|
||||||
|
├── common.sh # 公共函数库(路径、日志函数)
|
||||||
|
├── 01-init-env.sh # 步骤1:生成 .env.deploy-test 配置模板
|
||||||
|
├── 02-patch-config.sh # 步骤2:将 .env.deploy-test 写入各服务 YAML
|
||||||
|
├── 03-start-infra.sh # 步骤3:启动 Docker 容器(Redis/Kafka/Etcd/LiveKit)
|
||||||
|
├── 04-build.sh # 步骤4:编译所有后端 Go 服务
|
||||||
|
├── 05-start.sh # 步骤5:启动所有后端服务
|
||||||
|
├── 06-install-frontend.sh # 步骤6:安装前端依赖(可选)
|
||||||
|
├── 07-start-frontend.sh # 步骤7:启动前端开发服务器(可选)
|
||||||
|
├── stop.sh # 停止后端服务
|
||||||
|
├── stop-infra.sh # 停止 Docker 容器(含 LiveKit)
|
||||||
|
├── stop-frontend.sh # 停止前端服务
|
||||||
|
├── remove-infra.sh # 删除 Docker 容器及数据(危险!)
|
||||||
|
├── restart.sh # 重启指定服务(支持 --build)
|
||||||
|
├── status.sh # 查看所有服务状态
|
||||||
|
├── logs.sh # 查看日志(统一入口)
|
||||||
|
├── check-conn.sh # 验证 MongoDB / S3 连接
|
||||||
|
└── setup.sh # 一键完整部署(首次使用)
|
||||||
|
```
|
||||||
|
|
||||||
|
运行时目录(`.deploy-test/`,已加入 `.gitignore`):
|
||||||
|
|
||||||
|
```
|
||||||
|
.deploy-test/
|
||||||
|
├── bin/ # Go 编译产物
|
||||||
|
├── pids/ # PID 文件
|
||||||
|
├── logs/ # 后端/前端服务日志
|
||||||
|
├── docker-data/ # Docker 数据卷
|
||||||
|
├── docker-logs/ # Docker 容器日志(按日期滚动)
|
||||||
|
└── script-logs/ # 脚本执行日志(带时间戳)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 首次使用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 一键执行(推荐)
|
||||||
|
./deploy-test/setup.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 分步执行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 生成配置模板
|
||||||
|
./deploy-test/01-init-env.sh
|
||||||
|
|
||||||
|
# 2. 修改配置(重要:确认 DEPLOY_TEST_IP 等信息正确)
|
||||||
|
vim .env.deploy-test
|
||||||
|
|
||||||
|
# 3. 将配置写入各服务 YAML(包括 livekit/livekit.yaml)
|
||||||
|
./deploy-test/02-patch-config.sh
|
||||||
|
|
||||||
|
# 4. 启动 Docker 基础设施(Redis / Kafka / Etcd / LiveKit)
|
||||||
|
./deploy-test/03-start-infra.sh
|
||||||
|
|
||||||
|
# 5. 编译后端服务
|
||||||
|
./deploy-test/04-build.sh
|
||||||
|
|
||||||
|
# 6. 启动后端服务
|
||||||
|
./deploy-test/05-start.sh
|
||||||
|
|
||||||
|
# 7. 安装前端依赖(可选)
|
||||||
|
./deploy-test/06-install-frontend.sh
|
||||||
|
|
||||||
|
# 8. 启动前端开发服务器(可选)
|
||||||
|
./deploy-test/07-start-frontend.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配置文件(`.env.deploy-test`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ══ 测试服务器公网 IP ═══════════════════════════════════════════
|
||||||
|
DEPLOY_TEST_IP=54.116.29.247 # 本机公网 IP(LiveKit WebRTC 必需)
|
||||||
|
|
||||||
|
# ══ MongoDB ════════════════════════════════════════════════════
|
||||||
|
MONGO_HOST=47.237.103.4
|
||||||
|
MONGO_PORT=27017
|
||||||
|
MONGO_USERNAME=minio_pC5wMB
|
||||||
|
MONGO_PASSWORD=rI57PJsJhnz_qlRkfnTa0RPT
|
||||||
|
MONGO_AUTHSOURCE=openim_v3
|
||||||
|
MONGO_DATABASE=openim_v3
|
||||||
|
BUILD_MONGO_DATABASE=build
|
||||||
|
|
||||||
|
# ══ Amazon S3 ══════════════════════════════════════════════════
|
||||||
|
OPENIM_AWS_REGION=ap-southeast-1
|
||||||
|
OPENIM_AWS_BUCKET=im1688
|
||||||
|
OPENIM_AWS_ACCESS_KEY_ID=xxx
|
||||||
|
OPENIM_AWS_SECRET_ACCESS_KEY=xxx
|
||||||
|
|
||||||
|
BUILD_AWS_REGION=ap-east-1
|
||||||
|
BUILD_AWS_BUCKET=im-hk-apk
|
||||||
|
BUILD_AWS_ACCESS_KEY=xxx
|
||||||
|
BUILD_AWS_SECRET_KEY=xxx
|
||||||
|
|
||||||
|
# ══ Docker Redis / Kafka / Etcd ════════════════════════════════
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=openIM123
|
||||||
|
KAFKA_PORT=9092
|
||||||
|
ETCD_PORT=2379
|
||||||
|
|
||||||
|
# ══ LiveKit(本机 Docker,使用公网 IP)════════════════════════
|
||||||
|
LIVEKIT_NODE_IP=54.116.29.247 # 与 DEPLOY_TEST_IP 保持一致
|
||||||
|
LIVEKIT_URL=ws://127.0.0.1:7880
|
||||||
|
LIVEKIT_API_KEY=API8462dba2
|
||||||
|
LIVEKIT_API_SECRET=xxx
|
||||||
|
|
||||||
|
# ══ Cloudflare Stream / 腾讯云 RTC ════════════════════════════
|
||||||
|
CF_ACCOUNT_ID=
|
||||||
|
CF_API_TOKEN=
|
||||||
|
TENCENT_SDK_APP_ID=xxx
|
||||||
|
TENCENT_SDK_SECRET_KEY=xxx
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 服务地址
|
||||||
|
|
||||||
|
### 后端服务
|
||||||
|
|
||||||
|
| 服务 | 端口 |
|
||||||
|
|------|------|
|
||||||
|
| openim-server | :10002 (HTTP) / :10001 (WS) |
|
||||||
|
| chat-api | :10008 |
|
||||||
|
| admin-api | :10009 |
|
||||||
|
| meetingmsg | :8000 (WS) |
|
||||||
|
| livecloud | :8080 |
|
||||||
|
| livestream | :8081 |
|
||||||
|
| build-server | :8281 |
|
||||||
|
|
||||||
|
### 前端开发服务器(可选)
|
||||||
|
|
||||||
|
| 项目 | 端口 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| pc (Electron) | :7777 | Electron 桌面客户端 |
|
||||||
|
| meetingh5 | :5188 | 直播观看 H5(弹幕+视频) |
|
||||||
|
| h5 | :3003 | 移动端 H5 |
|
||||||
|
| cms | :8001 | 后台管理 |
|
||||||
|
| build-cms | :8002 | 构建管理后台 |
|
||||||
|
| build-down | :8003 | 下载页 |
|
||||||
|
|
||||||
|
> **meetingh5 访问方式**
|
||||||
|
>
|
||||||
|
> `02-patch-config.sh` 会自动生成 `meetingh5/.env.local`,设置默认后端地址:
|
||||||
|
>
|
||||||
|
> ```
|
||||||
|
> # 直接访问(使用 .env.local 中的默认后端)
|
||||||
|
> http://<DEPLOY_TEST_IP>:5188
|
||||||
|
>
|
||||||
|
> # 或显式传入 URL 参数(优先级最高)
|
||||||
|
> http://<DEPLOY_TEST_IP>:5188?ws=ws://<DEPLOY_TEST_IP>:8000&liveApi=http://<DEPLOY_TEST_IP>:8081
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> - `ws` → meetingmsg 弹幕 WebSocket `:8000`
|
||||||
|
> - `liveApi` → livestream 直播间 API `:8081`
|
||||||
|
|
||||||
|
### Docker 基础设施
|
||||||
|
|
||||||
|
| 服务 | 端口 |
|
||||||
|
|------|------|
|
||||||
|
| Redis | :6379 |
|
||||||
|
| Kafka | :9092 |
|
||||||
|
| Etcd | :2379 |
|
||||||
|
| LiveKit | :7880 (API) / :7882 (TCP) / :50000-51000/udp (WebRTC) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LiveKit 说明
|
||||||
|
|
||||||
|
本测试服务器运行本地 LiveKit 容器,WebRTC 媒体流通过公网 IP 对外暴露。
|
||||||
|
|
||||||
|
```
|
||||||
|
测试服务器(DEPLOY_TEST_IP: 54.116.29.247)
|
||||||
|
│
|
||||||
|
├── dev-redis :6379 ←── dev-livekit 通过 host.docker.internal 访问
|
||||||
|
└── dev-livekit
|
||||||
|
:7880 → HTTP API(后端连接)
|
||||||
|
:7882/tcp+udp → WebRTC fallback
|
||||||
|
:50000-51000/udp → WebRTC 媒体流(客户端直连公网 IP)
|
||||||
|
```
|
||||||
|
|
||||||
|
**防火墙必须开放**:7880/tcp、7882/tcp+udp、50000-51000/udp
|
||||||
|
|
||||||
|
本机 Mac(deploy-local)的 LiveKit 连接地址为 `ws://54.116.29.247:7880`,与此保持一致。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 日志体系
|
||||||
|
|
||||||
|
```
|
||||||
|
.deploy-test/
|
||||||
|
├── script-logs/ ← 每次脚本执行的完整输出(带时间戳,自动去除颜色码)
|
||||||
|
├── logs/ ← 后端/前端服务进程 stdout+stderr
|
||||||
|
└── docker-logs/ ← Docker 容器日志(每日一文件)
|
||||||
|
├── redis/
|
||||||
|
├── kafka/
|
||||||
|
├── etcd/
|
||||||
|
└── livekit/
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 查看所有日志概览
|
||||||
|
./deploy-test/logs.sh
|
||||||
|
|
||||||
|
# 实时跟踪某个服务
|
||||||
|
./deploy-test/logs.sh openim-server
|
||||||
|
./deploy-test/logs.sh livekit
|
||||||
|
./deploy-test/logs.sh cms
|
||||||
|
|
||||||
|
# 查看脚本执行历史
|
||||||
|
./deploy-test/logs.sh scripts
|
||||||
|
./deploy-test/logs.sh scripts --last # 最新一次完整输出
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 日常操作
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 早上开机
|
||||||
|
./deploy-test/03-start-infra.sh # Docker 容器(含 LiveKit)
|
||||||
|
./deploy-test/05-start.sh # 后端服务
|
||||||
|
|
||||||
|
# 查看状态
|
||||||
|
./deploy-test/status.sh
|
||||||
|
|
||||||
|
# 重启单个后端服务
|
||||||
|
./deploy-test/restart.sh chat-api
|
||||||
|
./deploy-test/restart.sh chat-api --build # 重编译 + 重启
|
||||||
|
|
||||||
|
# 下班关机
|
||||||
|
./deploy-test/stop.sh # 后端进程
|
||||||
|
./deploy-test/stop-infra.sh # Docker 容器(含 LiveKit,数据保留)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 故障排查
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 验证 MongoDB / S3 连接
|
||||||
|
./deploy-test/check-conn.sh
|
||||||
|
|
||||||
|
# 查看 LiveKit 日志(WebRTC 不通时)
|
||||||
|
./deploy-test/logs.sh livekit --last
|
||||||
|
|
||||||
|
# 重置 Docker 环境(删除所有数据)
|
||||||
|
./deploy-test/remove-infra.sh
|
||||||
|
./deploy-test/02-patch-config.sh
|
||||||
|
./deploy-test/03-start-infra.sh
|
||||||
|
```
|
||||||
180
check-conn.sh
Executable file
180
check-conn.sh
Executable file
@@ -0,0 +1,180 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# check-conn.sh — 验证远程服务连接(MongoDB 和 Amazon S3)
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# ./check-conn.sh # 同时检查 MongoDB 和 S3
|
||||||
|
# ./check-conn.sh mongo # 只检查 MongoDB
|
||||||
|
# ./check-conn.sh s3 # 只检查 S3
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
load_env
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
TARGET="${1:-all}"
|
||||||
|
|
||||||
|
header "远程服务连接检查"
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# MongoDB
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
check_mongo() {
|
||||||
|
step "MongoDB: ${MONGO_HOST}:${MONGO_PORT}/${MONGO_DATABASE}"
|
||||||
|
|
||||||
|
echo -e " Host: ${MONGO_HOST}"
|
||||||
|
echo -e " Port: ${MONGO_PORT}"
|
||||||
|
echo -e " Database: ${MONGO_DATABASE}"
|
||||||
|
echo -e " AuthSource: ${MONGO_AUTHSOURCE}"
|
||||||
|
echo -e " Username: ${MONGO_USERNAME}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
MONGO_URI="mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOST}:${MONGO_PORT}/${MONGO_DATABASE}?authSource=${MONGO_AUTHSOURCE}&directConnection=true"
|
||||||
|
|
||||||
|
# 方法1:mongosh
|
||||||
|
if command -v mongosh &>/dev/null; then
|
||||||
|
info "使用 mongosh 验证..."
|
||||||
|
if mongosh "$MONGO_URI" --quiet --eval \
|
||||||
|
'db.runCommand({ping:1}); db.getSiblingDB("'"${MONGO_DATABASE}"'").getCollectionNames().slice(0,5)' \
|
||||||
|
2>/dev/null; then
|
||||||
|
success "MongoDB 连接正常 ✓"
|
||||||
|
else
|
||||||
|
error "MongoDB 连接失败!请检查 .env.local 中的配置"
|
||||||
|
echo ""
|
||||||
|
echo " 排查步骤:"
|
||||||
|
echo " 1. 确认 MongoDB 服务器 ${MONGO_HOST} 可从本机访问"
|
||||||
|
echo " 2. 确认端口 ${MONGO_PORT} 已开放防火墙"
|
||||||
|
echo " 3. 确认用户名/密码/authSource 正确"
|
||||||
|
echo " 4. 手动测试: mongosh \"$MONGO_URI\""
|
||||||
|
fi
|
||||||
|
# 方法2:nc 端口连通
|
||||||
|
elif command -v nc &>/dev/null; then
|
||||||
|
info "mongosh 未安装,使用 nc 检查端口..."
|
||||||
|
if nc -z -w5 "${MONGO_HOST}" "${MONGO_PORT}" 2>/dev/null; then
|
||||||
|
success "MongoDB 端口 ${MONGO_HOST}:${MONGO_PORT} 可达 ✓"
|
||||||
|
warn "(未验证认证,安装 mongosh 可做完整测试)"
|
||||||
|
else
|
||||||
|
error "MongoDB 端口不可达: ${MONGO_HOST}:${MONGO_PORT}"
|
||||||
|
fi
|
||||||
|
# 方法3:Python pymongo
|
||||||
|
elif command -v python3 &>/dev/null && python3 -c "import pymongo" 2>/dev/null; then
|
||||||
|
info "使用 Python pymongo 验证..."
|
||||||
|
python3 - <<PYEOF
|
||||||
|
from pymongo import MongoClient
|
||||||
|
import sys
|
||||||
|
try:
|
||||||
|
c = MongoClient("${MONGO_URI}", serverSelectionTimeoutMS=5000)
|
||||||
|
c.server_info()
|
||||||
|
print(" MongoDB 连接正常 ✓")
|
||||||
|
c.close()
|
||||||
|
except Exception as e:
|
||||||
|
print(f" 连接失败: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
PYEOF
|
||||||
|
[[ $? -eq 0 ]] && success "MongoDB 连接正常" || error "MongoDB 连接失败"
|
||||||
|
else
|
||||||
|
warn "跳过 MongoDB 连接验证(请安装 mongosh: brew install mongosh)"
|
||||||
|
echo -e " 手动验证: mongosh \"${MONGO_URI}\""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Amazon S3
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 通用 S3 检查函数
|
||||||
|
# 用法: _check_s3_bucket <label> <key_id> <secret_key> <region> <bucket> [endpoint]
|
||||||
|
_check_s3_bucket() {
|
||||||
|
local label="$1" key_id="$2" secret_key="$3" region="$4" bucket="$5" endpoint="${6:-}"
|
||||||
|
|
||||||
|
echo -e " Bucket: ${bucket}"
|
||||||
|
echo -e " Region: ${region}"
|
||||||
|
echo -e " AccessKey: ${key_id}"
|
||||||
|
[[ -n "$endpoint" ]] && echo -e " Endpoint: ${endpoint}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [[ "${key_id}" == "YOUR_"* || -z "${key_id}" ]]; then
|
||||||
|
error "S3 AccessKeyID 未配置,请编辑 .env.local"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v aws &>/dev/null; then
|
||||||
|
info "使用 awscli 验证..."
|
||||||
|
local endpoint_arg=""
|
||||||
|
[[ -n "$endpoint" ]] && endpoint_arg="--endpoint-url $endpoint"
|
||||||
|
|
||||||
|
local result rc=0
|
||||||
|
result=$(
|
||||||
|
AWS_ACCESS_KEY_ID="$key_id" \
|
||||||
|
AWS_SECRET_ACCESS_KEY="$secret_key" \
|
||||||
|
AWS_DEFAULT_REGION="$region" \
|
||||||
|
aws s3 ls "s3://${bucket}" $endpoint_arg 2>&1 | head -5
|
||||||
|
) || rc=$?
|
||||||
|
|
||||||
|
if [[ $rc -eq 0 ]]; then
|
||||||
|
success "S3 Bucket 可访问 ✓"
|
||||||
|
[[ -n "$result" ]] && echo "$result" | sed 's/^/ /' || echo " (Bucket 为空)"
|
||||||
|
else
|
||||||
|
error "S3 访问失败!错误: $result"
|
||||||
|
echo " 排查: 确认 AccessKey/SecretKey、Bucket 名称、IAM s3:ListBucket 权限"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 测试写入
|
||||||
|
info "测试写入权限..."
|
||||||
|
local test_key="local-dev-test-$(date +%s)"
|
||||||
|
local write_ok=false
|
||||||
|
AWS_ACCESS_KEY_ID="$key_id" \
|
||||||
|
AWS_SECRET_ACCESS_KEY="$secret_key" \
|
||||||
|
AWS_DEFAULT_REGION="$region" \
|
||||||
|
aws s3 cp /dev/stdin "s3://${bucket}/${test_key}" \
|
||||||
|
$endpoint_arg --content-type text/plain \
|
||||||
|
<<< "local-dev-test" 2>/dev/null && write_ok=true || true
|
||||||
|
|
||||||
|
if $write_ok; then
|
||||||
|
AWS_ACCESS_KEY_ID="$key_id" AWS_SECRET_ACCESS_KEY="$secret_key" \
|
||||||
|
AWS_DEFAULT_REGION="$region" \
|
||||||
|
aws s3 rm "s3://${bucket}/${test_key}" $endpoint_arg 2>/dev/null || true
|
||||||
|
success "S3 写入权限正常 ✓"
|
||||||
|
else
|
||||||
|
warn "S3 写入测试失败(Bucket 可读但可能无写权限)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "awscli 未安装,跳过验证(brew install awscli)"
|
||||||
|
echo " 手动验证: AWS_ACCESS_KEY_ID=${key_id} aws s3 ls s3://${bucket}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_s3() {
|
||||||
|
step "S3 (open-im-server) — IM 文件存储"
|
||||||
|
_check_s3_bucket \
|
||||||
|
"openim" \
|
||||||
|
"${OPENIM_AWS_ACCESS_KEY_ID}" \
|
||||||
|
"${OPENIM_AWS_SECRET_ACCESS_KEY}" \
|
||||||
|
"${OPENIM_AWS_REGION}" \
|
||||||
|
"${OPENIM_AWS_BUCKET}" \
|
||||||
|
"${OPENIM_AWS_ENDPOINT:-}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
step "S3 (build-server) — App APK/IPA 构建产物"
|
||||||
|
_check_s3_bucket \
|
||||||
|
"build" \
|
||||||
|
"${BUILD_AWS_ACCESS_KEY}" \
|
||||||
|
"${BUILD_AWS_SECRET_KEY}" \
|
||||||
|
"${BUILD_AWS_REGION}" \
|
||||||
|
"${BUILD_AWS_BUCKET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 入口
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
case "$TARGET" in
|
||||||
|
all) check_mongo; echo ""; check_s3 ;;
|
||||||
|
mongo) check_mongo ;;
|
||||||
|
s3) check_s3 ;;
|
||||||
|
*)
|
||||||
|
error "未知参数: $TARGET"
|
||||||
|
echo "用法: $0 [all|mongo|s3]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
225
common.sh
Executable file
225
common.sh
Executable file
@@ -0,0 +1,225 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# common.sh — 公共函数库,供各子脚本 source 引入
|
||||||
|
# 不可直接执行
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# 防止重复加载
|
||||||
|
[[ -n "${_COMMON_LOADED:-}" ]] && return 0
|
||||||
|
_COMMON_LOADED=1
|
||||||
|
|
||||||
|
# ── 根目录(workspace46/)──────────────────────────────────────────────────────
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
|
||||||
|
# ── 运行时目录(测试服务器环境) ───────────────────────────────────────────────
|
||||||
|
LOG_DIR="$ROOT_DIR/.deploy-test/logs" # 后端服务日志
|
||||||
|
PID_DIR="$ROOT_DIR/.deploy-test/pids" # PID 文件(含日志收集进程)
|
||||||
|
BUILD_DIR="$ROOT_DIR/.deploy-test/bin" # 编译产物
|
||||||
|
DATA_DIR="$ROOT_DIR/.deploy-test/docker-data" # Docker 数据卷
|
||||||
|
DOCKER_LOG_DIR="$ROOT_DIR/.deploy-test/docker-logs" # Docker 容器日志
|
||||||
|
SCRIPT_LOG_DIR="$ROOT_DIR/.deploy-test/script-logs" # 脚本执行日志
|
||||||
|
ENV_FILE="$ROOT_DIR/.env.deploy-test"
|
||||||
|
|
||||||
|
# ── 颜色 ───────────────────────────────────────────────────────────────────────
|
||||||
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
||||||
|
|
||||||
|
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
|
||||||
|
success() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||||
|
step() { echo -e "\n${BOLD}${BLUE}▶ $*${NC}"; }
|
||||||
|
header() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${BLUE}══════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${BOLD}${BLUE} $*${NC}"
|
||||||
|
echo -e "${BOLD}${BLUE}══════════════════════════════════════════${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 初始化运行时目录 ────────────────────────────────────────────────────────────
|
||||||
|
init_dirs() {
|
||||||
|
mkdir -p "$LOG_DIR" "$PID_DIR" "$BUILD_DIR" "$DATA_DIR" \
|
||||||
|
"$DOCKER_LOG_DIR" "$SCRIPT_LOG_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# 脚本执行日志
|
||||||
|
# 调用位置:每个脚本 init_dirs 之后
|
||||||
|
# 效果:所有输出(stdout+stderr)同时写入 .local-dev/script-logs/<name>-<ts>.log
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
init_script_log() {
|
||||||
|
local script_name
|
||||||
|
script_name="$(basename "${BASH_SOURCE[1]:-$0}" .sh)"
|
||||||
|
local ts; ts="$(date +%Y%m%d-%H%M%S)"
|
||||||
|
export _CURRENT_SCRIPT_LOG="$SCRIPT_LOG_DIR/${script_name}-${ts}.log"
|
||||||
|
mkdir -p "$SCRIPT_LOG_DIR"
|
||||||
|
|
||||||
|
# 写入文件头(纯文本,不含颜色码)
|
||||||
|
{
|
||||||
|
echo "========================================"
|
||||||
|
echo "Script : $script_name"
|
||||||
|
echo "Started: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
echo "========================================"
|
||||||
|
} > "$_CURRENT_SCRIPT_LOG"
|
||||||
|
|
||||||
|
# exec:将所有后续输出同时流向终端和日志文件
|
||||||
|
# 用 sed 去除 ANSI 颜色码,保证日志文件可读
|
||||||
|
exec > >(tee >(sed $'s/\033\\[[0-9;]*m//g' >> "$_CURRENT_SCRIPT_LOG")) 2>&1
|
||||||
|
|
||||||
|
info "脚本日志 → $_CURRENT_SCRIPT_LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Docker 容器日志收集
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# 启动后台日志收集进程:docker logs -f → 本地文件(按日期滚动)
|
||||||
|
start_docker_logger() {
|
||||||
|
local cname="$1"
|
||||||
|
local svc="${cname#dev-}" # dev-redis → redis
|
||||||
|
local log_dir="$DOCKER_LOG_DIR/$svc"
|
||||||
|
local logfile="$log_dir/${svc}-$(date +%Y%m%d).log"
|
||||||
|
local pid_file="$PID_DIR/docker-log-${cname}.pid"
|
||||||
|
|
||||||
|
mkdir -p "$log_dir"
|
||||||
|
|
||||||
|
# 停止已有的收集进程
|
||||||
|
if [[ -f "$pid_file" ]] && kill -0 "$(cat "$pid_file")" 2>/dev/null; then
|
||||||
|
kill "$(cat "$pid_file")" 2>/dev/null || true
|
||||||
|
sleep 0.3
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 写分隔符,区分每次启动会话
|
||||||
|
{
|
||||||
|
echo ""
|
||||||
|
echo "──── 容器启动 $(date '+%Y-%m-%d %H:%M:%S') ────"
|
||||||
|
} >> "$logfile"
|
||||||
|
|
||||||
|
# 后台跟踪容器日志(docker logs 本身已含历史,--tail 0 只取新增)
|
||||||
|
# 首次启动时先 dump 当前快照,再 follow 新增
|
||||||
|
docker logs "$cname" >> "$logfile" 2>&1 || true
|
||||||
|
docker logs -f --tail 0 "$cname" >> "$logfile" 2>&1 &
|
||||||
|
echo $! > "$pid_file"
|
||||||
|
|
||||||
|
info " 容器日志 → $logfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 停止容器日志收集进程
|
||||||
|
stop_docker_logger() {
|
||||||
|
local cname="$1"
|
||||||
|
local pid_file="$PID_DIR/docker-log-${cname}.pid"
|
||||||
|
if [[ -f "$pid_file" ]]; then
|
||||||
|
local pid; pid=$(cat "$pid_file")
|
||||||
|
kill "$pid" 2>/dev/null || true
|
||||||
|
rm -f "$pid_file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 加载 .env.local ─────────────────────────────────────────────────────────────
|
||||||
|
load_env() {
|
||||||
|
if [[ ! -f "$ENV_FILE" ]]; then
|
||||||
|
error ".env.local 不存在,请先执行: ./deploy-test/01-init-env.sh"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
set -a
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source "$ENV_FILE"
|
||||||
|
set +a
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 检查必要工具 ────────────────────────────────────────────────────────────────
|
||||||
|
require_tools() {
|
||||||
|
local missing=()
|
||||||
|
for tool in "$@"; do
|
||||||
|
command -v "$tool" &>/dev/null || missing+=("$tool")
|
||||||
|
done
|
||||||
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||||
|
error "缺少必要工具: ${missing[*]}"
|
||||||
|
for t in "${missing[@]}"; do
|
||||||
|
case "$t" in
|
||||||
|
go) echo " → 安装 Go: https://go.dev/dl/" ;;
|
||||||
|
docker) echo " → 安装 Docker Desktop: https://www.docker.com/products/docker-desktop/" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Docker daemon 检查 ──────────────────────────────────────────────────────────
|
||||||
|
require_docker_running() {
|
||||||
|
require_tools docker
|
||||||
|
if ! docker info &>/dev/null; then
|
||||||
|
error "Docker daemon 未运行,请启动 Docker Desktop"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 启动单个后端服务(nohup 后台) ───────────────────────────────────────────────
|
||||||
|
start_svc() {
|
||||||
|
local name="$1" bin="$2" args="${3:-}" workdir="${4:-$ROOT_DIR}"
|
||||||
|
local pidfile="$PID_DIR/$name.pid" logfile="$LOG_DIR/$name.log"
|
||||||
|
|
||||||
|
if [[ -f "$pidfile" ]] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then
|
||||||
|
warn "$name 已在运行 (PID=$(cat "$pidfile")),跳过"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ ! -f "$bin" ]] && { error "$name 二进制不存在 ($bin),请先执行 04-build.sh"; return 1; }
|
||||||
|
|
||||||
|
info "启动 $name ..."
|
||||||
|
(
|
||||||
|
cd "$workdir"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
nohup "$bin" $args > "$logfile" 2>&1 &
|
||||||
|
echo $! > "$pidfile"
|
||||||
|
)
|
||||||
|
sleep 1
|
||||||
|
if kill -0 "$(cat "$pidfile")" 2>/dev/null; then
|
||||||
|
success " $name 已启动 (PID=$(cat "$pidfile")) → $logfile"
|
||||||
|
else
|
||||||
|
error " $name 启动失败,查看日志:"
|
||||||
|
tail -20 "$logfile" 2>/dev/null || true
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 停止单个后端服务 ────────────────────────────────────────────────────────────
|
||||||
|
stop_svc() {
|
||||||
|
local name="$1" pidfile="$PID_DIR/$name.pid"
|
||||||
|
if [[ -f "$pidfile" ]]; then
|
||||||
|
local pid; pid=$(cat "$pidfile")
|
||||||
|
if kill -0 "$pid" 2>/dev/null; then
|
||||||
|
kill "$pid" && success "$name 已停止 (PID=$pid)"
|
||||||
|
else
|
||||||
|
warn "$name 进程 $pid 不存在(可能已退出)"
|
||||||
|
fi
|
||||||
|
rm -f "$pidfile"
|
||||||
|
else
|
||||||
|
warn "$name 没有 PID 记录(未运行)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Docker 容器状态打印 ─────────────────────────────────────────────────────────
|
||||||
|
print_container_status() {
|
||||||
|
local label="$1" cname="$2" port="$3"
|
||||||
|
if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^${cname}$"; then
|
||||||
|
printf " ${GREEN}●${NC} %-12s container=%-14s :%-5s\n" "$label" "$cname" "$port"
|
||||||
|
elif docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q "^${cname}$"; then
|
||||||
|
printf " ${YELLOW}○${NC} %-12s stopped=%-14s :%-5s\n" "$label" "$cname" "$port"
|
||||||
|
else
|
||||||
|
printf " ${RED}✗${NC} %-12s (未创建) :%-5s\n" "$label" "$port"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 后端服务状态打印 ────────────────────────────────────────────────────────────
|
||||||
|
print_svc_status() {
|
||||||
|
local name="$1" desc="$2" pidfile="$PID_DIR/$name.pid"
|
||||||
|
if [[ -f "$pidfile" ]] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then
|
||||||
|
printf " ${GREEN}●${NC} %-18s PID=%-7s %s\n" "$name" "$(cat "$pidfile")" "$desc"
|
||||||
|
else
|
||||||
|
printf " ${RED}○${NC} %-18s %-11s %s\n" "$name" "未运行" "$desc"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 所有后端服务名列表 ──────────────────────────────────────────────────────────
|
||||||
|
ALL_SVCS=(openim-server chat-rpc admin-rpc chat-api admin-api meetingmsg livecloud livestream build-server)
|
||||||
205
logs.sh
Executable file
205
logs.sh
Executable file
@@ -0,0 +1,205 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# logs.sh — 查看服务日志
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# ./logs.sh <service> # tail -f 实时跟踪日志
|
||||||
|
# ./logs.sh <service> --last # 只显示最后 100 行(不跟踪)
|
||||||
|
# ./logs.sh <service> -n 50 # 显示最后 50 行并跟踪
|
||||||
|
# ./logs.sh scripts # 列出所有脚本执行日志
|
||||||
|
# ./logs.sh scripts --last # 查看最新一次脚本日志
|
||||||
|
#
|
||||||
|
# 日志目录一览:
|
||||||
|
# .local-dev/logs/ — 后端服务运行日志
|
||||||
|
# .local-dev/docker-logs/ — Docker 容器日志(每日一文件)
|
||||||
|
# .local-dev/script-logs/ — 脚本执行日志(带时间戳)
|
||||||
|
#
|
||||||
|
# 后端服务: openim-server, chat-rpc, admin-rpc, chat-api, admin-api,
|
||||||
|
# meetingmsg, livecloud, livestream, build-server
|
||||||
|
# 前端服务: pc, meetingh5, h5, cms, build-cms, build-down
|
||||||
|
# Docker: redis, kafka, etcd, livekit
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
# 注意:logs.sh 不调用 init_script_log,避免 tail -f 被重定向到日志文件
|
||||||
|
|
||||||
|
SVC="${1:-}"
|
||||||
|
OPT="${2:-}"
|
||||||
|
NLINES="${3:-100}"
|
||||||
|
|
||||||
|
# ── 无参数:打印概览 ──────────────────────────────────────────────────────────
|
||||||
|
if [[ -z "$SVC" ]]; then
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}用法:${NC} $0 <service|scripts> [--last|-n <lines>]"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${BOLD}后端服务日志${NC} ($LOG_DIR/):"
|
||||||
|
for svc in "${ALL_SVCS[@]}"; do
|
||||||
|
local_log="$LOG_DIR/$svc.log"
|
||||||
|
if [[ -f "$local_log" ]]; then
|
||||||
|
size=$(du -sh "$local_log" 2>/dev/null | awk '{print $1}')
|
||||||
|
printf " %-18s %s (%s)\n" "$svc" "$local_log" "$size"
|
||||||
|
else
|
||||||
|
printf " %-18s (无日志)\n" "$svc"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Docker 容器日志${NC} ($DOCKER_LOG_DIR/):"
|
||||||
|
for svc in redis kafka etcd livekit; do
|
||||||
|
log_dir="$DOCKER_LOG_DIR/$svc"
|
||||||
|
if [[ -d "$log_dir" ]]; then
|
||||||
|
latest=$(ls -t "$log_dir"/*.log 2>/dev/null | head -1 || echo "")
|
||||||
|
if [[ -n "$latest" ]]; then
|
||||||
|
size=$(du -sh "$latest" 2>/dev/null | awk '{print $1}')
|
||||||
|
printf " %-10s %s (%s)\n" "$svc" "$latest" "$size"
|
||||||
|
else
|
||||||
|
printf " %-10s (目录存在,暂无日志文件)\n" "$svc"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf " %-10s (未启动)\n" "$svc"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}前端服务日志${NC} ($LOG_DIR/fe-*.log):"
|
||||||
|
FE_LIST=(pc meetingh5 h5 cms build-cms build-down)
|
||||||
|
for fe in "${FE_LIST[@]}"; do
|
||||||
|
fe_log="$LOG_DIR/fe-${fe}.log"
|
||||||
|
if [[ -f "$fe_log" ]]; then
|
||||||
|
size=$(du -sh "$fe_log" 2>/dev/null | awk '{print $1}')
|
||||||
|
printf " %-14s %s (%s)\n" "$fe" "$fe_log" "$size"
|
||||||
|
else
|
||||||
|
printf " %-14s (无日志)\n" "$fe"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}脚本执行日志${NC} ($SCRIPT_LOG_DIR/):"
|
||||||
|
if [[ -d "$SCRIPT_LOG_DIR" ]]; then
|
||||||
|
ls -t "$SCRIPT_LOG_DIR"/*.log 2>/dev/null | head -5 | while read -r f; do
|
||||||
|
size=$(du -sh "$f" 2>/dev/null | awk '{print $1}')
|
||||||
|
printf " %s (%s)\n" "$(basename "$f")" "$size"
|
||||||
|
done
|
||||||
|
count=$(ls "$SCRIPT_LOG_DIR"/*.log 2>/dev/null | wc -l | tr -d ' ')
|
||||||
|
[[ "$count" -gt 5 ]] && echo " ... 共 $count 个文件"
|
||||||
|
else
|
||||||
|
echo " (尚无脚本日志)"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 脚本执行日志 ──────────────────────────────────────────────────────────────
|
||||||
|
if [[ "$SVC" == "scripts" ]]; then
|
||||||
|
if [[ ! -d "$SCRIPT_LOG_DIR" ]] || [[ -z "$(ls "$SCRIPT_LOG_DIR"/*.log 2>/dev/null)" ]]; then
|
||||||
|
warn "暂无脚本执行日志 ($SCRIPT_LOG_DIR/)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$OPT" == "--last" ]]; then
|
||||||
|
latest=$(ls -t "$SCRIPT_LOG_DIR"/*.log | head -1)
|
||||||
|
info "最新脚本日志: $latest"
|
||||||
|
echo "──────────────────────────────────────"
|
||||||
|
cat "$latest"
|
||||||
|
else
|
||||||
|
info "所有脚本执行日志 ($SCRIPT_LOG_DIR/):"
|
||||||
|
ls -lht "$SCRIPT_LOG_DIR"/*.log 2>/dev/null | awk '{printf " %-8s %s %s\n", $5, $6" "$7" "$8, $9}'
|
||||||
|
echo ""
|
||||||
|
echo "查看最新: $0 scripts --last"
|
||||||
|
echo "查看指定: cat $SCRIPT_LOG_DIR/<filename>"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Docker 容器日志 ───────────────────────────────────────────────────────────
|
||||||
|
_docker_log() {
|
||||||
|
local cname="$1"
|
||||||
|
local log_dir="$DOCKER_LOG_DIR/${cname#dev-}"
|
||||||
|
local latest_file
|
||||||
|
|
||||||
|
info "$cname 容器日志"
|
||||||
|
|
||||||
|
# 展示日志文件路径
|
||||||
|
if [[ -d "$log_dir" ]]; then
|
||||||
|
latest_file=$(ls -t "$log_dir"/*.log 2>/dev/null | head -1 || echo "")
|
||||||
|
if [[ -n "$latest_file" ]]; then
|
||||||
|
info "本地日志文件: $latest_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "──────────────────────────────────────"
|
||||||
|
|
||||||
|
if [[ "$OPT" == "--last" ]]; then
|
||||||
|
docker logs --tail "$NLINES" "$cname" 2>&1
|
||||||
|
else
|
||||||
|
docker logs -f --tail "${NLINES}" "$cname" 2>&1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$SVC" in
|
||||||
|
redis) _docker_log "dev-redis"; exit 0 ;;
|
||||||
|
kafka) _docker_log "dev-kafka"; exit 0 ;;
|
||||||
|
etcd) _docker_log "dev-etcd"; exit 0 ;;
|
||||||
|
livekit) _docker_log "dev-livekit"; exit 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# ── 前端服务日志 ──────────────────────────────────────────────────────────────
|
||||||
|
FE_LIST=(pc meetingh5 h5 cms build-cms build-down)
|
||||||
|
for _fe in "${FE_LIST[@]}"; do
|
||||||
|
if [[ "$SVC" == "$_fe" ]]; then
|
||||||
|
LOGFILE="$LOG_DIR/fe-${SVC}.log"
|
||||||
|
pidfile="$PID_DIR/fe-${SVC}.pid"
|
||||||
|
if [[ ! -f "$LOGFILE" ]]; then
|
||||||
|
error "日志文件不存在: $LOGFILE"
|
||||||
|
echo " $SVC 可能尚未启动。启动命令: ./deploy-test/07-start-frontend.sh $SVC"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -f "$pidfile" ]] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then
|
||||||
|
info "$SVC 正在运行 (PID=$(cat "$pidfile"))"
|
||||||
|
else
|
||||||
|
warn "$SVC 当前未运行(显示历史日志)"
|
||||||
|
fi
|
||||||
|
info "日志文件: $LOGFILE"
|
||||||
|
size=$(du -sh "$LOGFILE" 2>/dev/null | awk '{print $1}')
|
||||||
|
info "文件大小: $size"
|
||||||
|
echo "──────────────────────────────────────"
|
||||||
|
if [[ "$OPT" == "--last" ]]; then
|
||||||
|
tail -n "${NLINES}" "$LOGFILE"
|
||||||
|
elif [[ "$OPT" == "-n" ]]; then
|
||||||
|
tail -f -n "${NLINES}" "$LOGFILE"
|
||||||
|
else
|
||||||
|
tail -f -n 100 "$LOGFILE"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── 后端服务日志 ──────────────────────────────────────────────────────────────
|
||||||
|
LOGFILE="$LOG_DIR/$SVC.log"
|
||||||
|
|
||||||
|
if [[ ! -f "$LOGFILE" ]]; then
|
||||||
|
error "日志文件不存在: $LOGFILE"
|
||||||
|
echo " 服务 $SVC 可能尚未启动。启动命令: ./deploy-test/05-start.sh $SVC"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PIDFILE="$PID_DIR/$SVC.pid"
|
||||||
|
if [[ -f "$PIDFILE" ]] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
|
||||||
|
info "$SVC 正在运行 (PID=$(cat "$PIDFILE"))"
|
||||||
|
else
|
||||||
|
warn "$SVC 当前未运行(显示历史日志)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "日志文件: $LOGFILE"
|
||||||
|
size=$(du -sh "$LOGFILE" 2>/dev/null | awk '{print $1}')
|
||||||
|
info "文件大小: $size"
|
||||||
|
echo "──────────────────────────────────────"
|
||||||
|
|
||||||
|
if [[ "$OPT" == "--last" ]]; then
|
||||||
|
tail -n "${NLINES}" "$LOGFILE"
|
||||||
|
elif [[ "$OPT" == "-n" ]]; then
|
||||||
|
tail -f -n "${NLINES}" "$LOGFILE"
|
||||||
|
else
|
||||||
|
tail -f -n 100 "$LOGFILE"
|
||||||
|
fi
|
||||||
42
remove-infra.sh
Executable file
42
remove-infra.sh
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# remove-infra.sh — 删除 Docker 容器及本地持久化数据
|
||||||
|
#
|
||||||
|
# ⚠️ 危险操作:本地 Redis / Kafka / Etcd 数据将全部清除
|
||||||
|
# 适用场景:环境损坏需重置,或希望全新干净启动
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
require_docker_running
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
step "删除 Docker 基础设施容器及数据"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
warn "⚠️ 此操作将删除以下内容:"
|
||||||
|
echo " 容器: dev-livekit, dev-redis, dev-kafka, dev-etcd"
|
||||||
|
echo " 数据: $DATA_DIR/"
|
||||||
|
echo ""
|
||||||
|
read -p "确认删除?(输入 yes 继续): " -r CONFIRM
|
||||||
|
if [[ "$CONFIRM" != "yes" ]]; then
|
||||||
|
info "已取消"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
for cname in dev-livekit dev-redis dev-kafka dev-etcd; do
|
||||||
|
if docker ps -a --format '{{.Names}}' | grep -q "^${cname}$"; then
|
||||||
|
docker rm -f "$cname" > /dev/null && success "已删除容器: $cname"
|
||||||
|
else
|
||||||
|
info "容器不存在,跳过: $cname"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -d "$DATA_DIR" ]]; then
|
||||||
|
rm -rf "$DATA_DIR"
|
||||||
|
success "已删除数据目录: $DATA_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
success "清理完成"
|
||||||
|
echo -e " 重新初始化: ${CYAN}./deploy-test/03-start-infra.sh${NC}"
|
||||||
125
restart.sh
Executable file
125
restart.sh
Executable file
@@ -0,0 +1,125 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# restart.sh — 重启指定服务
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# ./restart.sh <service> # 重启后端服务(使用已有二进制)
|
||||||
|
# ./restart.sh <service> --build # 先重新编译再重启
|
||||||
|
# ./restart.sh <docker-service> # 重启 Docker 容器(redis/kafka/etcd)
|
||||||
|
#
|
||||||
|
# 示例:
|
||||||
|
# ./restart.sh chat-api
|
||||||
|
# ./restart.sh chat-api --build
|
||||||
|
# ./restart.sh redis
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
load_env
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
SVC="${1:-}"
|
||||||
|
OPT="${2:-}"
|
||||||
|
|
||||||
|
[[ -z "$SVC" ]] && { error "用法: $0 <service> [--build]"; exit 1; }
|
||||||
|
|
||||||
|
# ── 服务配置(与 05-start.sh 保持一致)───────────────────────────────────────
|
||||||
|
declare -A svc_workdir=(
|
||||||
|
[openim-server]="$ROOT_DIR/open-im-server"
|
||||||
|
[chat-rpc]="$ROOT_DIR/chat"
|
||||||
|
[admin-rpc]="$ROOT_DIR/chat"
|
||||||
|
[chat-api]="$ROOT_DIR/chat"
|
||||||
|
[admin-api]="$ROOT_DIR/chat"
|
||||||
|
[meetingmsg]="$ROOT_DIR/meetingmsg"
|
||||||
|
[livecloud]="$ROOT_DIR/livecloud"
|
||||||
|
[livestream]="$ROOT_DIR/livestream"
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A svc_args=(
|
||||||
|
[openim-server]="-c $ROOT_DIR/open-im-server/config"
|
||||||
|
[chat-rpc]="-c $ROOT_DIR/chat/config"
|
||||||
|
[admin-rpc]="-c $ROOT_DIR/chat/config"
|
||||||
|
[chat-api]="-c $ROOT_DIR/chat/config"
|
||||||
|
[admin-api]="-c $ROOT_DIR/chat/config"
|
||||||
|
[meetingmsg]=""
|
||||||
|
[livecloud]=""
|
||||||
|
[livestream]=""
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A svc_src_dir=(
|
||||||
|
[openim-server]="open-im-server"
|
||||||
|
[chat-rpc]="chat"
|
||||||
|
[admin-rpc]="chat"
|
||||||
|
[chat-api]="chat"
|
||||||
|
[admin-api]="chat"
|
||||||
|
[meetingmsg]="meetingmsg"
|
||||||
|
[livecloud]="livecloud"
|
||||||
|
[livestream]="livestream"
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A svc_src_pkg=(
|
||||||
|
[openim-server]="./cmd/main.go"
|
||||||
|
[chat-rpc]="./cmd/rpc/chat-rpc/"
|
||||||
|
[admin-rpc]="./cmd/rpc/admin-rpc/"
|
||||||
|
[chat-api]="./cmd/api/chat-api/"
|
||||||
|
[admin-api]="./cmd/api/admin-api/"
|
||||||
|
[meetingmsg]="."
|
||||||
|
[livecloud]="."
|
||||||
|
[livestream]="."
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── Docker 容器重启 ───────────────────────────────────────────────────────────
|
||||||
|
_restart_docker() {
|
||||||
|
local label="$1" cname="$2"
|
||||||
|
require_docker_running
|
||||||
|
if docker ps -a --format '{{.Names}}' | grep -q "^${cname}$"; then
|
||||||
|
info "重启容器 $cname ..."
|
||||||
|
docker restart "$cname" > /dev/null
|
||||||
|
sleep 2
|
||||||
|
if docker ps --format '{{.Names}}' | grep -q "^${cname}$"; then
|
||||||
|
success "$label 容器已重启 (container=$cname)"
|
||||||
|
else
|
||||||
|
error "$label 重启失败"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "容器 $cname 不存在,请先执行 03-start-infra.sh"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 处理 Docker 容器 ──────────────────────────────────────────────────────────
|
||||||
|
case "$SVC" in
|
||||||
|
redis) _restart_docker "Redis" "dev-redis"; exit 0 ;;
|
||||||
|
kafka) _restart_docker "Kafka" "dev-kafka"; exit 0 ;;
|
||||||
|
etcd) _restart_docker "Etcd" "dev-etcd"; exit 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# ── 处理后端服务 ──────────────────────────────────────────────────────────────
|
||||||
|
if [[ -z "${svc_workdir[$SVC]:-}" ]]; then
|
||||||
|
error "未知服务: $SVC"
|
||||||
|
echo "后端服务: ${!svc_workdir[*]}"
|
||||||
|
echo "Docker: redis, kafka, etcd"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
step "重启服务: $SVC"
|
||||||
|
|
||||||
|
# 是否先重新编译
|
||||||
|
if [[ "$OPT" == "--build" ]]; then
|
||||||
|
info "重新编译 $SVC ..."
|
||||||
|
dir="$ROOT_DIR/${svc_src_dir[$SVC]}"
|
||||||
|
pkg="${svc_src_pkg[$SVC]}"
|
||||||
|
(cd "$dir" && go build -o "$BUILD_DIR/$SVC" "$pkg") && \
|
||||||
|
success "编译完成" || { error "编译失败"; exit 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 停止旧进程
|
||||||
|
stop_svc "$SVC"
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# 启动新进程
|
||||||
|
start_svc "$SVC" \
|
||||||
|
"$BUILD_DIR/$SVC" \
|
||||||
|
"${svc_args[$SVC]}" \
|
||||||
|
"${svc_workdir[$SVC]}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "查看日志: ${CYAN}./deploy-test/logs.sh $SVC${NC}"
|
||||||
87
setup.sh
Executable file
87
setup.sh
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# setup.sh — 一键完整部署(首次使用)
|
||||||
|
#
|
||||||
|
# 按顺序执行:
|
||||||
|
# 01-init-env.sh → 生成 .env.local
|
||||||
|
# 02-patch-config.sh → 写入服务 YAML 配置
|
||||||
|
# 03-start-infra.sh → 启动 Docker 容器
|
||||||
|
# 04-build.sh → 编译所有 Go 服务
|
||||||
|
# 05-start.sh → 启动所有后端服务
|
||||||
|
#
|
||||||
|
# 各步骤均可独立重新执行,不必从头来过。
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/common.sh"
|
||||||
|
init_dirs
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
header "一键完整部署(首次使用)"
|
||||||
|
|
||||||
|
echo -e "${BOLD}基础设施策略:${NC}"
|
||||||
|
echo " Redis / Kafka / Etcd → Docker 容器(本地)"
|
||||||
|
echo " MongoDB → 远程服务"
|
||||||
|
echo " 文件存储 → Amazon S3"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ── 步骤 1:初始化 .env.local ─────────────────────────────────────────────────
|
||||||
|
step "[1/5] 初始化 .env.local"
|
||||||
|
bash "$SCRIPT_DIR/01-init-env.sh"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
warn "请确认 .env.local 中的 MongoDB 和 AWS S3 配置已正确填写!"
|
||||||
|
echo -e " ${CYAN}vim $ENV_FILE${NC}"
|
||||||
|
echo ""
|
||||||
|
read -p "配置已填写好,继续执行?(y/N): " -n 1 -r REPLY; echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
info "已暂停,编辑完成后重新执行: ./deploy-test/setup.sh"
|
||||||
|
info "或跳过此步直接从步骤 2 开始: ./deploy-test/02-patch-config.sh"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 步骤 2:写入服务配置 ──────────────────────────────────────────────────────
|
||||||
|
step "[2/5] 修改服务配置文件"
|
||||||
|
bash "$SCRIPT_DIR/02-patch-config.sh"
|
||||||
|
|
||||||
|
# ── 步骤 3:启动 Docker 基础设施 ─────────────────────────────────────────────
|
||||||
|
step "[3/5] 启动 Docker 基础设施"
|
||||||
|
bash "$SCRIPT_DIR/03-start-infra.sh"
|
||||||
|
|
||||||
|
info "等待基础设施就绪 (5s)..."
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# ── 步骤 4:编译 ──────────────────────────────────────────────────────────────
|
||||||
|
step "[4/5] 编译所有后端服务"
|
||||||
|
bash "$SCRIPT_DIR/04-build.sh"
|
||||||
|
|
||||||
|
# ── 步骤 5:启动 ──────────────────────────────────────────────────────────────
|
||||||
|
step "[5/5] 启动所有后端服务"
|
||||||
|
bash "$SCRIPT_DIR/05-start.sh"
|
||||||
|
|
||||||
|
# ── 完成汇总 ─────────────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${GREEN}╔══════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${BOLD}${GREEN}║ 本地环境部署完成! ║${NC}"
|
||||||
|
echo -e "${BOLD}${GREEN}╚══════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}服务地址:${NC}"
|
||||||
|
echo " IM API: http://localhost:10002"
|
||||||
|
echo " IM WebSocket: ws://localhost:10001"
|
||||||
|
echo " Chat API: http://localhost:10008"
|
||||||
|
echo " Admin API: http://localhost:10009"
|
||||||
|
echo " MeetingMsg WS: ws://localhost:8000"
|
||||||
|
echo " Livecloud: http://localhost:8080"
|
||||||
|
echo " Livestream: http://localhost:8081"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}日常命令:${NC}"
|
||||||
|
echo " ./deploy-test/status.sh # 查看全部状态"
|
||||||
|
echo " ./deploy-test/logs.sh <service> # 实时日志"
|
||||||
|
echo " ./deploy-test/restart.sh <service> # 重启服务"
|
||||||
|
echo " ./deploy-test/restart.sh <svc> --build # 重编译并重启"
|
||||||
|
echo " ./deploy-test/check-conn.sh # 验证 MongoDB/S3"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}停止服务:${NC}"
|
||||||
|
echo " ./deploy-test/stop.sh # 停止后端进程"
|
||||||
|
echo " ./deploy-test/stop-infra.sh # 停止 Docker 容器"
|
||||||
|
echo ""
|
||||||
90
status.sh
Executable file
90
status.sh
Executable file
@@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# status.sh — 查看所有服务运行状态
|
||||||
|
#
|
||||||
|
# 显示内容:
|
||||||
|
# - Docker 容器状态(Redis/Kafka/Etcd)
|
||||||
|
# - 远程服务配置摘要(MongoDB/S3)
|
||||||
|
# - 后端服务进程状态(PID + 监听端口)
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
load_env 2>/dev/null || true
|
||||||
|
|
||||||
|
header "服务运行状态"
|
||||||
|
|
||||||
|
# ── Docker 基础设施 ───────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}[ Docker 基础设施 ]${NC}"
|
||||||
|
print_container_status "Redis" "dev-redis" "${REDIS_PORT:-6379}"
|
||||||
|
print_container_status "Kafka" "dev-kafka" "${KAFKA_PORT:-9092}"
|
||||||
|
print_container_status "Etcd" "dev-etcd" "${ETCD_PORT:-2379}"
|
||||||
|
print_container_status "LiveKit" "dev-livekit" "7880"
|
||||||
|
printf " ${CYAN}◉${NC} %-10s 公网 %s:50000-51000/udp (WebRTC)\n" "" "${LIVEKIT_NODE_IP:-?}"
|
||||||
|
|
||||||
|
# ── 远程服务 ─────────────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}[ 远程服务(连接配置)]${NC}"
|
||||||
|
printf " ${CYAN}◉${NC} %-10s %s\n" "MongoDB" \
|
||||||
|
"${MONGO_HOST:-?}:${MONGO_PORT:-27017}/${MONGO_DATABASE:-?} (authSource=${MONGO_AUTHSOURCE:-?})"
|
||||||
|
printf " ${CYAN}◉${NC} %-10s %s\n" "S3" \
|
||||||
|
"s3://${AWS_BUCKET:-?} region=${AWS_REGION:-?}"
|
||||||
|
|
||||||
|
# ── 后端服务 ─────────────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}[ 后端服务 ]${NC}"
|
||||||
|
print_svc_status "openim-server" ":10002 (API) :10001 (MsgGateway WS)"
|
||||||
|
print_svc_status "chat-rpc" "内部 RPC → Etcd"
|
||||||
|
print_svc_status "admin-rpc" "内部 RPC → Etcd"
|
||||||
|
print_svc_status "chat-api" ":10008"
|
||||||
|
print_svc_status "admin-api" ":10009"
|
||||||
|
print_svc_status "meetingmsg" ":8000 (WS)"
|
||||||
|
print_svc_status "livecloud" ":8080"
|
||||||
|
print_svc_status "livestream" ":8081"
|
||||||
|
print_svc_status "build-server" ":8281"
|
||||||
|
|
||||||
|
# ── 前端服务 ─────────────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}[ 前端开发服务器 ]${NC}"
|
||||||
|
declare -A FE_PORT_MAP=(
|
||||||
|
[pc]="7777 (Electron Vite)"
|
||||||
|
[meetingh5]="5188"
|
||||||
|
[h5]="3003"
|
||||||
|
[cms]="8001"
|
||||||
|
[build-cms]="8002"
|
||||||
|
[build-down]="8003"
|
||||||
|
)
|
||||||
|
for fe in pc meetingh5 h5 cms build-cms build-down; do
|
||||||
|
pidfile="$PID_DIR/fe-${fe}.pid"
|
||||||
|
logfile="$LOG_DIR/fe-${fe}.log"
|
||||||
|
if [[ -f "$pidfile" ]] && kill -0 "$(cat "$pidfile")" 2>/dev/null; then
|
||||||
|
printf " ${GREEN}●${NC} %-14s PID=%-7s :%s\n" "$fe" "$(cat "$pidfile")" "${FE_PORT_MAP[$fe]}"
|
||||||
|
else
|
||||||
|
printf " ${RED}○${NC} %-14s 未运行 :%s\n" "$fe" "${FE_PORT_MAP[$fe]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── 端口占用检查 ──────────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}[ 端口占用 ]${NC}"
|
||||||
|
PORTS=(10002 10001 10008 10009 8000 8080 8081 8281 7777 5188 3003 8001 8002 8003)
|
||||||
|
for port in "${PORTS[@]}"; do
|
||||||
|
pid=$(lsof -ti :"$port" 2>/dev/null | head -1 || true)
|
||||||
|
if [[ -n "$pid" ]]; then
|
||||||
|
cmd=$(ps -p "$pid" -o comm= 2>/dev/null || echo "?")
|
||||||
|
printf " ${GREEN}●${NC} :%d PID=%-6s (%s)\n" "$port" "$pid" "$cmd"
|
||||||
|
else
|
||||||
|
printf " ${YELLOW}○${NC} :%d (未监听)\n" "$port"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── 快速操作提示 ──────────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}[ 快捷命令 ]${NC}"
|
||||||
|
echo " ./deploy-test/logs.sh <service> 查看日志(支持前端: pc/h5/cms 等)"
|
||||||
|
echo " ./deploy-test/restart.sh <service> 重启后端服务"
|
||||||
|
echo " ./deploy-test/restart.sh <svc> --build 重编译并重启"
|
||||||
|
echo " ./deploy-test/07-start-frontend.sh 启动前端服务"
|
||||||
|
echo " ./deploy-test/stop-frontend.sh 停止前端服务"
|
||||||
|
echo " ./deploy-test/check-conn.sh 验证 MongoDB/S3 连接"
|
||||||
|
echo ""
|
||||||
54
stop-frontend.sh
Executable file
54
stop-frontend.sh
Executable file
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# stop-frontend.sh — 停止前端开发服务器
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# ./stop-frontend.sh # 停止全部前端服务
|
||||||
|
# ./stop-frontend.sh <project> # 只停止指定项目
|
||||||
|
#
|
||||||
|
# 可用项目: pc, meetingh5, h5, cms, build-cms, build-down
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
init_dirs
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
FE_PROJECTS=(pc meetingh5 h5 cms build-cms build-down)
|
||||||
|
TARGET="${1:-all}"
|
||||||
|
|
||||||
|
_stop_fe() {
|
||||||
|
local name="$1" pidfile="$PID_DIR/fe-${name}.pid"
|
||||||
|
if [[ -f "$pidfile" ]]; then
|
||||||
|
local pid; pid=$(cat "$pidfile")
|
||||||
|
if kill -0 "$pid" 2>/dev/null; then
|
||||||
|
# 杀掉整个进程组(覆盖 npm/pnpm/yarn 子进程)
|
||||||
|
kill -- -"$(ps -o pgid= -p "$pid" 2>/dev/null | tr -d ' ')" 2>/dev/null || kill "$pid" 2>/dev/null || true
|
||||||
|
success "$name 已停止 (PID=$pid)"
|
||||||
|
else
|
||||||
|
warn "$name 进程不存在(可能已退出)"
|
||||||
|
fi
|
||||||
|
rm -f "$pidfile"
|
||||||
|
else
|
||||||
|
warn "$name 没有 PID 记录(未运行)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$TARGET" == "all" ]]; then
|
||||||
|
step "停止全部前端开发服务器"
|
||||||
|
for proj in "${FE_PROJECTS[@]}"; do
|
||||||
|
_stop_fe "$proj"
|
||||||
|
done
|
||||||
|
success "所有前端服务已停止"
|
||||||
|
else
|
||||||
|
local_valid=false
|
||||||
|
for p in "${FE_PROJECTS[@]}"; do
|
||||||
|
[[ "$p" == "$TARGET" ]] && local_valid=true && break
|
||||||
|
done
|
||||||
|
if ! $local_valid; then
|
||||||
|
error "未知项目: $TARGET"
|
||||||
|
echo "可用: ${FE_PROJECTS[*]}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
step "停止: $TARGET"
|
||||||
|
_stop_fe "$TARGET"
|
||||||
|
fi
|
||||||
51
stop-infra.sh
Executable file
51
stop-infra.sh
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# stop-infra.sh — 停止 Docker 基础设施容器
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# ./stop-infra.sh # 停止 Redis / Kafka / Etcd
|
||||||
|
# ./stop-infra.sh redis # 只停止 Redis
|
||||||
|
# ./stop-infra.sh kafka # 只停止 Kafka
|
||||||
|
# ./stop-infra.sh etcd # 只停止 Etcd
|
||||||
|
#
|
||||||
|
# 注意:仅停止容器,不删除数据。重新启动执行: 03-start-infra.sh
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
require_docker_running
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
TARGET="${1:-all}"
|
||||||
|
|
||||||
|
_stop_container() {
|
||||||
|
local label="$1" cname="$2"
|
||||||
|
# 先停日志收集进程
|
||||||
|
stop_docker_logger "$cname"
|
||||||
|
if docker ps --format '{{.Names}}' | grep -q "^${cname}$"; then
|
||||||
|
docker stop "$cname" > /dev/null && success "$label 容器已停止 (container=$cname)"
|
||||||
|
else
|
||||||
|
warn "$label 容器未在运行 (container=$cname)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$TARGET" in
|
||||||
|
all)
|
||||||
|
step "停止所有 Docker 基础设施"
|
||||||
|
_stop_container "LiveKit" "dev-livekit"
|
||||||
|
_stop_container "Kafka" "dev-kafka"
|
||||||
|
_stop_container "Redis" "dev-redis"
|
||||||
|
_stop_container "Etcd" "dev-etcd"
|
||||||
|
echo ""
|
||||||
|
success "所有 Docker 容器已停止(数据已保留)"
|
||||||
|
echo -e " 重新启动: ${CYAN}./deploy-test/03-start-infra.sh${NC}"
|
||||||
|
;;
|
||||||
|
redis) _stop_container "Redis" "dev-redis" ;;
|
||||||
|
kafka) _stop_container "Kafka" "dev-kafka" ;;
|
||||||
|
etcd) _stop_container "Etcd" "dev-etcd" ;;
|
||||||
|
livekit) _stop_container "LiveKit" "dev-livekit" ;;
|
||||||
|
*)
|
||||||
|
error "未知组件: $TARGET"
|
||||||
|
echo "可用: redis, kafka, etcd, livekit, all"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
42
stop.sh
Executable file
42
stop.sh
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================================
|
||||||
|
# stop.sh — 停止后端 Go 服务
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# ./stop.sh # 停止全部后端服务
|
||||||
|
# ./stop.sh <svc> # 只停止指定服务
|
||||||
|
#
|
||||||
|
# 注意:此命令只停止后端进程,不影响 Docker 容器(Redis/Kafka/Etcd)
|
||||||
|
# 如需停止 Docker 容器,执行: ./stop-infra.sh
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
||||||
|
init_dirs
|
||||||
|
init_script_log # ← 脚本执行日志
|
||||||
|
|
||||||
|
TARGET="${1:-all}"
|
||||||
|
|
||||||
|
if [[ "$TARGET" == "all" ]]; then
|
||||||
|
step "停止所有后端服务"
|
||||||
|
for svc in "${ALL_SVCS[@]}"; do
|
||||||
|
stop_svc "$svc"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
success "所有后端服务已停止"
|
||||||
|
echo ""
|
||||||
|
echo -e "如需停止 Docker 基础设施:"
|
||||||
|
echo -e " ${CYAN}./deploy-test/stop-infra.sh${NC}"
|
||||||
|
else
|
||||||
|
step "停止服务: $TARGET"
|
||||||
|
# 验证服务名合法
|
||||||
|
local_valid=false
|
||||||
|
for svc in "${ALL_SVCS[@]}"; do
|
||||||
|
[[ "$svc" == "$TARGET" ]] && local_valid=true && break
|
||||||
|
done
|
||||||
|
if ! $local_valid; then
|
||||||
|
error "未知服务: $TARGET"
|
||||||
|
echo "可用: ${ALL_SVCS[*]}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
stop_svc "$TARGET"
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user