---
title: FR-063.1g config 三層重整 — 舊設定 → 新環境設定對照表
---

# FR-063.1g config 三層重整 — 遷移對照表

> 產出於 2026-08-14（CM-1200）。用途：把六套環境 class 塌成一套之後，各部署點的
> `.env` 該怎麼補。**三環境 `.env` 實際更新等部署放行（環境鐵律），本檔只提供對照。**

## 0. 最重要的前提：現在完全不用改任何 `.env`

第 2 刀刻意做了**向後相容**——舊 JSON 包裝格式（`DB_SECRET` / `REDIS_SECRET` /
`JWT_SECRET` / `TURNSTILE_SECRET_KEY`）照舊可用且**優先**。三環境現有 `.env` 一個字
都不用動就能起服務，行為與改版前逐項相同（驗證方式見 §4）。

本表描述的是「想改用新格式時怎麼填」與「上版時建議補哪些項」。

## 1. 盤點事實：六套 class 其實只有一套在跑

SSH 唯讀查證（2026-08-14）三個部署點的 `ENV` 值：

| 環境 | 主機 | `.env` 的 `ENV` | 實際生效的 class |
|---|---|---|---|
| DEV | 本機 | `DEVELOP_PREMISE` | `DevelopmentPremiseConfig` |
| STG | 192.168.50.188 `/opt/compliance-manager-be/.env` | `DEVELOP_PREMISE` | `DevelopmentPremiseConfig` |
| POC | 192.168.50.189 `/opt/compliance-manager-be/.env` | `DEVELOP_PREMISE` | `DevelopmentPremiseConfig` |

**六套裡另外五套從未被任何部署點使用。** 因此新的單一 `Config` 以
`DevelopmentPremiseConfig` 的實效值為基準，這樣「同一份 `.env` 行為零差異」才成立。

## 2. 五套非 Development class 的差異值去哪了

程式化比對（每一套相對 `DevelopmentPremiseConfig`）的差異只有 12 個 key，五套完全相同：

| 原差異 key | 舊值（五套） | 去向 |
|---|---|---|
| `CORS_ALLOWED_ORIGINS` | `[WEBSITE_URL]` | → env `CORS_ALLOWED_ORIGINS`（逗號分隔，未設定 = `*`） |
| `DB_PORT` | `5432` | → env `DB_PORT`（未設定 = `5432`） |
| `DB_NAME` | `audit_manager` | → env `DB_NAME`（未設定 = `compliance_manager`；三環境本就都有設） |
| `LOGGING_LOCATION` | `/var/log/nics/api.log` | → env `LOG_DIR`（未設定 = `./log`，檔名固定 `api.log`） |
| `REDIS_SSL` | `True` | → env `REDIS_SSL`（未設定 = `false`） |
| `JWT_ACCESS_TOKEN_EXPIRES` | `30000` | → DB `system_configs` RUNTIME_CONFIG（第 3 刀） |
| `JWT_REFRESH_TOKEN_EXPIRES` | `72000` | → DB `system_configs` RUNTIME_CONFIG（第 3 刀） |
| `WEBSITE_URL` | `https://192.168.50.189/` | **死項刪**（唯一用途是餵 CORS，已 inline） |
| `SQLALCHEMY_DATABASE_URI` / `_BINDS` / `_POOL_RECYCLE` / `_TRACK_MODIFICATIONS` | — | **死項刪**（未裝 flask_sqlalchemy、app_factory 自組 URI，全無消費者） |
| `SCHEDULER_API_ENABLED` | `False` | **死項刪**（裝的是純 apscheduler 非 flask-apscheduler） |

## 3. 新舊格式對照（機密設定）

| 舊格式（仍支援，且優先） | 新平鋪格式 |
|---|---|
| `DB_SECRET={"rds_master_username": "u", "rds_master_password": "p"}` | `DB_USER=u`<br>`DB_PASSWORD=p` |
| `REDIS_SECRET={"redis_user_name": "u", "redis_user_password": "p"}` | `REDIS_USER=u`<br>`REDIS_PASSWORD=p` |
| `JWT_SECRET={"jwt_secret": "s"}` | `JWT_SECRET_KEY=s` |
| `TURNSTILE_SECRET_KEY={"turnstile_secret": "s"}` | `TURNSTILE_SECRET_KEY=s`（直接填字串） |

必填項（缺任一啟動即擋，訊息一次列全）：`DB_HOST`、`DB_USER`、`DB_PASSWORD`、
`JWT_SECRET_KEY`、`REDIS_HOST`。

## 4. 各環境上版時建議補的項目

三環境現有 `.env` 都**沒有**下列項目，補上才能表達原本靠 class 硬編的意圖：

| 環境 | 建議補 | 理由 |
|---|---|---|
| STG / POC | `CORS_ALLOWED_ORIGINS=<實際站台網址>` | 現況為 `*`（Development class 的值）。**注意這不是本次造成的**——因為 `ENV=DEVELOP_PREMISE`，STG/POC 一直都是 `*`，非本案迴歸。想收斂就趁上版補。 |
| 全部 | `LICENSE_ENFORCEMENT_ENABLED` / `LICENSE_READONLY_GATE_ENABLED` | 現靠程式預設隱式全開，曾是 DEV 踩雷點。顯式寫出。 |
| 全部（選用） | `LOG_DIR` | 不設 = `./log`，與現況相同。 |

`.env` 死變數（已從 DEV `.env` 移除，STG/POC 上版時可一併刪）：
`REDIS_PORT`（port 硬寫 6379）、`UPLOAD_DIR` / `UPLOAD_FILE_DIR` / `UPLOAD_STATIC_DIR`
（grep 全 repo + jedi-* 套件，非測試碼零消費者）。

## 5. 第三層：DB 營運參數

`public.system_configs`（ROOT tenant=1，`group='RUNTIME_CONFIG'`）七個鍵，由
`scripts/sql/2026-08-14-fr063-1g-runtime-config-seed.sql` seed（**目前只套 DEV**）：

`LOGIN_MAX_LOCK_COUNT` / `LOGIN_USER_LOCK_TIME` / `LOGIN_INACTIVITY_THRESHOLD_DAYS` /
`CHANGE_PASSWORD_THRESHOLD_DAYS` / `JWT_ACCESS_TOKEN_EXPIRES` /
`JWT_REFRESH_TOKEN_EXPIRES` / `MFA_REQUIRED`

生效時機分兩類：

- **登入政策四項＋MFA**：每次登入即時讀 → 改 DB **立即生效**，不需重啟。
- **JWT 兩項效期**：由 `flask_jwt_extended` 讀 `app.config`，啟動時套用 → 改 DB **需重啟 BE**。

DB 沒有該列 / DB 不可用 → 自動落回程式內建預設（值與改版前相同），不影響啟動。

## 6. STG / POC 上版注意事項 checklist

> 給未來實際執行上版的人照著勾。**全部動作都要等決策者放行才做（環境鐵律）**；
> 兩環境的 BE 部署點都是 `/opt/compliance-manager-be/`（`.env` 同目錄）。
> 標記：**必做**＝上版當次要完成；**建議**＝趁上版一併收斂；**optional**＝可不動。

### 上版前（可先準備，不動環境）

- [ ] **必做**：確認要部署的版包含本次 config 改版（第 1–3 刀 + 死項清理，commit `5acedd9b` 之後）。
- [ ] **建議**：預先擬好兩環境 `.env` 的增補 diff（見下），上版視窗內只做貼上與重啟。

### 上版時 — `.env` 增補（每環境各做一次）

- [ ] **必做**：`CORS_ALLOWED_ORIGINS` 補實際站台網址。
  - 現況是 `*` 全放行——**既存問題非本次迴歸**（兩環境 `ENV` 一直填 `DEVELOP_PREMISE`，等同 Development 的 `*`）。
  - STG（188）：`CORS_ALLOWED_ORIGINS=https://192.168.50.188`
  - POC（189）：`CORS_ALLOWED_ORIGINS=https://192.168.50.189`
  - 格式：逗號分隔可多值；填錯會擋掉 FE 呼叫，改完重啟後先開站確認登入正常。
- [ ] **必做**：`LICENSE_ENFORCEMENT_ENABLED` / `LICENSE_READONLY_GATE_ENABLED` 顯式寫入 `.env`
  （現靠程式預設隱式全開，曾是 DEV 踩雷點；值依該環境的授權策略填 `true`/`false`）。
- [ ] **建議**：刪 `.env` 死變數：`REDIS_PORT`、`UPLOAD_DIR`、`UPLOAD_FILE_DIR`、`UPLOAD_STATIC_DIR`（§4，零消費者）。
- [ ] **optional**：`LOG_DIR`（不設 = `./log`，與現況相同，可不動）。
- [ ] **optional**：機密設定改用新平鋪格式（§3）。**向後相容、舊格式優先，可完全不動**；若要改，改完整組（`DB_SECRET` → `DB_USER`+`DB_PASSWORD` 等），不要半套混填。

### 上版時 — DB seed migration

- [ ] **必做**：套 `scripts/sql/2026-08-14-fr063-1g-runtime-config-seed.sql`（**目前只套過 DEV**）。
  - 指令（依 SQL migration 鐵則，帳號 `cmmgr`，密碼查 `.env`）：
    ```bash
    psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_stg \
      --single-transaction -v ON_ERROR_STOP=1 \
      -f scripts/sql/2026-08-14-fr063-1g-runtime-config-seed.sql
    psql -h 192.168.50.189 -p 25432 -U cmmgr -d guidant_ai_poc \
      --single-transaction -v ON_ERROR_STOP=1 \
      -f scripts/sql/2026-08-14-fr063-1g-runtime-config-seed.sql
    ```
  - `ON CONFLICT DO NOTHING`，可重複執行；不套也能開機（落回內建預設），但 DB 覆寫層形同不存在。

### 上版後 — 驗證與營運須知

- [ ] **必做**：重啟 BE 後健康檢查——服務起得來、登入正常、啟動 log 有印環境顯示標籤、無缺必填項報錯。
- [ ] **必做（營運須知，寫給日後調參的人）**：`system_configs` RUNTIME_CONFIG 七鍵生效時機分兩類——
  - 登入政策四項＋`MFA_REQUIRED`：改 DB **立即生效**，不需重啟；
  - `JWT_ACCESS_TOKEN_EXPIRES` / `JWT_REFRESH_TOKEN_EXPIRES`：框架啟動期讀取，改 DB 後**必須重啟 BE 才生效**。
- [ ] **建議**：抽驗 CORS 生效——從非允許來源打一次 API 應被擋（或看 response header `Access-Control-Allow-Origin` 不再是 `*`）。
- [ ] **必做**：驗證完成後回報決策者，並在本檔（或部署紀錄）註記各環境完成日期。

## 7. 正式上線前置作業（超出 STG/POC 範圍的 production 事項）

> 2026-08-15 起集中收錄「不綁定某次上版、但正式對外/落地版交付前必須完成」的前置項。
> 各項多有外部 lead time，**宜提早啟動**；細節待決策者討論定案後展開。

- [ ] **Google Drive 正式 OAuth 應用程式申請** 🔴 有外部 lead time（Google 審核數天～數週），勿壓線辦
  - 現況：全環境共用 DEVELOP_PREMISE 的開發用 OAuth Client（redirect URI 指 localhost:8000），正式站台無法用
  - 待辦：Google Cloud Console 申請正式 OAuth app → redirect URI 填正式站台網址 → 通過 Google 應用審核（unverified app 對外部帳號有 100 人/敏感 scope 限制）→ 三支 env（GOOGLE_DRIVE_OAUTH_CLIENT_ID/SECRET/REDIRECT_URI）換正式值
  - ⚠️ 待討論定案：SaaS 版單一正式 app 全平台共用；**落地版實務上只能由客戶自備 OAuth app**（我們的 app 無法登記客戶內網 redirect URI）——裝機文件需附客戶申請步驟或代設 SOP，歸 FR-063.3 部署文件範圍
  - 決策點（找決策者）：申請用哪個 Google 帳號（公司帳號歸屬）／app 名稱與品牌資訊／SaaS 版檔案落地帳號的營運政策（平台帳號 vs 各租戶自授權）
