All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m59s
712 lines
30 KiB
Bash
Executable File
712 lines
30 KiB
Bash
Executable File
#!/bin/bash
|
||
# ============================================================================
|
||
# 君鸿卡管系统 - 本地开发环境变量设置脚本
|
||
# ============================================================================
|
||
# 使用方法:
|
||
# 1. 首次设置:./scripts/setup-env.sh
|
||
# 2. 指定文件:./scripts/setup-env.sh .env.gray
|
||
# 3. 加载环境:source .env.local
|
||
# 4. 启动服务:go run cmd/api/main.go
|
||
# ============================================================================
|
||
|
||
set -e
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
CYAN='\033[0;36m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 配置文件路径,可通过 ENV_FILE=/tmp/demo.env ./scripts/setup-env.sh 覆盖,便于本地试生成。
|
||
ENV_FILE="${ENV_FILE:-.env.local}"
|
||
# 输出格式:shell 适合 source,systemd 适合 EnvironmentFile=。
|
||
ENV_FORMAT="${ENV_FORMAT:-shell}"
|
||
|
||
show_usage() {
|
||
cat <<'EOF'
|
||
君鸿卡管系统 - 环境变量配置向导
|
||
|
||
用法:
|
||
./scripts/setup-env.sh
|
||
./scripts/setup-env.sh .env.gray
|
||
./scripts/setup-env.sh --file .env.gray
|
||
./scripts/setup-env.sh -f .env.gray
|
||
./scripts/setup-env.sh --format systemd --file /etc/junhong/cmp-gray.env
|
||
./scripts/setup-env.sh --systemd .env.gray
|
||
|
||
说明:
|
||
不传参数时默认生成 .env.local。
|
||
指定文件名适合灰度、预发、生产等部署环境,例如 .env.gray。
|
||
默认格式为 shell,会生成 export KEY=VALUE。
|
||
systemd 格式会生成 KEY=VALUE,可用于 systemd EnvironmentFile=。
|
||
也可以继续使用 ENV_FILE=.env.gray ./scripts/setup-env.sh。
|
||
EOF
|
||
}
|
||
|
||
parse_args() {
|
||
while [ "$#" -gt 0 ]; do
|
||
case "$1" in
|
||
-h|--help)
|
||
show_usage
|
||
exit 0
|
||
;;
|
||
-f|--file)
|
||
if [ -z "${2:-}" ]; then
|
||
print_error "--file 需要指定生成文件名"
|
||
exit 1
|
||
fi
|
||
if [ "${ENV_FILE_SET_BY_ARG:-false}" = "true" ]; then
|
||
print_error "只能指定一个生成文件名"
|
||
show_usage
|
||
exit 1
|
||
fi
|
||
ENV_FILE="$2"
|
||
ENV_FILE_SET_BY_ARG="true"
|
||
shift 2
|
||
;;
|
||
--format)
|
||
if [ -z "${2:-}" ]; then
|
||
print_error "--format 需要指定 shell 或 systemd"
|
||
exit 1
|
||
fi
|
||
ENV_FORMAT="$2"
|
||
shift 2
|
||
;;
|
||
--format=*)
|
||
ENV_FORMAT="${1#--format=}"
|
||
shift
|
||
;;
|
||
--systemd)
|
||
ENV_FORMAT="systemd"
|
||
shift
|
||
;;
|
||
--file=*)
|
||
if [ "${ENV_FILE_SET_BY_ARG:-false}" = "true" ]; then
|
||
print_error "只能指定一个生成文件名"
|
||
show_usage
|
||
exit 1
|
||
fi
|
||
ENV_FILE="${1#--file=}"
|
||
ENV_FILE_SET_BY_ARG="true"
|
||
shift
|
||
;;
|
||
-*)
|
||
print_error "未知参数: $1"
|
||
show_usage
|
||
exit 1
|
||
;;
|
||
*)
|
||
if [ "${ENV_FILE_SET_BY_ARG:-false}" = "true" ]; then
|
||
print_error "只能指定一个生成文件名"
|
||
show_usage
|
||
exit 1
|
||
fi
|
||
ENV_FILE="$1"
|
||
ENV_FILE_SET_BY_ARG="true"
|
||
shift
|
||
;;
|
||
esac
|
||
done
|
||
|
||
if [ -z "$ENV_FILE" ]; then
|
||
print_error "生成文件名不能为空"
|
||
exit 1
|
||
fi
|
||
|
||
case "$ENV_FORMAT" in
|
||
shell|systemd)
|
||
;;
|
||
*)
|
||
print_error "未知输出格式: $ENV_FORMAT(可选 shell 或 systemd)"
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# 打印带颜色的消息
|
||
print_header() {
|
||
echo -e "\n${BLUE}========================================${NC}"
|
||
echo -e "${BLUE} $1${NC}"
|
||
echo -e "${BLUE}========================================${NC}\n"
|
||
}
|
||
|
||
print_info() {
|
||
echo -e "${CYAN}[INFO]${NC} $1"
|
||
}
|
||
|
||
print_success() {
|
||
echo -e "${GREEN}[OK]${NC} $1"
|
||
}
|
||
|
||
print_warning() {
|
||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}[ERROR]${NC} $1"
|
||
}
|
||
|
||
# 读取用户输入(带默认值)
|
||
read_input() {
|
||
local prompt="$1"
|
||
local default="$2"
|
||
local var_name="$3"
|
||
local is_password="${4:-false}"
|
||
local input=""
|
||
local display_default="$default"
|
||
|
||
if [ -z "$display_default" ]; then
|
||
display_default="空"
|
||
fi
|
||
|
||
if [ "$is_password" = "true" ]; then
|
||
if [ -n "$default" ]; then
|
||
display_default="****"
|
||
fi
|
||
echo -n -e "${CYAN}$prompt${NC} [默认: $display_default]: "
|
||
IFS= read -r -s input
|
||
echo ""
|
||
else
|
||
echo -n -e "${CYAN}$prompt${NC} [默认: $display_default]: "
|
||
IFS= read -r input
|
||
fi
|
||
|
||
if [ -z "$input" ]; then
|
||
printf -v "$var_name" '%s' "$default"
|
||
else
|
||
printf -v "$var_name" '%s' "$input"
|
||
fi
|
||
}
|
||
|
||
ask_yes_no() {
|
||
local prompt="$1"
|
||
local answer=""
|
||
|
||
echo -n "$prompt [y/N]: "
|
||
IFS= read -r answer
|
||
[ "$answer" = "y" ] || [ "$answer" = "Y" ]
|
||
}
|
||
|
||
# 生成随机密钥
|
||
generate_secret() {
|
||
if command -v openssl &> /dev/null; then
|
||
openssl rand -hex 32
|
||
elif command -v xxd &> /dev/null; then
|
||
head -c 32 /dev/urandom | xxd -p
|
||
else
|
||
head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n'
|
||
fi
|
||
}
|
||
|
||
# 检查服务是否可用
|
||
check_service() {
|
||
local host="$1"
|
||
local port="$2"
|
||
local name="$3"
|
||
|
||
if command -v nc &> /dev/null; then
|
||
if nc -z "$host" "$port" 2>/dev/null; then
|
||
print_success "$name ($host:$port) 连接正常"
|
||
return 0
|
||
else
|
||
print_warning "$name ($host:$port) 无法连接"
|
||
return 1
|
||
fi
|
||
else
|
||
print_info "跳过 $name 连接检查(nc 命令不可用)"
|
||
return 0
|
||
fi
|
||
}
|
||
|
||
shell_escape() {
|
||
local value="$1"
|
||
value=${value//\'/\'\\\'\'}
|
||
printf "'%s'" "$value"
|
||
}
|
||
|
||
systemd_escape() {
|
||
local value="$1"
|
||
value=${value//\\/\\\\}
|
||
value=${value//\"/\\\"}
|
||
printf '"%s"' "$value"
|
||
}
|
||
|
||
env_value_escape() {
|
||
case "$ENV_FORMAT" in
|
||
systemd)
|
||
systemd_escape "$1"
|
||
;;
|
||
*)
|
||
shell_escape "$1"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
write_line() {
|
||
printf '%s\n' "$1" >> "$ENV_FILE"
|
||
}
|
||
|
||
write_blank() {
|
||
printf '\n' >> "$ENV_FILE"
|
||
}
|
||
|
||
write_section() {
|
||
write_blank
|
||
write_line "# ----------------------------------------------------------------------------"
|
||
write_line "# $1"
|
||
write_line "# ----------------------------------------------------------------------------"
|
||
}
|
||
|
||
write_note() {
|
||
write_line "# $1"
|
||
}
|
||
|
||
write_export() {
|
||
local name="$1"
|
||
local value="$2"
|
||
local description="$3"
|
||
|
||
write_note "$description"
|
||
if [ "$ENV_FORMAT" = "systemd" ]; then
|
||
printf '%s=%s\n' "$name" "$(env_value_escape "$value")" >> "$ENV_FILE"
|
||
else
|
||
printf 'export %s=%s\n' "$name" "$(env_value_escape "$value")" >> "$ENV_FILE"
|
||
fi
|
||
}
|
||
|
||
write_commented_export() {
|
||
local name="$1"
|
||
local value="$2"
|
||
local description="$3"
|
||
|
||
write_note "$description"
|
||
if [ "$ENV_FORMAT" = "systemd" ]; then
|
||
printf '# %s=%s\n' "$name" "$(env_value_escape "$value")" >> "$ENV_FILE"
|
||
else
|
||
printf '# export %s=%s\n' "$name" "$(env_value_escape "$value")" >> "$ENV_FILE"
|
||
fi
|
||
}
|
||
|
||
init_env_file() {
|
||
: > "$ENV_FILE"
|
||
write_line "# =========================================================================="
|
||
write_line "# 君鸿卡管系统 - 本地开发环境变量"
|
||
write_line "# 生成时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||
write_line "# 配置依据: pkg/config/defaults/config.yaml + pkg/config/loader.go"
|
||
write_line "# 输出格式: $ENV_FORMAT"
|
||
write_line "# =========================================================================="
|
||
if [ "$ENV_FORMAT" = "systemd" ]; then
|
||
write_line "# 使用方法:"
|
||
write_line "# 在 systemd unit 中添加:EnvironmentFile=$ENV_FILE"
|
||
write_line "# 然后执行:systemctl daemon-reload && systemctl restart <service>"
|
||
else
|
||
write_line "# 使用方法:"
|
||
write_line "# source $ENV_FILE && go run cmd/api/main.go"
|
||
write_line "# 或者:"
|
||
write_line "# ./scripts/run-local.sh"
|
||
fi
|
||
write_line "#"
|
||
write_line "# 说明:"
|
||
if [ "$ENV_FORMAT" = "systemd" ]; then
|
||
write_line "# - 未注释的 KEY=VALUE 是 systemd 会读取的配置。"
|
||
write_line "# - 注释掉的 KEY=VALUE 是当前有默认值、按需启用或当前主流程未使用的配置。"
|
||
else
|
||
write_line "# - 未注释的 export 是本地启动常用或必填配置。"
|
||
write_line "# - 注释掉的 export 是当前有默认值、按需启用或当前主流程未使用的配置。"
|
||
fi
|
||
write_line "# - 取消注释前请先阅读上方说明,避免覆盖嵌入配置默认值。"
|
||
write_line "# =========================================================================="
|
||
}
|
||
|
||
write_required_config() {
|
||
write_section "数据库配置(必填)"
|
||
write_export "JUNHONG_DATABASE_HOST" "$DB_HOST" "数据库主机地址;应用启动必填。"
|
||
write_export "JUNHONG_DATABASE_PORT" "$DB_PORT" "数据库端口;默认 5432。"
|
||
write_export "JUNHONG_DATABASE_USER" "$DB_USER" "数据库用户名;应用启动必填。"
|
||
write_export "JUNHONG_DATABASE_PASSWORD" "$DB_PASSWORD" "数据库密码;应用启动必填,敏感信息请勿提交。"
|
||
write_export "JUNHONG_DATABASE_DBNAME" "$DB_NAME" "数据库名称;应用启动必填。"
|
||
write_export "JUNHONG_DATABASE_SSLMODE" "$DB_SSLMODE" "数据库 SSL 模式;本地通常使用 disable。"
|
||
|
||
write_section "Redis 配置(必填)"
|
||
write_export "JUNHONG_REDIS_ADDRESS" "$REDIS_ADDRESS" "Redis 主机地址;应用启动必填。"
|
||
write_export "JUNHONG_REDIS_PORT" "$REDIS_PORT" "Redis 端口;默认 6379。"
|
||
write_export "JUNHONG_REDIS_PASSWORD" "$REDIS_PASSWORD" "Redis 密码;无密码时留空。"
|
||
write_export "JUNHONG_REDIS_DB" "$REDIS_DB" "Redis 数据库编号;默认 0。"
|
||
|
||
write_section "JWT 配置(必填)"
|
||
write_export "JUNHONG_JWT_SECRET_KEY" "$JWT_SECRET_KEY" "JWT 签名密钥;必须至少 32 字符,生产环境必须单独生成。"
|
||
write_commented_export "JUNHONG_JWT_TOKEN_DURATION" "24h" "C 端 JWT Token 有效期;默认 24h,通常无需覆盖。"
|
||
write_commented_export "JUNHONG_JWT_ACCESS_TOKEN_TTL" "24h" "B 端访问令牌 TTL;默认 24h。"
|
||
write_commented_export "JUNHONG_JWT_REFRESH_TOKEN_TTL" "168h" "B 端刷新令牌 TTL;默认 168h。"
|
||
}
|
||
|
||
write_common_config() {
|
||
write_section "服务器配置"
|
||
write_export "JUNHONG_SERVER_ADDRESS" "$SERVER_ADDRESS" "服务监听地址;本地默认 :3000。"
|
||
write_commented_export "JUNHONG_SERVER_READ_TIMEOUT" "30s" "读取请求超时时间;默认 30s,通常无需覆盖。"
|
||
write_commented_export "JUNHONG_SERVER_WRITE_TIMEOUT" "30s" "写响应超时时间;默认 30s,通常无需覆盖。"
|
||
write_commented_export "JUNHONG_SERVER_SHUTDOWN_TIMEOUT" "30s" "优雅关闭超时时间;默认 30s。"
|
||
write_commented_export "JUNHONG_SERVER_PREFORK" "false" "Fiber 预分叉模式;本地和 macOS 开发环境通常不要启用。"
|
||
|
||
write_section "日志配置"
|
||
write_export "JUNHONG_LOGGING_LEVEL" "$LOGGING_LEVEL" "日志级别;可选 debug/info/warn/error。"
|
||
write_export "JUNHONG_LOGGING_DEVELOPMENT" "true" "本地开发模式日志;生产环境应使用 false。"
|
||
write_export "JUNHONG_LOGGING_APP_LOG_FILENAME" "logs/app.log" "应用日志文件路径;本地写入项目 logs 目录。"
|
||
write_export "JUNHONG_LOGGING_ACCESS_LOG_FILENAME" "logs/access.log" "访问日志文件路径;本地写入项目 logs 目录。"
|
||
write_commented_export "JUNHONG_LOGGING_APP_LOG_MAX_SIZE" "100" "应用日志单文件最大大小(MB);默认 100。"
|
||
write_commented_export "JUNHONG_LOGGING_APP_LOG_MAX_BACKUPS" "3" "应用日志最多保留的旧文件数;默认 3。"
|
||
write_commented_export "JUNHONG_LOGGING_APP_LOG_MAX_AGE" "7" "应用日志最多保留天数;默认 7。"
|
||
write_commented_export "JUNHONG_LOGGING_APP_LOG_COMPRESS" "true" "是否压缩应用日志轮转文件;默认 true。"
|
||
write_commented_export "JUNHONG_LOGGING_ACCESS_LOG_MAX_SIZE" "100" "访问日志单文件最大大小(MB);默认 100。"
|
||
write_commented_export "JUNHONG_LOGGING_ACCESS_LOG_MAX_BACKUPS" "3" "访问日志最多保留的旧文件数;默认 3。"
|
||
write_commented_export "JUNHONG_LOGGING_ACCESS_LOG_MAX_AGE" "7" "访问日志最多保留天数;默认 7。"
|
||
write_commented_export "JUNHONG_LOGGING_ACCESS_LOG_COMPRESS" "true" "是否压缩访问日志轮转文件;默认 true。"
|
||
|
||
write_section "客户端配置"
|
||
write_export "JUNHONG_CLIENT_REQUIRE_PHONE_BINDING" "true" "是否强制 C 端用户绑定手机号;当前默认 true。"
|
||
}
|
||
|
||
write_tuning_config() {
|
||
write_section "数据库连接池配置(可选)"
|
||
write_commented_export "JUNHONG_DATABASE_MAX_OPEN_CONNS" "25" "数据库最大打开连接数;默认 25,本地通常不用改。"
|
||
write_commented_export "JUNHONG_DATABASE_MAX_IDLE_CONNS" "10" "数据库最大空闲连接数;默认 10。"
|
||
write_commented_export "JUNHONG_DATABASE_CONN_MAX_LIFETIME" "5m" "数据库连接最大生命周期;默认 5m。"
|
||
|
||
write_section "Redis 连接池配置(可选)"
|
||
write_commented_export "JUNHONG_REDIS_POOL_SIZE" "10" "Redis 连接池大小;默认 10。"
|
||
write_commented_export "JUNHONG_REDIS_MIN_IDLE_CONNS" "5" "Redis 最小空闲连接数;默认 5。"
|
||
write_commented_export "JUNHONG_REDIS_DIAL_TIMEOUT" "5s" "Redis 建连超时;默认 5s。"
|
||
write_commented_export "JUNHONG_REDIS_READ_TIMEOUT" "3s" "Redis 读取超时;默认 3s。"
|
||
write_commented_export "JUNHONG_REDIS_WRITE_TIMEOUT" "3s" "Redis 写入超时;默认 3s。"
|
||
|
||
write_section "任务队列配置(可选)"
|
||
write_commented_export "JUNHONG_QUEUE_CONCURRENCY" "10" "Worker 并发数;默认 10。"
|
||
write_commented_export "JUNHONG_QUEUE_RETRY_MAX" "5" "任务最大重试次数;默认 5。"
|
||
write_commented_export "JUNHONG_QUEUE_TIMEOUT" "10m" "单个任务超时时间;默认 10m。"
|
||
write_note "队列权重 critical/default/low 当前由嵌入配置 queue.queues 管理,未绑定为环境变量。"
|
||
|
||
write_section "限流中间件配置(可选)"
|
||
write_commented_export "JUNHONG_MIDDLEWARE_ENABLE_RATE_LIMITER" "false" "是否启用全局限流;默认关闭。"
|
||
write_commented_export "JUNHONG_MIDDLEWARE_RATE_LIMITER_MAX" "100" "限流窗口内最大请求数;默认 100。"
|
||
write_commented_export "JUNHONG_MIDDLEWARE_RATE_LIMITER_EXPIRATION" "1m" "限流时间窗口;默认 1m。"
|
||
write_commented_export "JUNHONG_MIDDLEWARE_RATE_LIMITER_STORAGE" "memory" "限流存储后端;可选 memory 或 redis。"
|
||
|
||
write_section "跨域中间件配置(可选)"
|
||
write_commented_export "JUNHONG_MIDDLEWARE_CORS_ENABLED" "true" "是否启用 CORS;默认启用。"
|
||
write_commented_export "JUNHONG_MIDDLEWARE_CORS_ALLOW_ORIGINS" "*" "允许访问 API 的前端来源,* 表示全部来源。"
|
||
write_commented_export "JUNHONG_MIDDLEWARE_CORS_ALLOW_METHODS" "GET,POST,PUT,PATCH,DELETE,OPTIONS" "允许跨域调用的 HTTP 方法。"
|
||
write_commented_export "JUNHONG_MIDDLEWARE_CORS_ALLOW_HEADERS" "Origin,Content-Type,Accept,Authorization,X-Requested-With,X-Request-ID" "允许跨域携带的请求头。"
|
||
write_commented_export "JUNHONG_MIDDLEWARE_CORS_ALLOW_CREDENTIALS" "false" "是否允许浏览器携带 Cookie 等凭证;来源为 * 时必须保持 false。"
|
||
|
||
write_section "轮询配置(可选)"
|
||
write_commented_export "JUNHONG_POLLING_VERBOSE_LOG" "false" "是否输出轮询详细日志;默认 false,排查上游数据时再开启。"
|
||
write_commented_export "JUNHONG_POLLING_AUTO_TRIGGER_ENABLE_AUTO_TRIGGER" "true" "是否启用 C 端实名自动触发;默认 true。"
|
||
write_commented_export "JUNHONG_POLLING_AUTO_TRIGGER_AUTO_TRIGGER_SYSTEM_USER_ID" "1" "自动触发使用的系统用户 ID;生产建议改成专用平台账号。"
|
||
|
||
write_section "Worker 运行配置(可选)"
|
||
write_commented_export "JUNHONG_WORKER_ROLE" "all" "Worker 角色;单实例默认 all,多实例可用 leader/consumer。"
|
||
write_commented_export "JUNHONG_WORKER_INSTANCE_NAME" "" "Worker 实例名称;多实例部署时用于区分日志。"
|
||
}
|
||
|
||
write_gateway_config() {
|
||
write_section "Gateway 服务配置"
|
||
if [ "$GATEWAY_CONFIGURED" = "true" ]; then
|
||
write_export "JUNHONG_GATEWAY_BASE_URL" "$GATEWAY_BASE_URL" "Gateway API 基础 URL;已按本次输入覆盖嵌入默认值。"
|
||
write_export "JUNHONG_GATEWAY_APP_ID" "$GATEWAY_APP_ID" "Gateway 应用 ID;敏感信息请勿提交。"
|
||
write_export "JUNHONG_GATEWAY_APP_SECRET" "$GATEWAY_APP_SECRET" "Gateway 应用密钥;敏感信息请勿提交。"
|
||
write_export "JUNHONG_GATEWAY_TIMEOUT" "$GATEWAY_TIMEOUT" "Gateway 请求超时时间(秒);有效范围 5-300。"
|
||
else
|
||
write_note "当前嵌入配置已有默认 Gateway 配置;只有需要覆盖环境差异时再取消注释。"
|
||
write_commented_export "JUNHONG_GATEWAY_BASE_URL" "https://lplan.whjhft.com/openapi" "Gateway API 基础 URL;默认来自嵌入配置。"
|
||
write_commented_export "JUNHONG_GATEWAY_APP_ID" "60bgt1X8i7AvXqkd" "Gateway 应用 ID;默认来自嵌入配置。"
|
||
write_commented_export "JUNHONG_GATEWAY_APP_SECRET" "BZeQttaZQt0i73moF" "Gateway 应用密钥;默认来自嵌入配置,敏感信息请勿提交。"
|
||
write_commented_export "JUNHONG_GATEWAY_TIMEOUT" "30" "Gateway 请求超时时间(秒);默认 30。"
|
||
fi
|
||
}
|
||
|
||
write_sms_config() {
|
||
write_section "短信服务配置(可选)"
|
||
if [ "$SMS_CONFIGURED" = "true" ]; then
|
||
write_export "JUNHONG_SMS_GATEWAY_URL" "$SMS_GATEWAY_URL" "短信网关地址;配置后会初始化短信客户端。"
|
||
write_export "JUNHONG_SMS_USERNAME" "$SMS_USERNAME" "短信账号用户名。"
|
||
write_export "JUNHONG_SMS_PASSWORD" "$SMS_PASSWORD" "短信账号密码;敏感信息请勿提交。"
|
||
write_export "JUNHONG_SMS_SIGNATURE" "$SMS_SIGNATURE" "短信签名,例如【君鸿】。"
|
||
write_export "JUNHONG_SMS_TIMEOUT" "$SMS_TIMEOUT" "短信请求超时时间;有效范围 5s-60s。"
|
||
else
|
||
write_note "短信服务未配置时系统会跳过短信客户端初始化;需要短信验证码时再取消注释。"
|
||
write_commented_export "JUNHONG_SMS_GATEWAY_URL" "" "短信网关地址;设置后 username/password/signature 也必须填写。"
|
||
write_commented_export "JUNHONG_SMS_USERNAME" "" "短信账号用户名。"
|
||
write_commented_export "JUNHONG_SMS_PASSWORD" "" "短信账号密码;敏感信息请勿提交。"
|
||
write_commented_export "JUNHONG_SMS_SIGNATURE" "" "短信签名,例如【君鸿】。"
|
||
write_commented_export "JUNHONG_SMS_TIMEOUT" "10s" "短信请求超时时间;默认 10s。"
|
||
fi
|
||
}
|
||
|
||
write_storage_config() {
|
||
write_section "对象存储配置(可选)"
|
||
if [ "$STORAGE_CONFIGURED" = "true" ]; then
|
||
write_export "JUNHONG_STORAGE_PROVIDER" "s3" "对象存储提供商;当前仅支持 s3 兼容服务。"
|
||
write_export "JUNHONG_STORAGE_TEMP_DIR" "/tmp/junhong-storage" "临时文件目录;用于导入导出等临时文件。"
|
||
write_export "JUNHONG_STORAGE_S3_ENDPOINT" "$STORAGE_S3_ENDPOINT" "S3 端点;配置后会初始化对象存储服务。"
|
||
write_export "JUNHONG_STORAGE_S3_REGION" "$STORAGE_S3_REGION" "S3 区域。"
|
||
write_export "JUNHONG_STORAGE_S3_BUCKET" "$STORAGE_S3_BUCKET" "S3 存储桶名称。"
|
||
write_export "JUNHONG_STORAGE_S3_ACCESS_KEY_ID" "$STORAGE_S3_ACCESS_KEY_ID" "S3 Access Key ID;敏感信息请勿提交。"
|
||
write_export "JUNHONG_STORAGE_S3_SECRET_ACCESS_KEY" "$STORAGE_S3_SECRET_ACCESS_KEY" "S3 Secret Access Key;敏感信息请勿提交。"
|
||
write_export "JUNHONG_STORAGE_S3_USE_SSL" "$STORAGE_S3_USE_SSL" "是否使用 SSL;本地兼容服务常用 false。"
|
||
write_export "JUNHONG_STORAGE_S3_PATH_STYLE" "$STORAGE_S3_PATH_STYLE" "是否使用路径风格;兼容 S3 服务通常使用 true。"
|
||
write_export "JUNHONG_STORAGE_PRESIGN_UPLOAD_EXPIRES" "15m" "预签名上传 URL 有效期;默认 15m。"
|
||
write_export "JUNHONG_STORAGE_PRESIGN_DOWNLOAD_EXPIRES" "24h" "预签名下载 URL 有效期;默认 24h。"
|
||
else
|
||
write_note "未配置 S3 端点时系统会跳过对象存储初始化;导入导出文件能力需要启用。"
|
||
write_commented_export "JUNHONG_STORAGE_PROVIDER" "s3" "对象存储提供商;当前仅支持 s3 兼容服务。"
|
||
write_commented_export "JUNHONG_STORAGE_TEMP_DIR" "/tmp/junhong-storage" "临时文件目录;默认 /tmp/junhong-storage。"
|
||
write_commented_export "JUNHONG_STORAGE_S3_ENDPOINT" "" "S3 端点;为空时对象存储服务不初始化。"
|
||
write_commented_export "JUNHONG_STORAGE_S3_REGION" "" "S3 区域。"
|
||
write_commented_export "JUNHONG_STORAGE_S3_BUCKET" "" "S3 存储桶名称。"
|
||
write_commented_export "JUNHONG_STORAGE_S3_ACCESS_KEY_ID" "" "S3 Access Key ID;敏感信息请勿提交。"
|
||
write_commented_export "JUNHONG_STORAGE_S3_SECRET_ACCESS_KEY" "" "S3 Secret Access Key;敏感信息请勿提交。"
|
||
write_commented_export "JUNHONG_STORAGE_S3_USE_SSL" "false" "是否使用 SSL;默认 false。"
|
||
write_commented_export "JUNHONG_STORAGE_S3_PATH_STYLE" "true" "是否使用路径风格;默认 true。"
|
||
write_commented_export "JUNHONG_STORAGE_PRESIGN_UPLOAD_EXPIRES" "15m" "预签名上传 URL 有效期;默认 15m。"
|
||
write_commented_export "JUNHONG_STORAGE_PRESIGN_DOWNLOAD_EXPIRES" "24h" "预签名下载 URL 有效期;默认 24h。"
|
||
fi
|
||
}
|
||
|
||
write_admin_config() {
|
||
write_section "默认超级管理员配置(可选)"
|
||
write_note "不配置时会使用 pkg/constants 中的默认超管账号;生产环境建议在首次启动前覆盖。"
|
||
write_commented_export "JUNHONG_DEFAULT_ADMIN_USERNAME" "" "默认超级管理员用户名;留空则使用代码内置默认值 admin。"
|
||
write_commented_export "JUNHONG_DEFAULT_ADMIN_PASSWORD" "" "默认超级管理员密码;留空则使用代码内置默认值 Admin@123456。"
|
||
write_commented_export "JUNHONG_DEFAULT_ADMIN_PHONE" "" "默认超级管理员手机号;留空则使用代码内置默认值 13800000000。"
|
||
}
|
||
|
||
write_migration_compat_config() {
|
||
write_section "迁移工具兼容配置(用于 scripts/migrate.sh)"
|
||
write_export "MIGRATIONS_DIR" "migrations" "迁移目录;保留给迁移工具兼容使用。"
|
||
write_export "DB_HOST" "$DB_HOST" "兼容旧迁移脚本的数据库主机。"
|
||
write_export "DB_PORT" "$DB_PORT" "兼容旧迁移脚本的数据库端口。"
|
||
write_export "DB_USER" "$DB_USER" "兼容旧迁移脚本的数据库用户名。"
|
||
write_export "DB_PASSWORD" "$DB_PASSWORD" "兼容旧迁移脚本的数据库密码。"
|
||
write_export "DB_NAME" "$DB_NAME" "兼容旧迁移脚本的数据库名称。"
|
||
write_export "DB_SSLMODE" "$DB_SSLMODE" "兼容旧迁移脚本的 SSL 模式。"
|
||
}
|
||
|
||
generate_env_file() {
|
||
init_env_file
|
||
write_required_config
|
||
write_common_config
|
||
write_tuning_config
|
||
write_gateway_config
|
||
write_sms_config
|
||
write_storage_config
|
||
write_admin_config
|
||
write_migration_compat_config
|
||
}
|
||
|
||
warn_if_env_file_not_ignored() {
|
||
if [ ! -f ".gitignore" ]; then
|
||
return
|
||
fi
|
||
if [ "$ENV_FILE" = ".env.local" ] && ! grep -q "^\.env\.local$" .gitignore; then
|
||
echo ".env.local" >> .gitignore
|
||
print_info "已将 .env.local 添加到 .gitignore"
|
||
return
|
||
fi
|
||
if [[ "$ENV_FILE" = /* ]]; then
|
||
return
|
||
fi
|
||
if command -v git &> /dev/null && git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
|
||
if ! git check-ignore -q -- "$ENV_FILE"; then
|
||
print_warning "当前生成文件 $ENV_FILE 未被 Git 忽略,请勿提交敏感环境变量。"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# 主流程
|
||
main() {
|
||
parse_args "$@"
|
||
|
||
print_header "君鸿卡管系统 - 环境变量配置向导"
|
||
|
||
echo "本脚本将根据当前配置系统生成本地开发环境变量文件。"
|
||
echo "配置将保存到 $ENV_FILE 文件中。"
|
||
echo "输出格式: $ENV_FORMAT"
|
||
echo ""
|
||
|
||
# 检查是否已存在配置文件
|
||
if [ -f "$ENV_FILE" ]; then
|
||
print_warning "发现已存在的配置文件: $ENV_FILE"
|
||
if ! ask_yes_no "是否覆盖?"; then
|
||
print_info "已取消。如需更新配置,请手动编辑 $ENV_FILE"
|
||
exit 0
|
||
fi
|
||
fi
|
||
|
||
# ========================================
|
||
# 连通性检测配置
|
||
# ========================================
|
||
print_header "连通性检测(可选)"
|
||
|
||
if ask_yes_no "是否在填写配置后检测 PostgreSQL/Redis 连通性?"; then
|
||
CHECK_SERVICES="true"
|
||
else
|
||
CHECK_SERVICES="false"
|
||
print_info "已跳过连通性检测;适合灰度、预发、生产等内网地址配置。"
|
||
fi
|
||
|
||
# ========================================
|
||
# 数据库配置
|
||
# ========================================
|
||
print_header "数据库配置 (PostgreSQL)"
|
||
|
||
read_input "数据库主机地址" "localhost" DB_HOST
|
||
read_input "数据库端口" "5432" DB_PORT
|
||
read_input "数据库用户名" "postgres" DB_USER
|
||
read_input "数据库密码" "postgres" DB_PASSWORD "true"
|
||
read_input "数据库名称" "junhong_cmp_dev" DB_NAME
|
||
read_input "SSL 模式" "disable" DB_SSLMODE
|
||
|
||
# 测试数据库连接
|
||
echo ""
|
||
if [ "$CHECK_SERVICES" = "true" ]; then
|
||
check_service "$DB_HOST" "$DB_PORT" "PostgreSQL" || true
|
||
else
|
||
print_info "跳过 PostgreSQL 连通性检测"
|
||
fi
|
||
|
||
# ========================================
|
||
# Redis 配置
|
||
# ========================================
|
||
print_header "Redis 配置"
|
||
|
||
read_input "Redis 主机地址" "localhost" REDIS_ADDRESS
|
||
read_input "Redis 端口" "6379" REDIS_PORT
|
||
read_input "Redis 密码(无密码直接回车)" "" REDIS_PASSWORD "true"
|
||
read_input "Redis 数据库编号" "0" REDIS_DB
|
||
|
||
# 测试 Redis 连接
|
||
echo ""
|
||
if [ "$CHECK_SERVICES" = "true" ]; then
|
||
check_service "$REDIS_ADDRESS" "$REDIS_PORT" "Redis" || true
|
||
else
|
||
print_info "跳过 Redis 连通性检测"
|
||
fi
|
||
|
||
# ========================================
|
||
# JWT 配置
|
||
# ========================================
|
||
print_header "JWT 配置"
|
||
|
||
DEFAULT_SECRET=$(generate_secret)
|
||
read_input "JWT 密钥(直接回车生成随机密钥)" "$DEFAULT_SECRET" JWT_SECRET_KEY "true"
|
||
|
||
# ========================================
|
||
# 服务器和日志配置
|
||
# ========================================
|
||
print_header "服务器与日志配置"
|
||
|
||
read_input "服务监听地址" ":3000" SERVER_ADDRESS
|
||
read_input "日志级别 (debug/info/warn/error)" "debug" LOGGING_LEVEL
|
||
|
||
# ========================================
|
||
# Gateway 配置
|
||
# ========================================
|
||
print_header "Gateway 配置(可选覆盖)"
|
||
|
||
if ask_yes_no "是否覆盖 Gateway 服务配置?"; then
|
||
read_input "Gateway API 基础 URL" "https://lplan.whjhft.com/openapi" GATEWAY_BASE_URL
|
||
read_input "Gateway 应用 ID" "" GATEWAY_APP_ID
|
||
read_input "Gateway 应用密钥" "" GATEWAY_APP_SECRET "true"
|
||
read_input "Gateway 请求超时时间(秒)" "30" GATEWAY_TIMEOUT
|
||
GATEWAY_CONFIGURED="true"
|
||
else
|
||
GATEWAY_CONFIGURED="false"
|
||
fi
|
||
|
||
# ========================================
|
||
# 短信配置
|
||
# ========================================
|
||
print_header "短信配置(可选)"
|
||
|
||
if ask_yes_no "是否配置短信服务?"; then
|
||
read_input "短信网关 URL" "" SMS_GATEWAY_URL
|
||
read_input "短信账号用户名" "" SMS_USERNAME
|
||
read_input "短信账号密码" "" SMS_PASSWORD "true"
|
||
read_input "短信签名" "" SMS_SIGNATURE
|
||
read_input "短信请求超时时间" "10s" SMS_TIMEOUT
|
||
SMS_CONFIGURED="true"
|
||
else
|
||
SMS_CONFIGURED="false"
|
||
fi
|
||
|
||
# ========================================
|
||
# 对象存储配置
|
||
# ========================================
|
||
print_header "对象存储配置(可选)"
|
||
|
||
if ask_yes_no "是否配置对象存储?"; then
|
||
read_input "S3 端点" "" STORAGE_S3_ENDPOINT
|
||
read_input "S3 区域" "" STORAGE_S3_REGION
|
||
read_input "S3 存储桶" "" STORAGE_S3_BUCKET
|
||
read_input "S3 Access Key ID" "" STORAGE_S3_ACCESS_KEY_ID
|
||
read_input "S3 Secret Access Key" "" STORAGE_S3_SECRET_ACCESS_KEY "true"
|
||
read_input "是否使用 SSL(true/false)" "false" STORAGE_S3_USE_SSL
|
||
read_input "是否使用路径风格(true/false)" "true" STORAGE_S3_PATH_STYLE
|
||
STORAGE_CONFIGURED="true"
|
||
else
|
||
STORAGE_CONFIGURED="false"
|
||
fi
|
||
|
||
# ========================================
|
||
# 生成配置文件
|
||
# ========================================
|
||
print_header "生成配置文件"
|
||
|
||
generate_env_file
|
||
|
||
print_success "配置文件已生成: $ENV_FILE"
|
||
|
||
# ========================================
|
||
# 后续步骤提示
|
||
# ========================================
|
||
print_header "设置完成!"
|
||
|
||
echo -e "${GREEN}环境变量已配置完成!${NC}"
|
||
echo ""
|
||
echo "后续步骤:"
|
||
echo ""
|
||
if [ "$ENV_FORMAT" = "systemd" ]; then
|
||
echo " 1. 在 systemd unit 中引用:"
|
||
echo -e " ${CYAN}EnvironmentFile=$ENV_FILE${NC}"
|
||
echo ""
|
||
echo " 2. 重载并重启服务:"
|
||
echo -e " ${CYAN}systemctl daemon-reload && systemctl restart <service>${NC}"
|
||
echo ""
|
||
echo " 3. 需要手动迁移时,可临时生成 shell 格式文件或手动导出 DB_* 兼容变量。"
|
||
else
|
||
echo " 1. 加载环境变量:"
|
||
echo -e " ${CYAN}source $ENV_FILE${NC}"
|
||
echo ""
|
||
echo " 2. 运行数据库迁移(如果需要):"
|
||
echo -e " ${CYAN}./scripts/migrate.sh up${NC}"
|
||
echo ""
|
||
echo " 3. 启动 API 服务:"
|
||
echo -e " ${CYAN}go run cmd/api/main.go${NC}"
|
||
echo ""
|
||
echo " 4. 启动 Worker 服务(可选):"
|
||
echo -e " ${CYAN}go run cmd/worker/main.go${NC}"
|
||
echo ""
|
||
echo " 或者使用快捷脚本:"
|
||
echo -e " ${CYAN}./scripts/run-local.sh${NC}"
|
||
fi
|
||
echo ""
|
||
print_warning "注意:$ENV_FILE 包含敏感信息,请勿提交到 Git!"
|
||
|
||
warn_if_env_file_not_ignored
|
||
}
|
||
|
||
# 运行主流程
|
||
main "$@"
|