build: refresh docker deployment workflow
This commit is contained in:
Regular → Executable
+246
-17
@@ -12,6 +12,174 @@ NETWORK_MODE=""
|
||||
LOCAL_IP=""
|
||||
PUBLIC_IP=""
|
||||
DEPLOYMENT_MODE="basic"
|
||||
FORCED_MODE=""
|
||||
LOCAL_IP_OVERRIDE=""
|
||||
|
||||
declare -a IP_CANDIDATES=()
|
||||
declare -A __SEEN_IPS=()
|
||||
|
||||
add_ip_candidate() {
|
||||
local ip="$1"
|
||||
[[ -z "$ip" ]] && return
|
||||
[[ "$ip" == "127."* ]] && return
|
||||
[[ "$ip" == "0.0.0.0" ]] && return
|
||||
if [[ -z "${__SEEN_IPS[$ip]}" ]]; then
|
||||
IP_CANDIDATES+=("$ip")
|
||||
__SEEN_IPS[$ip]=1
|
||||
fi
|
||||
}
|
||||
|
||||
is_rfc1918_ip() {
|
||||
local ip="$1"
|
||||
case "$ip" in
|
||||
10.*|192.168.*|172.1[6-9].*|172.2[0-9].*|172.3[0-1].*)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
is_cgnat_ip() {
|
||||
local ip="$1"
|
||||
case "$ip" in
|
||||
100.*)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
is_reserved_benchmark_ip() {
|
||||
local ip="$1"
|
||||
case "$ip" in
|
||||
198.18.*|198.19.*)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
is_link_local_ip() {
|
||||
local ip="$1"
|
||||
case "$ip" in
|
||||
169.254.*)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
is_routable_public_ip() {
|
||||
local ip="$1"
|
||||
if [[ -z "$ip" ]]; then
|
||||
return 1
|
||||
fi
|
||||
if is_rfc1918_ip "$ip"; then
|
||||
return 1
|
||||
fi
|
||||
if is_cgnat_ip "$ip"; then
|
||||
return 1
|
||||
fi
|
||||
if is_reserved_benchmark_ip "$ip"; then
|
||||
return 1
|
||||
fi
|
||||
case "$ip" in
|
||||
127.*|169.254.*)
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
collect_ip_candidates() {
|
||||
IP_CANDIDATES=()
|
||||
unset __SEEN_IPS
|
||||
declare -A __SEEN_IPS=()
|
||||
|
||||
if command -v hostname >/dev/null 2>&1; then
|
||||
local host_ips
|
||||
host_ips=$(hostname -I 2>/dev/null || true)
|
||||
for ip in $host_ips; do
|
||||
add_ip_candidate "$ip"
|
||||
done
|
||||
fi
|
||||
|
||||
if command -v ip >/dev/null 2>&1; then
|
||||
while IFS= read -r ip; do
|
||||
add_ip_candidate "$ip"
|
||||
done < <(ip -o -4 addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1)
|
||||
fi
|
||||
|
||||
if command -v ifconfig >/dev/null 2>&1; then
|
||||
while IFS= read -r ip; do
|
||||
add_ip_candidate "$ip"
|
||||
done < <(ifconfig 2>/dev/null | awk '/inet / {print $2}' | grep -E '^[0-9]+(\.[0-9]+){3}$')
|
||||
fi
|
||||
|
||||
if command -v ip >/dev/null 2>&1; then
|
||||
local route_ip
|
||||
route_ip=$(ip route get 1.1.1.1 2>/dev/null | awk '{for (i=1;i<=NF;i++) if ($i=="src") {print $(i+1); exit}}')
|
||||
add_ip_candidate "$route_ip"
|
||||
fi
|
||||
|
||||
if [[ ${#IP_CANDIDATES[@]} -eq 0 ]]; then
|
||||
local fallback
|
||||
fallback=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||
add_ip_candidate "$fallback"
|
||||
fi
|
||||
}
|
||||
|
||||
resolve_local_ip() {
|
||||
if [[ -n "$LOCAL_IP_OVERRIDE" ]]; then
|
||||
LOCAL_IP="$LOCAL_IP_OVERRIDE"
|
||||
return
|
||||
fi
|
||||
|
||||
collect_ip_candidates
|
||||
|
||||
if [[ ${#IP_CANDIDATES[@]} -eq 0 ]]; then
|
||||
LOCAL_IP=""
|
||||
return
|
||||
fi
|
||||
|
||||
local ip
|
||||
for ip in "${IP_CANDIDATES[@]}"; do
|
||||
if is_rfc1918_ip "$ip"; then
|
||||
LOCAL_IP="$ip"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
for ip in "${IP_CANDIDATES[@]}"; do
|
||||
if is_cgnat_ip "$ip"; then
|
||||
LOCAL_IP="$ip"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
for ip in "${IP_CANDIDATES[@]}"; do
|
||||
if is_reserved_benchmark_ip "$ip"; then
|
||||
continue
|
||||
fi
|
||||
if is_link_local_ip "$ip"; then
|
||||
continue
|
||||
fi
|
||||
LOCAL_IP="$ip"
|
||||
return
|
||||
done
|
||||
|
||||
LOCAL_IP="${IP_CANDIDATES[0]}"
|
||||
}
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
@@ -33,35 +201,80 @@ log_error() {
|
||||
# 检测网络环境
|
||||
detect_network_environment() {
|
||||
log_info "检测网络环境..."
|
||||
|
||||
# 获取本机IP
|
||||
LOCAL_IP=$(ip route get 1.1.1.1 2>/dev/null | awk '{print $7}' | head -1)
|
||||
if [[ -z "$LOCAL_IP" ]]; then
|
||||
LOCAL_IP=$(hostname -I | awk '{print $1}')
|
||||
fi
|
||||
|
||||
|
||||
resolve_local_ip
|
||||
|
||||
if [[ -z "$LOCAL_IP" ]]; then
|
||||
LOCAL_IP="127.0.0.1"
|
||||
log_warning "无法自动检测本机IP,使用默认值: $LOCAL_IP"
|
||||
fi
|
||||
|
||||
# 检测公网连接
|
||||
|
||||
if [[ "$FORCED_MODE" == "private" ]]; then
|
||||
NETWORK_MODE="private"
|
||||
PUBLIC_IP=""
|
||||
log_info "已通过参数指定网络模式: $NETWORK_MODE"
|
||||
echo " 本机IP: $LOCAL_IP"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local mode_guess="private"
|
||||
local printed_prompt_info="false"
|
||||
PUBLIC_IP=""
|
||||
|
||||
if curl -s --connect-timeout 5 --max-time 10 ifconfig.me > /dev/null 2>&1; then
|
||||
PUBLIC_IP=$(curl -s --connect-timeout 5 --max-time 10 ifconfig.me 2>/dev/null || echo "")
|
||||
if [[ -n "$PUBLIC_IP" ]]; then
|
||||
NETWORK_MODE="public"
|
||||
log_success "检测到公网环境"
|
||||
if is_routable_public_ip "$PUBLIC_IP"; then
|
||||
mode_guess="public"
|
||||
else
|
||||
log_warning "检测到测试或保留网段公网IP,按内网环境处理"
|
||||
fi
|
||||
else
|
||||
log_warning "公网连接不稳定,按内网环境处理"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$FORCED_MODE" ]]; then
|
||||
if [[ "$mode_guess" == "public" ]]; then
|
||||
echo " 本机IP: $LOCAL_IP"
|
||||
echo " 公网IP: $PUBLIC_IP"
|
||||
printed_prompt_info="true"
|
||||
read -r -p "是否按公网模式继续?(Y/n): " confirm </dev/tty 2>/dev/null || confirm="Y"
|
||||
confirm=${confirm:-Y}
|
||||
if [[ "$confirm" =~ ^[Yy]$ ]]; then
|
||||
NETWORK_MODE="public"
|
||||
else
|
||||
NETWORK_MODE="private"
|
||||
PUBLIC_IP=""
|
||||
log_warning "按用户选择,已切换为内网模式"
|
||||
fi
|
||||
else
|
||||
NETWORK_MODE="private"
|
||||
log_warning "公网连接不稳定,按内网环境处理"
|
||||
echo " 本机IP: $LOCAL_IP"
|
||||
fi
|
||||
else
|
||||
NETWORK_MODE="private"
|
||||
NETWORK_MODE="$FORCED_MODE"
|
||||
if [[ "$FORCED_MODE" == "public" && -z "$PUBLIC_IP" ]]; then
|
||||
log_warning "未能检测到公网IP,仍按公网模式继续,请确认网络配置"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$NETWORK_MODE" != "public" ]]; then
|
||||
PUBLIC_IP=""
|
||||
fi
|
||||
|
||||
if [[ "$FORCED_MODE" == "public" ]]; then
|
||||
log_info "已通过参数指定网络模式: $NETWORK_MODE"
|
||||
elif [[ "$NETWORK_MODE" == "public" ]]; then
|
||||
log_success "检测到公网环境"
|
||||
else
|
||||
log_success "检测到内网环境"
|
||||
fi
|
||||
|
||||
if [[ "$printed_prompt_info" == "false" ]]; then
|
||||
echo " 本机IP: $LOCAL_IP"
|
||||
if [[ "$NETWORK_MODE" == "public" && -n "$PUBLIC_IP" ]]; then
|
||||
echo " 公网IP: $PUBLIC_IP"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -181,7 +394,7 @@ check_port_availability() {
|
||||
|
||||
if [[ ${#occupied_ports[@]} -gt 0 ]]; then
|
||||
log_warning "以下端口已被占用: ${occupied_ports[*]}"
|
||||
log_info "可以通过修改.env文件中的端口配置来解决冲突"
|
||||
log_info "可以通过修改 .env 中的端口,或先执行 './deploy.sh --clean' / 'docker-compose down' 清理旧容器"
|
||||
else
|
||||
log_success "所有端口都可用"
|
||||
fi
|
||||
@@ -216,6 +429,21 @@ main() {
|
||||
;;
|
||||
--mode)
|
||||
DEPLOYMENT_MODE="$2"
|
||||
case "$2" in
|
||||
private|basic)
|
||||
FORCED_MODE="private"
|
||||
;;
|
||||
public|full)
|
||||
FORCED_MODE="public"
|
||||
;;
|
||||
*)
|
||||
FORCED_MODE=""
|
||||
;;
|
||||
esac
|
||||
shift 2
|
||||
;;
|
||||
--local-ip)
|
||||
LOCAL_IP_OVERRIDE="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
@@ -240,7 +468,7 @@ main() {
|
||||
fi
|
||||
echo ""
|
||||
|
||||
check_port_availability "80,443,3000,3001,3478,5349,6379"
|
||||
check_port_availability "80,443,3002,3001,3478,5349,6379"
|
||||
echo ""
|
||||
|
||||
detect_deployment_mode
|
||||
@@ -259,6 +487,7 @@ main() {
|
||||
export PUBLIC_IP
|
||||
export DEPLOYMENT_MODE
|
||||
export DOMAIN_NAME
|
||||
export LOCAL_IP_OVERRIDE
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -266,4 +495,4 @@ main() {
|
||||
# 如果脚本被直接执行
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
fi
|
||||
|
||||
Regular → Executable
+114
-23
@@ -33,10 +33,75 @@ generate_env_file() {
|
||||
log_info "生成环境变量配置..."
|
||||
|
||||
local env_file=".env"
|
||||
|
||||
# 生成随机密码
|
||||
local turn_password=$(openssl rand -base64 32 2>/dev/null || echo "privydrop$(date +%s)")
|
||||
|
||||
|
||||
# 读取已有配置以保留用户自定义字段(如代理、TURN)
|
||||
declare -A existing_env=()
|
||||
if [[ -f "$env_file" ]]; then
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" || "${line:0:1}" == "#" ]] && continue
|
||||
if [[ "$line" == *=* ]]; then
|
||||
local key="${line%%=*}"
|
||||
local value="${line#*=}"
|
||||
existing_env[$key]="$value"
|
||||
fi
|
||||
done < "$env_file"
|
||||
fi
|
||||
|
||||
# 生成随机密码(同时保存到全局变量,供后续生成 TURN 配置使用)
|
||||
local turn_password="${existing_env[TURN_PASSWORD]}"
|
||||
if [[ -z "$turn_password" ]]; then
|
||||
turn_password=$(openssl rand -base64 32 2>/dev/null || echo "privydrop$(date +%s)")
|
||||
fi
|
||||
|
||||
# 计算不同部署模式下的访问入口
|
||||
local cors_origin="http://${LOCAL_IP}:3002"
|
||||
local api_url="http://${LOCAL_IP}:3001"
|
||||
local ssl_mode="self-signed"
|
||||
local turn_enabled="false"
|
||||
local turn_host_value=""
|
||||
local turn_realm_value="${existing_env[TURN_REALM]:-turn.local}"
|
||||
local turn_username_value="${existing_env[TURN_USERNAME]:-privydrop}"
|
||||
local next_public_turn_host=""
|
||||
local next_public_turn_username=""
|
||||
local next_public_turn_password=""
|
||||
|
||||
if [[ "$DEPLOYMENT_MODE" == "public" ]]; then
|
||||
cors_origin="http://${DOMAIN_NAME:-$LOCAL_IP}"
|
||||
api_url="$cors_origin"
|
||||
turn_enabled="true"
|
||||
elif [[ "$DEPLOYMENT_MODE" == "full" ]]; then
|
||||
cors_origin="https://${DOMAIN_NAME:-$LOCAL_IP}"
|
||||
api_url="$cors_origin"
|
||||
ssl_mode="letsencrypt"
|
||||
turn_enabled="true"
|
||||
fi
|
||||
|
||||
if [[ "$turn_enabled" == "true" ]]; then
|
||||
if [[ -n "$DOMAIN_NAME" ]]; then
|
||||
turn_host_value="turn.${DOMAIN_NAME}"
|
||||
turn_realm_value="turn.${DOMAIN_NAME}"
|
||||
else
|
||||
turn_host_value="$LOCAL_IP"
|
||||
turn_realm_value="turn.local"
|
||||
fi
|
||||
|
||||
next_public_turn_host="$turn_host_value"
|
||||
next_public_turn_username="$turn_username_value"
|
||||
next_public_turn_password="$turn_password"
|
||||
fi
|
||||
|
||||
local default_no_proxy="localhost,127.0.0.1,backend,frontend,redis,coturn"
|
||||
local http_proxy_value="${HTTP_PROXY:-${existing_env[HTTP_PROXY]}}"
|
||||
local https_proxy_value="${HTTPS_PROXY:-${existing_env[HTTPS_PROXY]}}"
|
||||
local no_proxy_value="${NO_PROXY:-${existing_env[NO_PROXY]:-$default_no_proxy}}"
|
||||
|
||||
# 将关键 TURN 参数暴露给后续步骤
|
||||
TURN_ENABLED="$turn_enabled"
|
||||
TURN_USERNAME="$turn_username_value"
|
||||
TURN_PASSWORD="$turn_password"
|
||||
TURN_REALM="$turn_realm_value"
|
||||
TURN_HOST="$turn_host_value"
|
||||
|
||||
cat > "$env_file" << EOF
|
||||
# PrivyDrop Docker 配置文件
|
||||
# 自动生成时间: $(date)
|
||||
@@ -46,13 +111,16 @@ generate_env_file() {
|
||||
# =============================================================================
|
||||
# 网络配置
|
||||
# =============================================================================
|
||||
CORS_ORIGIN=http://${LOCAL_IP}
|
||||
NEXT_PUBLIC_API_URL=http://${LOCAL_IP}:3001
|
||||
CORS_ORIGIN=${cors_origin}
|
||||
NEXT_PUBLIC_API_URL=${api_url}
|
||||
NEXT_PUBLIC_TURN_HOST=${next_public_turn_host}
|
||||
NEXT_PUBLIC_TURN_USERNAME=${next_public_turn_username}
|
||||
NEXT_PUBLIC_TURN_PASSWORD=${next_public_turn_password}
|
||||
|
||||
# =============================================================================
|
||||
# 端口配置
|
||||
# =============================================================================
|
||||
FRONTEND_PORT=3000
|
||||
FRONTEND_PORT=3002
|
||||
BACKEND_PORT=3001
|
||||
HTTP_PORT=80
|
||||
HTTPS_PORT=443
|
||||
@@ -74,16 +142,16 @@ PUBLIC_IP=${PUBLIC_IP:-}
|
||||
# =============================================================================
|
||||
# SSL配置
|
||||
# =============================================================================
|
||||
SSL_MODE=self-signed
|
||||
SSL_MODE=${ssl_mode}
|
||||
DOMAIN_NAME=${DOMAIN_NAME:-}
|
||||
|
||||
# =============================================================================
|
||||
# TURN服务器配置 (可选)
|
||||
# =============================================================================
|
||||
TURN_ENABLED=${TURN_ENABLED:-false}
|
||||
TURN_USERNAME=privydrop
|
||||
TURN_ENABLED=${turn_enabled}
|
||||
TURN_USERNAME=${turn_username_value}
|
||||
TURN_PASSWORD=${turn_password}
|
||||
TURN_REALM=${DOMAIN_NAME:-turn.local}
|
||||
TURN_REALM=${turn_realm_value}
|
||||
|
||||
# =============================================================================
|
||||
# Nginx配置
|
||||
@@ -94,18 +162,15 @@ NGINX_SERVER_NAME=${DOMAIN_NAME:-${LOCAL_IP}}
|
||||
# 日志配置
|
||||
# =============================================================================
|
||||
LOG_LEVEL=info
|
||||
|
||||
# =============================================================================
|
||||
# 代理配置 (可选)
|
||||
# =============================================================================
|
||||
HTTP_PROXY=${http_proxy_value}
|
||||
HTTPS_PROXY=${https_proxy_value}
|
||||
NO_PROXY=${no_proxy_value}
|
||||
EOF
|
||||
|
||||
# 根据部署模式调整配置
|
||||
if [[ "$DEPLOYMENT_MODE" == "full" ]]; then
|
||||
sed -i "s|CORS_ORIGIN=http://|CORS_ORIGIN=https://|g" "$env_file"
|
||||
sed -i "s|NEXT_PUBLIC_API_URL=http://|NEXT_PUBLIC_API_URL=https://|g" "$env_file"
|
||||
sed -i "s|SSL_MODE=self-signed|SSL_MODE=letsencrypt|g" "$env_file"
|
||||
sed -i "s|TURN_ENABLED=false|TURN_ENABLED=true|g" "$env_file"
|
||||
elif [[ "$DEPLOYMENT_MODE" == "public" ]]; then
|
||||
sed -i "s|TURN_ENABLED=false|TURN_ENABLED=true|g" "$env_file"
|
||||
fi
|
||||
|
||||
log_success "环境变量配置已生成: $env_file"
|
||||
}
|
||||
|
||||
@@ -117,7 +182,7 @@ generate_nginx_config() {
|
||||
|
||||
local server_name="${DOMAIN_NAME:-${LOCAL_IP} localhost}"
|
||||
local upstream_backend="backend:3001"
|
||||
local upstream_frontend="frontend:3000"
|
||||
local upstream_frontend="frontend:3002"
|
||||
|
||||
# 生成主Nginx配置
|
||||
cat > docker/nginx/nginx.conf << 'EOF'
|
||||
@@ -548,6 +613,32 @@ main() {
|
||||
echo -e "${BLUE}=== PrivyDrop 配置生成 ===${NC}"
|
||||
echo ""
|
||||
|
||||
# 解析参数(与环境检测脚本保持一致)
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--domain)
|
||||
DOMAIN_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--mode)
|
||||
DEPLOYMENT_MODE="$2"
|
||||
if [[ "$2" == "private" || "$2" == "basic" ]]; then
|
||||
FORCED_MODE="private"
|
||||
elif [[ "$2" == "public" || "$2" == "full" ]]; then
|
||||
FORCED_MODE="public"
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
--local-ip)
|
||||
LOCAL_IP_OVERRIDE="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 首先运行环境检测
|
||||
if ! detect_network_environment; then
|
||||
log_error "环境检测失败"
|
||||
@@ -597,4 +688,4 @@ main() {
|
||||
# 如果脚本被直接执行
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
fi
|
||||
|
||||
Regular → Executable
+5
-5
@@ -92,7 +92,7 @@ test_network_connectivity() {
|
||||
echo -e "${BLUE}=== 网络连接测试 ===${NC}"
|
||||
|
||||
# 测试端口连通性
|
||||
local ports=("3000:前端" "3001:后端" "6379:Redis")
|
||||
local ports=("3002:前端" "3001:后端" "6379:Redis")
|
||||
|
||||
for port_info in "${ports[@]}"; do
|
||||
local port=$(echo "$port_info" | cut -d':' -f1)
|
||||
@@ -114,7 +114,7 @@ test_api_functionality() {
|
||||
|
||||
# 健康检查API
|
||||
run_test "后端健康检查API" "curl -f http://localhost:3001/health"
|
||||
run_test "前端健康检查API" "curl -f http://localhost:3000/api/health"
|
||||
run_test "前端健康检查API" "curl -f http://localhost:3002/api/health"
|
||||
|
||||
# 后端详细健康检查
|
||||
if curl -f http://localhost:3001/health/detailed >/dev/null 2>&1; then
|
||||
@@ -144,7 +144,7 @@ test_webrtc_functionality() {
|
||||
echo -e "${BLUE}=== WebRTC功能测试 ===${NC}"
|
||||
|
||||
# 测试前端页面加载
|
||||
if curl -f http://localhost:3000 >/dev/null 2>&1; then
|
||||
if curl -f http://localhost:3002 >/dev/null 2>&1; then
|
||||
log_success "前端页面可访问"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
@@ -350,7 +350,7 @@ generate_report() {
|
||||
echo -e "${GREEN}🎉 所有测试通过!PrivyDrop 部署成功!${NC}"
|
||||
echo ""
|
||||
echo "🔗 访问链接:"
|
||||
echo " 前端应用: http://localhost:3000"
|
||||
echo " 前端应用: http://localhost:3002"
|
||||
echo " 后端API: http://localhost:3001"
|
||||
|
||||
# 显示局域网访问地址
|
||||
@@ -359,7 +359,7 @@ generate_report() {
|
||||
if [[ -n "$local_ip" && "$local_ip" != "127.0.0.1" ]]; then
|
||||
echo ""
|
||||
echo "🌐 局域网访问:"
|
||||
echo " 前端应用: http://$local_ip:3000"
|
||||
echo " 前端应用: http://$local_ip:3002"
|
||||
echo " 后端API: http://$local_ip:3001"
|
||||
fi
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user