From c7e80cc2cd7c4e59b2fc47b06ddbe984e6b0d8e8 Mon Sep 17 00:00:00 2001 From: tangwang Date: Tue, 10 Mar 2026 10:40:14 +0800 Subject: [PATCH] 新的 .env 管理机制如下: 1. 新增 `scripts/init_env.sh` - 若 `.env` 不存在,从 `.env.example` 复制生成 - 支持 `--force`:覆盖 `.env` 并备份为 `.env.bak` - 首次搭建时统一执行:`./scripts/init_env.sh` --- .env | 41 +++++++++++++++++++++++++++++++++++++++++ .env.example | 4 +++- README.md | 2 ++ activate.sh | 30 ++++-------------------------- api/translator_app.py | 1 - docs/QUICKSTART.md | 14 ++++++++++---- scripts/create_tenant_index.sh | 9 ++++----- scripts/init_env.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ scripts/lib/load_env.sh | 40 ++++++++++++++++++++++++++++++++++++++++ scripts/service_ctl.sh | 35 ++++------------------------------- setup.sh | 7 +------ 11 files changed, 151 insertions(+), 74 deletions(-) create mode 100644 .env create mode 100755 scripts/init_env.sh create mode 100644 scripts/lib/load_env.sh diff --git a/.env b/.env new file mode 100644 index 0000000..3aafe7d --- /dev/null +++ b/.env @@ -0,0 +1,41 @@ +# Elasticsearch Configuration +ES_HOST=http://localhost:9200 +ES_USERNAME=saas +ES_PASSWORD=4hOaLaf41y2VuI8y + +# Redis Configuration (Optional) - AI 生产 10.200.16.14:6479 +REDIS_HOST=10.200.16.14 +REDIS_PORT=6479 +REDIS_PASSWORD=dxEkegEZ@C5SXWKv + +# DeepL Translation API +DEEPL_AUTH_KEY=c9293ab4-ad25-479b-919f-ab4e63b429ed + +# API Service Configuration +API_HOST=0.0.0.0 +API_PORT=6002 + +# MySQL Database Configuration (Shoplazza) - AI 生产 10.200.16.14:3316 +DB_HOST=10.200.16.14 +DB_PORT=3316 +DB_DATABASE=saas +DB_USERNAME=root +DB_PASSWORD=qY8tgodLoA&KT#yQ + +# Model Directories +TEXT_MODEL_DIR=/data/tw/models/bge-m3 # 已经改为web请求了,不使用本地模型 +IMAGE_MODEL_DIR=/data/tw/models/cn-clip # 已经改为web请求了,不使用本地模型 + +# Cache Directory +CACHE_DIR=.cache + +# Frontend API Base URL +API_BASE_URL=http://43.166.252.75:6002 + + +# 国内 +DASHSCOPE_API_KEY=sk-c3b8d4db061840aa8effb748df2a997b +# 美国 +DASHSCOPE_API_KEY=sk-482cc3ff37a8467dab134a7a46830556 + +OPENAI_API_KEY=sk-HvmTMKtuznibZ75l7L2uF2jiaYocCthqd8Cbdkl09KTE7Ft0 diff --git a/.env.example b/.env.example index b50e7c8..3a50c45 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,8 @@ # Environment Configuration Template -# Copy this file to .env and update with your actual values. +# Run: ./scripts/init_env.sh (copies this file to .env if missing) +# Then edit .env with your actual values. # 生产/测试凭证与远程登录方式见 docs/QUICKSTART.md §1.6 +# Note: Values with special chars (#, $, spaces) should be quoted, e.g. PASSWORD="my#pass" # Elasticsearch (生产默认 10.200.16.14:9200,本地可用 localhost) ES_HOST=http://localhost:9200 diff --git a/README.md b/README.md index 21eaf8f..8c4ecc7 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ README 用于给后续开发者建立统一认知:**系统框架、模块边 ```bash # 首次创建环境(默认基础依赖) ./scripts/create_venv.sh +./scripts/init_env.sh # 从 .env.example 生成本地 .env(若不存在) source activate.sh # 启动核心服务(backend/indexer/frontend) @@ -67,6 +68,7 @@ START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh - **单一配置源**:服务地址、provider 选择、后端参数统一在 `config/config.yaml`,环境变量仅做覆盖。 - **接口契约优先**:外部 API 契约与 provider 契约稳定,内部重构不影响调用方。 - **扩展走工厂**:新增 provider/backend 必须在工厂函数中显式注册,禁止旁路分支。 +- **简洁和规范优先**:对待异常情况,分情况而定,如果是系统启动期间、加载资源/配置等出错,可以尽早暴露问题,不要fallback走一套跟配置不符合的另外一套机制,即尽量保证逻辑清晰、简洁;但是线上的服务如果遇到异常情况可以根据最佳实践选取应对方法,比如记录错误日志并且保证服务正常运行。规范优先,尽量用一套机制满足需求,而不是兼容各种不合理的情况,对于不合理的内容优先清理、重构,而不是兼容,以保证代码是简洁的。 - **可观测性优先**:健康检查、关键日志、请求上下文必须可追踪。 - **测试优先保障契约**:CI 首先保证接口契约和核心路径可用,再逐步扩展性能与业务测试。 diff --git a/activate.sh b/activate.sh index f0e6be4..3afad50 100644 --- a/activate.sh +++ b/activate.sh @@ -27,31 +27,9 @@ else return 1 fi -# 如果需要加载 .env 中的环境变量 -ENV_FILE="${PROJECT_ROOT}/.env" -if [ -f "${ENV_FILE}" ]; then - while IFS= read -r line || [ -n "${line}" ]; do - line="${line%$'\r'}" - [[ -z "${line//[[:space:]]/}" ]] && continue - [[ "${line}" =~ ^[[:space:]]*# ]] && continue - [[ "${line}" != *=* ]] && continue - - key="${line%%=*}" - value="${line#*=}" - key="${key#"${key%%[![:space:]]*}"}" - key="${key%"${key##*[![:space:]]}"}" - value="${value#"${value%%[![:space:]]*}"}" - - if [[ ${#value} -ge 2 ]]; then - first="${value:0:1}" - last="${value: -1}" - if [[ ("${first}" == '"' && "${last}" == '"') || ("${first}" == "'" && "${last}" == "'") ]]; then - value="${value:1:${#value}-2}" - fi - fi - - export "${key}=${value}" - done < "${ENV_FILE}" -fi +# 2) Load .env (optional) +# shellcheck source=scripts/lib/load_env.sh +source "${PROJECT_ROOT}/scripts/lib/load_env.sh" +load_env_file "${PROJECT_ROOT}/.env" echo "Environment activated (${ENV_KIND}): ${VIRTUAL_ENV:-${CONDA_DEFAULT_ENV:-unknown}}" diff --git a/api/translator_app.py b/api/translator_app.py index 1c710de..73858cb 100644 --- a/api/translator_app.py +++ b/api/translator_app.py @@ -187,7 +187,6 @@ async def startup_event(): logger.info(f"Translation service ready with default model: {default_model}") except Exception as e: logger.error(f"Failed to initialize translator: {e}", exc_info=True) - logger.warning("Service will start but translation may not work correctly") @app.get("/health") diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index 4933b40..0a331f1 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -255,7 +255,13 @@ curl -u 'saas:4hOaLaf41y2VuI8y' \ }' ``` -在项目根目录创建 `.env`(可复制 `.env.example` 后按环境修改): +在项目根目录创建 `.env`: + +```bash +./scripts/init_env.sh # 从 .env.example 复制(若 .env 不存在) +``` + +然后按环境修改 `.env`。**含特殊字符(#、$、&、空格)的密码需加引号**,例如: ```env # MySQL(AI 生产 10.200.16.14:3316,推荐 root) @@ -263,17 +269,17 @@ DB_HOST=10.200.16.14 DB_PORT=3316 DB_DATABASE=saas DB_USERNAME=root -DB_PASSWORD=qY8tgodLoA&KT#yQ +DB_PASSWORD="qY8tgodLoA&KT#yQ" # Elasticsearch ES_HOST=http://10.200.16.14:9200 ES_USERNAME=saas -ES_PASSWORD=4hOaLaf41y2VuI8y +ES_PASSWORD="4hOaLaf41y2VuI8y" # Redis(可选) REDIS_HOST=10.200.16.14 REDIS_PORT=6479 -REDIS_PASSWORD=dxEkegEZ@C5SXWKv +REDIS_PASSWORD="dxEkegEZ@C5SXWKv" # DeepL 翻译(按需) DEEPL_AUTH_KEY=your-key diff --git a/scripts/create_tenant_index.sh b/scripts/create_tenant_index.sh index cde2b4a..f10b0e4 100755 --- a/scripts/create_tenant_index.sh +++ b/scripts/create_tenant_index.sh @@ -5,13 +5,12 @@ # 切换到项目根目录 cd "$(dirname "$0")/.." +PROJECT_ROOT="$(pwd)" # 加载 .env 文件(如果存在) -if [ -f .env ]; then - set -a - source .env - set +a -fi +# shellcheck source=scripts/lib/load_env.sh +source "${PROJECT_ROOT}/scripts/lib/load_env.sh" +load_env_file "${PROJECT_ROOT}/.env" ES_HOST="${ES_HOST:-http://localhost:9200}" ES_USERNAME="${ES_USERNAME:-}" diff --git a/scripts/init_env.sh b/scripts/init_env.sh new file mode 100755 index 0000000..6b373cd --- /dev/null +++ b/scripts/init_env.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# +# Initialize .env from .env.example. +# Usage: +# ./scripts/init_env.sh # Copy .env.example → .env if missing +# ./scripts/init_env.sh --force # Overwrite .env with .env.example (backup to .env.bak) +# +# Production credentials: see docs/QUICKSTART.md §1.6 +# +set -euo pipefail + +PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "${PROJECT_ROOT}" + +ENV_FILE="${PROJECT_ROOT}/.env" +EXAMPLE_FILE="${PROJECT_ROOT}/.env.example" + +if [ ! -f "${EXAMPLE_FILE}" ]; then + echo "ERROR: .env.example not found at ${EXAMPLE_FILE}" >&2 + exit 1 +fi + +force="${1:-}" +if [[ "${force}" == "--force" ]]; then + if [ -f "${ENV_FILE}" ]; then + cp "${ENV_FILE}" "${ENV_FILE}.bak" + echo "[init_env] Backed up .env to .env.bak" + fi + cp "${EXAMPLE_FILE}" "${ENV_FILE}" + echo "[init_env] Created .env from .env.example" + exit 0 +fi + +if [ -f "${ENV_FILE}" ]; then + echo "[init_env] .env already exists, skipping" + exit 0 +fi + +cp "${EXAMPLE_FILE}" "${ENV_FILE}" +echo "[init_env] Created .env from .env.example" +echo "" +echo "Next: Edit .env with your credentials (see docs/QUICKSTART.md §1.6)" diff --git a/scripts/lib/load_env.sh b/scripts/lib/load_env.sh new file mode 100644 index 0000000..9f34130 --- /dev/null +++ b/scripts/lib/load_env.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# +# Shared .env loader. Uses standard .env format: +# KEY=value +# KEY="value with spaces" +# KEY='value' +# +# Values with special chars (#, $, spaces) should be quoted in .env. +# Usage: source scripts/lib/load_env.sh && load_env_file /path/to/.env +# +load_env_file() { + local env_file="${1:-}" + [[ -z "${env_file}" || ! -f "${env_file}" ]] && return 0 + + while IFS= read -r line || [[ -n "${line}" ]]; do + line="${line%$'\r'}" + [[ -z "${line//[[:space:]]/}" ]] && continue + [[ "${line}" =~ ^[[:space:]]*# ]] && continue + [[ "${line}" != *=* ]] && continue + + local key="${line%%=*}" + local value="${line#*=}" + key="${key#"${key%%[![:space:]]*}"}" + key="${key%"${key##*[![:space:]]}"}" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + + # Strip surrounding quotes (standard .env behavior) + if [[ ${#value} -ge 2 ]]; then + local first="${value:0:1}" + local last="${value: -1}" + if [[ ("${first}" == '"' && "${last}" == '"') || ("${first}" == "'" && "${last}" == "'") ]]; then + value="${value:1:${#value}-2}" + fi + fi + + # Safe export: %q produces shell-quoted string for values with spaces/special chars + eval "$(printf 'export %s=%q\n' "$key" "$value")" + done < "${env_file}" +} diff --git a/scripts/service_ctl.sh b/scripts/service_ctl.sh index 41c2645..ae629d5 100755 --- a/scripts/service_ctl.sh +++ b/scripts/service_ctl.sh @@ -11,6 +11,9 @@ LOG_DIR="${PROJECT_ROOT}/logs" mkdir -p "${LOG_DIR}" +# shellcheck source=scripts/lib/load_env.sh +source "${PROJECT_ROOT}/scripts/lib/load_env.sh" + CORE_SERVICES=("backend" "indexer" "frontend") OPTIONAL_SERVICES=("embedding" "translator" "reranker" "tei") LEGACY_SERVICES=("clip" "cnclip") @@ -19,36 +22,6 @@ all_services() { echo "${CORE_SERVICES[@]} ${OPTIONAL_SERVICES[@]} ${LEGACY_SERVICES[@]}" } -load_env_file() { - local env_file="${PROJECT_ROOT}/.env" - if [ ! -f "${env_file}" ]; then - return 0 - fi - - while IFS= read -r line || [ -n "${line}" ]; do - line="${line%$'\r'}" - [[ -z "${line//[[:space:]]/}" ]] && continue - [[ "${line}" =~ ^[[:space:]]*# ]] && continue - [[ "${line}" != *=* ]] && continue - - local key="${line%%=*}" - local value="${line#*=}" - key="${key#"${key%%[![:space:]]*}"}" - key="${key%"${key##*[![:space:]]}"}" - value="${value#"${value%%[![:space:]]*}"}" - - if [[ ${#value} -ge 2 ]]; then - local first="${value:0:1}" - local last="${value: -1}" - if [[ ("${first}" == '"' && "${last}" == '"') || ("${first}" == "'" && "${last}" == "'") ]]; then - value="${value:1:${#value}-2}" - fi - fi - - export "${key}=${value}" - done < "${env_file}" -} - get_port() { local service="$1" case "${service}" in @@ -387,7 +360,7 @@ main() { local action="$1" shift || true - load_env_file + load_env_file "${PROJECT_ROOT}/.env" local targets targets="$(resolve_targets "${action}" "$@")" if [ -z "${targets}" ]; then diff --git a/setup.sh b/setup.sh index 6f26507..6b9c88e 100755 --- a/setup.sh +++ b/setup.sh @@ -34,12 +34,7 @@ echo -e "\n${YELLOW}Current Python version:${NC}" python --version echo -e "\n${YELLOW}Step 2: Loading configuration${NC}" -# Check if .env exists -if [ ! -f ".env" ]; then - echo -e "${YELLOW}Creating .env from .env.example...${NC}" - cp .env.example .env - echo -e "${GREEN}.env file created. Please update it with your actual configuration.${NC}" -fi +./scripts/init_env.sh # Display configuration echo -e "${GREEN}Configuration loaded:${NC}" -- libgit2 0.21.2