Commit c7e80cc2cd7c4e59b2fc47b06ddbe984e6b0d8e8

Authored by tangwang
1 parent 80955935

新的 .env 管理机制如下:

1. 新增 `scripts/init_env.sh`
- 若 `.env` 不存在,从 `.env.example` 复制生成
- 支持 `--force`:覆盖 `.env` 并备份为 `.env.bak`
- 首次搭建时统一执行:`./scripts/init_env.sh`

 2. 统一加载逻辑 `scripts/lib/load_env.sh`
- 移除 `activate.sh` 和 `service_ctl.sh` 中的重复解析逻辑
- 使用共享的 `load_env_file`,并改为 `eval "$(printf 'export %s=%q\n'
  "$key" "$value")"` 安全导出
- 支持含 ``、`$`、空格等特殊字符的值(需在 `.env` 中用引号包裹)

 3. 使用方式
- **activate.sh**:`source scripts/lib/load_env.sh` 后调用
  `load_env_file`
- **service_ctl.sh**:同上,去掉内联的 `load_env_file` 实现
- **create_tenant_index.sh**:改为使用共享 loader,不再用 `set -a;
  source .env`

 4. 文档更新
- **README.md**:在快速开始中加入 `./scripts/init_env.sh`
- **docs/QUICKSTART.md**:说明 `init_env.sh`
  用法,并强调含特殊字符的密码需加引号
- **.env.example**:补充注释说明引号规则

 5. setup.sh
- 用 `./scripts/init_env.sh` 替代原先的 `cp .env.example .env`

---

**推荐流程**:
```bash
./scripts/create_venv.sh
./scripts/init_env.sh     从 .env.example 生成本地 .env
source activate.sh
./run.sh
```

**密码写法**:若密码包含 ``、`$`、`&`、空格等,需加引号,例如:
```env
DB_PASSWORD="qY8tgodLoA&KTyQ"
ES_PASSWORD="4hOaLaf41y2VuI8y"
```
.env 0 → 100644
... ... @@ -0,0 +1,41 @@
  1 +# Elasticsearch Configuration
  2 +ES_HOST=http://localhost:9200
  3 +ES_USERNAME=saas
  4 +ES_PASSWORD=4hOaLaf41y2VuI8y
  5 +
  6 +# Redis Configuration (Optional) - AI 生产 10.200.16.14:6479
  7 +REDIS_HOST=10.200.16.14
  8 +REDIS_PORT=6479
  9 +REDIS_PASSWORD=dxEkegEZ@C5SXWKv
  10 +
  11 +# DeepL Translation API
  12 +DEEPL_AUTH_KEY=c9293ab4-ad25-479b-919f-ab4e63b429ed
  13 +
  14 +# API Service Configuration
  15 +API_HOST=0.0.0.0
  16 +API_PORT=6002
  17 +
  18 +# MySQL Database Configuration (Shoplazza) - AI 生产 10.200.16.14:3316
  19 +DB_HOST=10.200.16.14
  20 +DB_PORT=3316
  21 +DB_DATABASE=saas
  22 +DB_USERNAME=root
  23 +DB_PASSWORD=qY8tgodLoA&KT#yQ
  24 +
  25 +# Model Directories
  26 +TEXT_MODEL_DIR=/data/tw/models/bge-m3 # 已经改为web请求了,不使用本地模型
  27 +IMAGE_MODEL_DIR=/data/tw/models/cn-clip # 已经改为web请求了,不使用本地模型
  28 +
  29 +# Cache Directory
  30 +CACHE_DIR=.cache
  31 +
  32 +# Frontend API Base URL
  33 +API_BASE_URL=http://43.166.252.75:6002
  34 +
  35 +
  36 +# 国内
  37 +DASHSCOPE_API_KEY=sk-c3b8d4db061840aa8effb748df2a997b
  38 +# 美国
  39 +DASHSCOPE_API_KEY=sk-482cc3ff37a8467dab134a7a46830556
  40 +
  41 +OPENAI_API_KEY=sk-HvmTMKtuznibZ75l7L2uF2jiaYocCthqd8Cbdkl09KTE7Ft0
... ...
.env.example
1 1 # Environment Configuration Template
2   -# Copy this file to .env and update with your actual values.
  2 +# Run: ./scripts/init_env.sh (copies this file to .env if missing)
  3 +# Then edit .env with your actual values.
3 4 # 生产/测试凭证与远程登录方式见 docs/QUICKSTART.md §1.6
  5 +# Note: Values with special chars (#, $, spaces) should be quoted, e.g. PASSWORD="my#pass"
4 6  
5 7 # Elasticsearch (生产默认 10.200.16.14:9200,本地可用 localhost)
6 8 ES_HOST=http://localhost:9200
... ...
README.md
... ... @@ -22,6 +22,7 @@ README 用于给åŽç»­å¼€å‘者建立统一认知:**ç³»ç»Ÿæ¡†æž¶ã€æ¨¡å—è¾¹ç•
22 22 ```bash
23 23 # 首次创建环境(默认基础ä¾èµ–)
24 24 ./scripts/create_venv.sh
  25 +./scripts/init_env.sh # 从 .env.example ç”Ÿæˆæœ¬åœ° .env(若ä¸å­˜åœ¨ï¼‰
25 26 source activate.sh
26 27  
27 28 # å¯åŠ¨æ ¸å¿ƒæœåŠ¡ï¼ˆbackend/indexer/frontend)
... ... @@ -67,6 +68,7 @@ START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh
67 68 - **å•一é…ç½®æº**:æœåŠ¡åœ°å€ã€provider 选择ã€åŽç«¯å‚数统一在 `config/config.yaml`,环境å˜é‡ä»…åšè¦†ç›–。
68 69 - **接å£å¥‘约优先**:外部 API 契约与 provider å¥‘çº¦ç¨³å®šï¼Œå†…éƒ¨é‡æž„ä¸å½±å“调用方。
69 70 - **扩展走工厂**:新增 provider/backend å¿…é¡»åœ¨å·¥åŽ‚å‡½æ•°ä¸­æ˜¾å¼æ³¨å†Œï¼Œç¦æ­¢æ—路分支。
  71 +- **简æ´å’Œè§„范优先**:对待异常情况,分情况而定,如果是系统å¯åŠ¨æœŸé—´ã€åŠ è½½èµ„æº/é…置等出错,å¯ä»¥å°½æ—©æš´éœ²é—®é¢˜ï¼Œä¸è¦fallback走一套跟é…ç½®ä¸ç¬¦åˆçš„å¦å¤–一套机制,å³å°½é‡ä¿è¯é€»è¾‘清晰ã€ç®€æ´ï¼›ä½†æ˜¯çº¿ä¸Šçš„æœåŠ¡å¦‚æžœé‡åˆ°å¼‚常情况å¯ä»¥æ ¹æ®æœ€ä½³å®žè·µé€‰å–应对方法,比如记录错误日志并且ä¿è¯æœåŠ¡æ­£å¸¸è¿è¡Œã€‚规范优先,尽é‡ç”¨ä¸€å¥—æœºåˆ¶æ»¡è¶³éœ€æ±‚ï¼Œè€Œä¸æ˜¯å…¼å®¹å„ç§ä¸åˆç†çš„æƒ…况,对于ä¸åˆç†çš„内容优先清ç†ã€é‡æž„ï¼Œè€Œä¸æ˜¯å…¼å®¹ï¼Œä»¥ä¿è¯ä»£ç æ˜¯ç®€æ´çš„。
70 72 - **å¯è§‚测性优先**:å¥åº·æ£€æŸ¥ã€å…³é”®æ—¥å¿—ã€è¯·æ±‚上下文必须å¯è¿½è¸ªã€‚
71 73 - **测试优先ä¿éšœå¥‘约**:CI 首先ä¿è¯æŽ¥å£å¥‘约和核心路径å¯ç”¨ï¼Œå†é€æ­¥æ‰©å±•性能与业务测试。
72 74  
... ...
activate.sh
... ... @@ -27,31 +27,9 @@ else
27 27 return 1
28 28 fi
29 29  
30   -# 如果需要加载 .env 中的环境变量
31   -ENV_FILE="${PROJECT_ROOT}/.env"
32   -if [ -f "${ENV_FILE}" ]; then
33   - while IFS= read -r line || [ -n "${line}" ]; do
34   - line="${line%$'\r'}"
35   - [[ -z "${line//[[:space:]]/}" ]] && continue
36   - [[ "${line}" =~ ^[[:space:]]*# ]] && continue
37   - [[ "${line}" != *=* ]] && continue
38   -
39   - key="${line%%=*}"
40   - value="${line#*=}"
41   - key="${key#"${key%%[![:space:]]*}"}"
42   - key="${key%"${key##*[![:space:]]}"}"
43   - value="${value#"${value%%[![:space:]]*}"}"
44   -
45   - if [[ ${#value} -ge 2 ]]; then
46   - first="${value:0:1}"
47   - last="${value: -1}"
48   - if [[ ("${first}" == '"' && "${last}" == '"') || ("${first}" == "'" && "${last}" == "'") ]]; then
49   - value="${value:1:${#value}-2}"
50   - fi
51   - fi
52   -
53   - export "${key}=${value}"
54   - done < "${ENV_FILE}"
55   -fi
  30 +# 2) Load .env (optional)
  31 +# shellcheck source=scripts/lib/load_env.sh
  32 +source "${PROJECT_ROOT}/scripts/lib/load_env.sh"
  33 +load_env_file "${PROJECT_ROOT}/.env"
56 34  
57 35 echo "Environment activated (${ENV_KIND}): ${VIRTUAL_ENV:-${CONDA_DEFAULT_ENV:-unknown}}"
... ...
api/translator_app.py
... ... @@ -187,7 +187,6 @@ async def startup_event():
187 187 logger.info(f"Translation service ready with default model: {default_model}")
188 188 except Exception as e:
189 189 logger.error(f"Failed to initialize translator: {e}", exc_info=True)
190   - logger.warning("Service will start but translation may not work correctly")
191 190  
192 191  
193 192 @app.get("/health")
... ...
docs/QUICKSTART.md
... ... @@ -255,7 +255,13 @@ curl -u &#39;saas:4hOaLaf41y2VuI8y&#39; \
255 255 }'
256 256 ```
257 257  
258   -在项目根目录创建 `.env`(可复制 `.env.example` 后按环境修改):
  258 +在项目根目录创建 `.env`:
  259 +
  260 +```bash
  261 +./scripts/init_env.sh # 从 .env.example 复制(若 .env 不存在)
  262 +```
  263 +
  264 +然后按环境修改 `.env`。**含特殊字符(#、$、&、空格)的密码需加引号**,例如:
259 265  
260 266 ```env
261 267 # MySQL(AI 生产 10.200.16.14:3316,推荐 root)
... ... @@ -263,17 +269,17 @@ DB_HOST=10.200.16.14
263 269 DB_PORT=3316
264 270 DB_DATABASE=saas
265 271 DB_USERNAME=root
266   -DB_PASSWORD=qY8tgodLoA&KT#yQ
  272 +DB_PASSWORD="qY8tgodLoA&KT#yQ"
267 273  
268 274 # Elasticsearch
269 275 ES_HOST=http://10.200.16.14:9200
270 276 ES_USERNAME=saas
271   -ES_PASSWORD=4hOaLaf41y2VuI8y
  277 +ES_PASSWORD="4hOaLaf41y2VuI8y"
272 278  
273 279 # Redis(可选)
274 280 REDIS_HOST=10.200.16.14
275 281 REDIS_PORT=6479
276   -REDIS_PASSWORD=dxEkegEZ@C5SXWKv
  282 +REDIS_PASSWORD="dxEkegEZ@C5SXWKv"
277 283  
278 284 # DeepL 翻译(按需)
279 285 DEEPL_AUTH_KEY=your-key
... ...
scripts/create_tenant_index.sh
... ... @@ -5,13 +5,12 @@
5 5  
6 6 # 切换到项目根目录
7 7 cd "$(dirname "$0")/.."
  8 +PROJECT_ROOT="$(pwd)"
8 9  
9 10 # 加载 .env 文件(如果存在)
10   -if [ -f .env ]; then
11   - set -a
12   - source .env
13   - set +a
14   -fi
  11 +# shellcheck source=scripts/lib/load_env.sh
  12 +source "${PROJECT_ROOT}/scripts/lib/load_env.sh"
  13 +load_env_file "${PROJECT_ROOT}/.env"
15 14  
16 15 ES_HOST="${ES_HOST:-http://localhost:9200}"
17 16 ES_USERNAME="${ES_USERNAME:-}"
... ...
scripts/init_env.sh 0 → 100755
... ... @@ -0,0 +1,42 @@
  1 +#!/bin/bash
  2 +#
  3 +# Initialize .env from .env.example.
  4 +# Usage:
  5 +# ./scripts/init_env.sh # Copy .env.example → .env if missing
  6 +# ./scripts/init_env.sh --force # Overwrite .env with .env.example (backup to .env.bak)
  7 +#
  8 +# Production credentials: see docs/QUICKSTART.md §1.6
  9 +#
  10 +set -euo pipefail
  11 +
  12 +PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
  13 +cd "${PROJECT_ROOT}"
  14 +
  15 +ENV_FILE="${PROJECT_ROOT}/.env"
  16 +EXAMPLE_FILE="${PROJECT_ROOT}/.env.example"
  17 +
  18 +if [ ! -f "${EXAMPLE_FILE}" ]; then
  19 + echo "ERROR: .env.example not found at ${EXAMPLE_FILE}" >&2
  20 + exit 1
  21 +fi
  22 +
  23 +force="${1:-}"
  24 +if [[ "${force}" == "--force" ]]; then
  25 + if [ -f "${ENV_FILE}" ]; then
  26 + cp "${ENV_FILE}" "${ENV_FILE}.bak"
  27 + echo "[init_env] Backed up .env to .env.bak"
  28 + fi
  29 + cp "${EXAMPLE_FILE}" "${ENV_FILE}"
  30 + echo "[init_env] Created .env from .env.example"
  31 + exit 0
  32 +fi
  33 +
  34 +if [ -f "${ENV_FILE}" ]; then
  35 + echo "[init_env] .env already exists, skipping"
  36 + exit 0
  37 +fi
  38 +
  39 +cp "${EXAMPLE_FILE}" "${ENV_FILE}"
  40 +echo "[init_env] Created .env from .env.example"
  41 +echo ""
  42 +echo "Next: Edit .env with your credentials (see docs/QUICKSTART.md §1.6)"
... ...
scripts/lib/load_env.sh 0 → 100644
... ... @@ -0,0 +1,40 @@
  1 +#!/bin/bash
  2 +#
  3 +# Shared .env loader. Uses standard .env format:
  4 +# KEY=value
  5 +# KEY="value with spaces"
  6 +# KEY='value'
  7 +#
  8 +# Values with special chars (#, $, spaces) should be quoted in .env.
  9 +# Usage: source scripts/lib/load_env.sh && load_env_file /path/to/.env
  10 +#
  11 +load_env_file() {
  12 + local env_file="${1:-}"
  13 + [[ -z "${env_file}" || ! -f "${env_file}" ]] && return 0
  14 +
  15 + while IFS= read -r line || [[ -n "${line}" ]]; do
  16 + line="${line%$'\r'}"
  17 + [[ -z "${line//[[:space:]]/}" ]] && continue
  18 + [[ "${line}" =~ ^[[:space:]]*# ]] && continue
  19 + [[ "${line}" != *=* ]] && continue
  20 +
  21 + local key="${line%%=*}"
  22 + local value="${line#*=}"
  23 + key="${key#"${key%%[![:space:]]*}"}"
  24 + key="${key%"${key##*[![:space:]]}"}"
  25 + value="${value#"${value%%[![:space:]]*}"}"
  26 + value="${value%"${value##*[![:space:]]}"}"
  27 +
  28 + # Strip surrounding quotes (standard .env behavior)
  29 + if [[ ${#value} -ge 2 ]]; then
  30 + local first="${value:0:1}"
  31 + local last="${value: -1}"
  32 + if [[ ("${first}" == '"' && "${last}" == '"') || ("${first}" == "'" && "${last}" == "'") ]]; then
  33 + value="${value:1:${#value}-2}"
  34 + fi
  35 + fi
  36 +
  37 + # Safe export: %q produces shell-quoted string for values with spaces/special chars
  38 + eval "$(printf 'export %s=%q\n' "$key" "$value")"
  39 + done < "${env_file}"
  40 +}
... ...
scripts/service_ctl.sh
... ... @@ -11,6 +11,9 @@ LOG_DIR=&quot;${PROJECT_ROOT}/logs&quot;
11 11  
12 12 mkdir -p "${LOG_DIR}"
13 13  
  14 +# shellcheck source=scripts/lib/load_env.sh
  15 +source "${PROJECT_ROOT}/scripts/lib/load_env.sh"
  16 +
14 17 CORE_SERVICES=("backend" "indexer" "frontend")
15 18 OPTIONAL_SERVICES=("embedding" "translator" "reranker" "tei")
16 19 LEGACY_SERVICES=("clip" "cnclip")
... ... @@ -19,36 +22,6 @@ all_services() {
19 22 echo "${CORE_SERVICES[@]} ${OPTIONAL_SERVICES[@]} ${LEGACY_SERVICES[@]}"
20 23 }
21 24  
22   -load_env_file() {
23   - local env_file="${PROJECT_ROOT}/.env"
24   - if [ ! -f "${env_file}" ]; then
25   - return 0
26   - fi
27   -
28   - while IFS= read -r line || [ -n "${line}" ]; do
29   - line="${line%$'\r'}"
30   - [[ -z "${line//[[:space:]]/}" ]] && continue
31   - [[ "${line}" =~ ^[[:space:]]*# ]] && continue
32   - [[ "${line}" != *=* ]] && continue
33   -
34   - local key="${line%%=*}"
35   - local value="${line#*=}"
36   - key="${key#"${key%%[![:space:]]*}"}"
37   - key="${key%"${key##*[![:space:]]}"}"
38   - value="${value#"${value%%[![:space:]]*}"}"
39   -
40   - if [[ ${#value} -ge 2 ]]; then
41   - local first="${value:0:1}"
42   - local last="${value: -1}"
43   - if [[ ("${first}" == '"' && "${last}" == '"') || ("${first}" == "'" && "${last}" == "'") ]]; then
44   - value="${value:1:${#value}-2}"
45   - fi
46   - fi
47   -
48   - export "${key}=${value}"
49   - done < "${env_file}"
50   -}
51   -
52 25 get_port() {
53 26 local service="$1"
54 27 case "${service}" in
... ... @@ -387,7 +360,7 @@ main() {
387 360 local action="$1"
388 361 shift || true
389 362  
390   - load_env_file
  363 + load_env_file "${PROJECT_ROOT}/.env"
391 364 local targets
392 365 targets="$(resolve_targets "${action}" "$@")"
393 366 if [ -z "${targets}" ]; then
... ...
setup.sh
... ... @@ -34,12 +34,7 @@ echo -e &quot;\n${YELLOW}Current Python version:${NC}&quot;
34 34 python --version
35 35  
36 36 echo -e "\n${YELLOW}Step 2: Loading configuration${NC}"
37   -# Check if .env exists
38   -if [ ! -f ".env" ]; then
39   - echo -e "${YELLOW}Creating .env from .env.example...${NC}"
40   - cp .env.example .env
41   - echo -e "${GREEN}.env file created. Please update it with your actual configuration.${NC}"
42   -fi
  37 +./scripts/init_env.sh
43 38  
44 39 # Display configuration
45 40 echo -e "${GREEN}Configuration loaded:${NC}"
... ...