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"
```
@@ -0,0 +1,41 @@ @@ -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
1 # Environment Configuration Template 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 # 生产/测试凭证与远程登录方式见 docs/QUICKSTART.md §1.6 4 # 生产/测试凭证与远程登录方式见 docs/QUICKSTART.md §1.6
  5 +# Note: Values with special chars (#, $, spaces) should be quoted, e.g. PASSWORD="my#pass"
4 6
5 # Elasticsearch (生产默认 10.200.16.14:9200,本地可用 localhost) 7 # Elasticsearch (生产默认 10.200.16.14:9200,本地可用 localhost)
6 ES_HOST=http://localhost:9200 8 ES_HOST=http://localhost:9200
@@ -22,6 +22,7 @@ README 用于给åŽç»­å¼€å‘者建立统一认知:**ç³»ç»Ÿæ¡†æž¶ã€æ¨¡å—è¾¹ç• @@ -22,6 +22,7 @@ README 用于给åŽç»­å¼€å‘者建立统一认知:**ç³»ç»Ÿæ¡†æž¶ã€æ¨¡å—è¾¹ç•
22 ```bash 22 ```bash
23 # 首次创建环境(默认基础ä¾èµ–) 23 # 首次创建环境(默认基础ä¾èµ–)
24 ./scripts/create_venv.sh 24 ./scripts/create_venv.sh
  25 +./scripts/init_env.sh # 从 .env.example ç”Ÿæˆæœ¬åœ° .env(若ä¸å­˜åœ¨ï¼‰
25 source activate.sh 26 source activate.sh
26 27
27 # å¯åŠ¨æ ¸å¿ƒæœåŠ¡ï¼ˆbackend/indexer/frontend) 28 # å¯åŠ¨æ ¸å¿ƒæœåŠ¡ï¼ˆbackend/indexer/frontend)
@@ -67,6 +68,7 @@ START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh @@ -67,6 +68,7 @@ START_EMBEDDING=1 START_TRANSLATOR=1 START_RERANKER=1 ./run.sh
67 - **å•一é…ç½®æº**:æœåŠ¡åœ°å€ã€provider 选择ã€åŽç«¯å‚数统一在 `config/config.yaml`,环境å˜é‡ä»…åšè¦†ç›–。 68 - **å•一é…ç½®æº**:æœåŠ¡åœ°å€ã€provider 选择ã€åŽç«¯å‚数统一在 `config/config.yaml`,环境å˜é‡ä»…åšè¦†ç›–。
68 - **接å£å¥‘约优先**:外部 API 契约与 provider å¥‘çº¦ç¨³å®šï¼Œå†…éƒ¨é‡æž„ä¸å½±å“调用方。 69 - **接å£å¥‘约优先**:外部 API 契约与 provider å¥‘çº¦ç¨³å®šï¼Œå†…éƒ¨é‡æž„ä¸å½±å“调用方。
69 - **扩展走工厂**:新增 provider/backend å¿…é¡»åœ¨å·¥åŽ‚å‡½æ•°ä¸­æ˜¾å¼æ³¨å†Œï¼Œç¦æ­¢æ—路分支。 70 - **扩展走工厂**:新增 provider/backend å¿…é¡»åœ¨å·¥åŽ‚å‡½æ•°ä¸­æ˜¾å¼æ³¨å†Œï¼Œç¦æ­¢æ—路分支。
  71 +- **简æ´å’Œè§„范优先**:对待异常情况,分情况而定,如果是系统å¯åŠ¨æœŸé—´ã€åŠ è½½èµ„æº/é…置等出错,å¯ä»¥å°½æ—©æš´éœ²é—®é¢˜ï¼Œä¸è¦fallback走一套跟é…ç½®ä¸ç¬¦åˆçš„å¦å¤–一套机制,å³å°½é‡ä¿è¯é€»è¾‘清晰ã€ç®€æ´ï¼›ä½†æ˜¯çº¿ä¸Šçš„æœåŠ¡å¦‚æžœé‡åˆ°å¼‚常情况å¯ä»¥æ ¹æ®æœ€ä½³å®žè·µé€‰å–应对方法,比如记录错误日志并且ä¿è¯æœåŠ¡æ­£å¸¸è¿è¡Œã€‚规范优先,尽é‡ç”¨ä¸€å¥—æœºåˆ¶æ»¡è¶³éœ€æ±‚ï¼Œè€Œä¸æ˜¯å…¼å®¹å„ç§ä¸åˆç†çš„æƒ…况,对于ä¸åˆç†çš„内容优先清ç†ã€é‡æž„ï¼Œè€Œä¸æ˜¯å…¼å®¹ï¼Œä»¥ä¿è¯ä»£ç æ˜¯ç®€æ´çš„。
70 - **å¯è§‚测性优先**:å¥åº·æ£€æŸ¥ã€å…³é”®æ—¥å¿—ã€è¯·æ±‚上下文必须å¯è¿½è¸ªã€‚ 72 - **å¯è§‚测性优先**:å¥åº·æ£€æŸ¥ã€å…³é”®æ—¥å¿—ã€è¯·æ±‚上下文必须å¯è¿½è¸ªã€‚
71 - **测试优先ä¿éšœå¥‘约**:CI 首先ä¿è¯æŽ¥å£å¥‘约和核心路径å¯ç”¨ï¼Œå†é€æ­¥æ‰©å±•性能与业务测试。 73 - **测试优先ä¿éšœå¥‘约**:CI 首先ä¿è¯æŽ¥å£å¥‘约和核心路径å¯ç”¨ï¼Œå†é€æ­¥æ‰©å±•性能与业务测试。
72 74
@@ -27,31 +27,9 @@ else @@ -27,31 +27,9 @@ else
27 return 1 27 return 1
28 fi 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 echo "Environment activated (${ENV_KIND}): ${VIRTUAL_ENV:-${CONDA_DEFAULT_ENV:-unknown}}" 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,7 +187,6 @@ async def startup_event():
187 logger.info(f"Translation service ready with default model: {default_model}") 187 logger.info(f"Translation service ready with default model: {default_model}")
188 except Exception as e: 188 except Exception as e:
189 logger.error(f"Failed to initialize translator: {e}", exc_info=True) 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 @app.get("/health") 192 @app.get("/health")
docs/QUICKSTART.md
@@ -255,7 +255,13 @@ curl -u &#39;saas:4hOaLaf41y2VuI8y&#39; \ @@ -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 ```env 266 ```env
261 # MySQL(AI 生产 10.200.16.14:3316,推荐 root) 267 # MySQL(AI 生产 10.200.16.14:3316,推荐 root)
@@ -263,17 +269,17 @@ DB_HOST=10.200.16.14 @@ -263,17 +269,17 @@ DB_HOST=10.200.16.14
263 DB_PORT=3316 269 DB_PORT=3316
264 DB_DATABASE=saas 270 DB_DATABASE=saas
265 DB_USERNAME=root 271 DB_USERNAME=root
266 -DB_PASSWORD=qY8tgodLoA&KT#yQ 272 +DB_PASSWORD="qY8tgodLoA&KT#yQ"
267 273
268 # Elasticsearch 274 # Elasticsearch
269 ES_HOST=http://10.200.16.14:9200 275 ES_HOST=http://10.200.16.14:9200
270 ES_USERNAME=saas 276 ES_USERNAME=saas
271 -ES_PASSWORD=4hOaLaf41y2VuI8y 277 +ES_PASSWORD="4hOaLaf41y2VuI8y"
272 278
273 # Redis(可选) 279 # Redis(可选)
274 REDIS_HOST=10.200.16.14 280 REDIS_HOST=10.200.16.14
275 REDIS_PORT=6479 281 REDIS_PORT=6479
276 -REDIS_PASSWORD=dxEkegEZ@C5SXWKv 282 +REDIS_PASSWORD="dxEkegEZ@C5SXWKv"
277 283
278 # DeepL 翻译(按需) 284 # DeepL 翻译(按需)
279 DEEPL_AUTH_KEY=your-key 285 DEEPL_AUTH_KEY=your-key
scripts/create_tenant_index.sh
@@ -5,13 +5,12 @@ @@ -5,13 +5,12 @@
5 5
6 # 切换到项目根目录 6 # 切换到项目根目录
7 cd "$(dirname "$0")/.." 7 cd "$(dirname "$0")/.."
  8 +PROJECT_ROOT="$(pwd)"
8 9
9 # 加载 .env 文件(如果存在) 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 ES_HOST="${ES_HOST:-http://localhost:9200}" 15 ES_HOST="${ES_HOST:-http://localhost:9200}"
17 ES_USERNAME="${ES_USERNAME:-}" 16 ES_USERNAME="${ES_USERNAME:-}"
scripts/init_env.sh 0 → 100755
@@ -0,0 +1,42 @@ @@ -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 @@ @@ -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,6 +11,9 @@ LOG_DIR=&quot;${PROJECT_ROOT}/logs&quot;
11 11
12 mkdir -p "${LOG_DIR}" 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 CORE_SERVICES=("backend" "indexer" "frontend") 17 CORE_SERVICES=("backend" "indexer" "frontend")
15 OPTIONAL_SERVICES=("embedding" "translator" "reranker" "tei") 18 OPTIONAL_SERVICES=("embedding" "translator" "reranker" "tei")
16 LEGACY_SERVICES=("clip" "cnclip") 19 LEGACY_SERVICES=("clip" "cnclip")
@@ -19,36 +22,6 @@ all_services() { @@ -19,36 +22,6 @@ all_services() {
19 echo "${CORE_SERVICES[@]} ${OPTIONAL_SERVICES[@]} ${LEGACY_SERVICES[@]}" 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 get_port() { 25 get_port() {
53 local service="$1" 26 local service="$1"
54 case "${service}" in 27 case "${service}" in
@@ -387,7 +360,7 @@ main() { @@ -387,7 +360,7 @@ main() {
387 local action="$1" 360 local action="$1"
388 shift || true 361 shift || true
389 362
390 - load_env_file 363 + load_env_file "${PROJECT_ROOT}/.env"
391 local targets 364 local targets
392 targets="$(resolve_targets "${action}" "$@")" 365 targets="$(resolve_targets "${action}" "$@")"
393 if [ -z "${targets}" ]; then 366 if [ -z "${targets}" ]; then
@@ -34,12 +34,7 @@ echo -e &quot;\n${YELLOW}Current Python version:${NC}&quot; @@ -34,12 +34,7 @@ echo -e &quot;\n${YELLOW}Current Python version:${NC}&quot;
34 python --version 34 python --version
35 35
36 echo -e "\n${YELLOW}Step 2: Loading configuration${NC}" 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 # Display configuration 39 # Display configuration
45 echo -e "${GREEN}Configuration loaded:${NC}" 40 echo -e "${GREEN}Configuration loaded:${NC}"