-
Notifications
You must be signed in to change notification settings - Fork 110
feat(app): support legacy env variables and adapt to new settings object #818
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
fb2ac07
1618bba
b8f6b21
7c51d19
f34fa2b
48f036d
7cdbb63
6be36b3
797adae
0f5d8a5
e1829df
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| ################# TACHYBASE APPLICATION ################# | ||
| APP_ENV=development | ||
| APP_PORT=3000 | ||
| APP_KEY=test-key | ||
|
|
||
| # experimental support | ||
| EXTENSION_UI_BASE_PATH=/adapters/ | ||
|
|
||
| API_BASE_PATH=/api/ | ||
| API_BASE_URL= | ||
|
|
||
| # console | file | dailyRotateFile | ||
| LOGGER_TRANSPORT= | ||
| LOGGER_BASE_PATH=storage/logs | ||
| # error | warn | info | debug | ||
| LOGGER_LEVEL= | ||
| # If LOGGER_TRANSPORT is dailyRotateFile and using days, add 'd' as the suffix. | ||
| LOGGER_MAX_FILES= | ||
| # add 'k', 'm', 'g' as the suffix. | ||
| LOGGER_MAX_SIZE= | ||
| # json | splitter, split by '|' character | ||
| LOGGER_FORMAT= | ||
|
|
||
| ################# DATABASE ################# | ||
|
|
||
| DB_DIALECT=sqlite | ||
| DB_STORAGE=storage/db/tachybase.sqlite | ||
| DB_TABLE_PREFIX= | ||
| # DB_HOST=localhost | ||
| # DB_PORT=5432 | ||
| # DB_DATABASE=postgres | ||
| # DB_USER=tachybase | ||
| # DB_PASSWORD=tachybase | ||
| # DB_LOGGING=on | ||
| # DB_UNDERSCORED=false | ||
|
|
||
| #== SSL CONFIG ==# | ||
| # DB_DIALECT_OPTIONS_SSL_CA= | ||
| # DB_DIALECT_OPTIONS_SSL_KEY= | ||
| # DB_DIALECT_OPTIONS_SSL_CERT= | ||
| # DB_DIALECT_OPTIONS_SSL_REJECT_UNAUTHORIZED=true | ||
|
|
||
| ################# CACHE ################# | ||
| CACHE_DEFAULT_STORE=memory | ||
| # max number of items in memory cache | ||
| CACHE_MEMORY_MAX=2000 | ||
| # CACHE_REDIS_URL= | ||
|
|
||
| ################# STORAGE (Initialization only) ################# | ||
|
|
||
| INIT_APP_LANG=en-US | ||
| [email protected] | ||
| INIT_ROOT_PASSWORD=!Admin123. | ||
| INIT_ROOT_NICKNAME=Super Admin | ||
| INIT_ROOT_USERNAME=tachybase | ||
|
|
||
|
Comment on lines
+51
to
+56
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Root account credentials exposed
🤖 Prompt for AI Agents |
||
| ################# ENCRYPTION FIELD ################# | ||
|
|
||
| ENCRYPTION_FIELD_KEY= | ||
| ##### PRESETS ##### | ||
|
|
||
| # 单写名称:添加指定插件且默认启用 名称前加!:移除指定插件 名称前加|:添加指定插件但默认禁用 | ||
| # PRESETS_CORE_PLUGINS=api-doc,api-keys,!messages | ||
| # PRESETS_LOCAL_PLUGINS=gantt,!iframe-block,|audit-logs | ||
| PRESETS_BULTIN_PLUGINS=acl,app-info,auth,backup,cloud-component,collection,cron,data-source,error-handler,event-source,file,workflow,message,pdf,ui-schema,user,web,worker-thread,env-secrets | ||
| PRESETS_EXTERNAL_PLUGINS=action-bulk-edit,action-bulk-update,action-custom-request,action-duplicate,action-export,action-import,action-print,block-calendar,block-charts,block-gantt,block-kanban,block-presentation,field-china-region,field-formula,field-sequence,field-encryption,log-viewer,otp,instrumentation,full-text-search,password-policy,auth-pages,manual-notification,auth-main-app,!adapter-bullmq,!adapter-red-node,!adapter-remix,!api-keys,!audit-logs,!auth-cas,!auth-dingtalk,!auth-lark,!auth-oidc,!auth-saml,!auth-sms,!auth-wechat,!auth-wecom,!block-comments,!block-map,!block-step-form,!data-source-common,!demos-game-runesweeper,!devtools,!field-markdown-vditor,!field-snapshot,!hera,!i18n-editor,!multi-app,!multi-app-share-collection,!online-user,!simple-cms,!sub-accounts,!theme-editor,!workflow-approval,!ai-chat,!department,!workflow-analysis,!api-logs,!ocr-convert,!text-copy,!user-manual-feishu,!evaluator-mathjs | ||
|
|
||
|
|
||
| # 主应用工作线程默认数量 | ||
| WORKER_COUNT=1 | ||
| # WORKER_TIMEOUT=1800 | ||
| # 主应用工作线程最大数量 | ||
| WORKER_COUNT_MAX=8 | ||
| # WORKER_ERROR_RETRY=3 | ||
| # 子应用工作线程默认数量 | ||
| WORKER_COUNT_SUB=0 | ||
| # 子应用工作线程最大数量 | ||
| WORKER_COUNT_MAX_SUB=1 | ||
|
|
||
| # export config, max length of export data to use main thread and page size in worker thread | ||
| # EXPORT_LENGTH_MAX=2000 | ||
| # EXPORT_WORKER_PAGESIZE=1000 | ||
|
|
||
| # 开发环境测试locale 强制使用 cache | ||
| #FORCE_LOCALE_CACHE=1 | ||
|
|
||
| # 禁止子应用装载的插件,多个用逗号分隔 | ||
| # FORBID_SUB_APP_PLUGINS=multi-app,manual-notification,multi-app-share-collection | ||
|
|
||
|
|
||
| # 工作线程最大内存,单位为MB | ||
| # WORKER_MAX_MEMORY=4096 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,17 +2,25 @@ import fs from 'node:fs'; | |
| import path from 'node:path'; | ||
| import TachybaseGlobal from '@tachybase/globals'; | ||
|
|
||
| import { parseEnvironment } from './utils'; | ||
| import { convertEnvToSettings, parseEnvironment } from './utils'; | ||
|
|
||
| // 解析环境变量 | ||
| parseEnvironment(); | ||
|
|
||
| // 读取配置 | ||
| if (!fs.existsSync(`${process.env.TEGO_RUNTIME_HOME}/settings.js`)) { | ||
| fs.mkdirSync(`${process.env.TEGO_RUNTIME_HOME}`, { recursive: true }); | ||
| fs.copyFileSync(path.join(__dirname, '../presets/settings.js'), `${process.env.TEGO_RUNTIME_HOME}/settings.js`); | ||
| // 兼容旧的 .env 环境变量文件作为配置(仅在 settings.js 不存在时使用,且 .env 文件存在) | ||
| if ( | ||
| fs.existsSync(path.join(process.env.TEGO_RUNTIME_HOME, '.env')) && | ||
| !fs.existsSync(`${process.env.TEGO_RUNTIME_HOME}/settings.js`) | ||
| ) { | ||
| if (!fs.existsSync(`${process.env.TEGO_RUNTIME_HOME}/settings.js`)) { | ||
| fs.mkdirSync(`${process.env.TEGO_RUNTIME_HOME}`, { recursive: true }); | ||
| fs.copyFileSync(path.join(__dirname, '../presets/settings.js'), `${process.env.TEGO_RUNTIME_HOME}/settings.js`); | ||
| } | ||
| TachybaseGlobal.settings = require(`${process.env.TEGO_RUNTIME_HOME}/settings.js`); | ||
| } else { | ||
|
Comment on lines
+11
to
+21
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainLegacy .env path is not loaded; remove redundant existsSync and use path.join The branch checks for TEGO_RUNTIME_HOME/.env but never loads it. Unless the generated settings.js itself loads TEGO_RUNTIME_HOME/.env, env values in that file will be ignored. Also, the inner existsSync check is redundant, and mixing template strings with filesystem paths is brittle. Suggested inline refactor (loads runtime .env, removes the redundant check, and uses path.join consistently): -if (
- fs.existsSync(path.join(process.env.TEGO_RUNTIME_HOME, '.env')) &&
- !fs.existsSync(`${process.env.TEGO_RUNTIME_HOME}/settings.js`)
-) {
- if (!fs.existsSync(`${process.env.TEGO_RUNTIME_HOME}/settings.js`)) {
- fs.mkdirSync(`${process.env.TEGO_RUNTIME_HOME}`, { recursive: true });
- fs.copyFileSync(path.join(__dirname, '../presets/settings.js'), `${process.env.TEGO_RUNTIME_HOME}/settings.js`);
- }
- TachybaseGlobal.settings = require(`${process.env.TEGO_RUNTIME_HOME}/settings.js`);
+if (
+ fs.existsSync(path.join(process.env.TEGO_RUNTIME_HOME!, '.env')) &&
+ !fs.existsSync(path.join(process.env.TEGO_RUNTIME_HOME!, 'settings.js'))
+) {
+ // Load legacy .env before generating settings.js so its values are available
+ dotenvConfig({ path: path.join(process.env.TEGO_RUNTIME_HOME!, '.env') });
+ fs.mkdirSync(process.env.TEGO_RUNTIME_HOME!, { recursive: true });
+ fs.copyFileSync(
+ path.join(__dirname, '../presets/settings.js'),
+ path.join(process.env.TEGO_RUNTIME_HOME!, 'settings.js'),
+ );
+ TachybaseGlobal.settings = require(path.join(process.env.TEGO_RUNTIME_HOME!, 'settings.js'));Add this import at the top of the file: import { config as dotenvConfig } from 'dotenv';Would you confirm whether presets/settings.js actually loads TEGO_RUNTIME_HOME/.env by itself? If not, loading it here (as shown) ensures legacy env files take effect. 🏁 Script executed: #!/bin/bash
set -e
echo "=== Locating presets/settings.js ==="
PRESET_FILE=$(fd -t f presets/settings.js || echo "")
if [[ -n "$PRESET_FILE" ]]; then
echo "Found presets/settings.js at: $PRESET_FILE"
echo "----- First 200 lines of presets/settings.js -----"
sed -n '1,200p' "$PRESET_FILE"
else
echo "presets/settings.js not found"
fi
echo
echo "=== Locating packages/tego/src/constants.ts ==="
CONST_FILE=$(fd -t f -p "constants.ts" | grep "packages/tego/src/constants.ts" || echo "")
if [[ -n "$CONST_FILE" ]]; then
echo "Found constants.ts at: $CONST_FILE"
echo "----- Lines 1-50 of constants.ts -----"
sed -n '1,50p' "$CONST_FILE"
else
echo "packages/tego/src/constants.ts not found"
fiLength of output: 2218 🏁 Script executed: #!/bin/bash
set -e
echo "=== Searching for any 'presets' directories ==="
fd -t d presets || echo "No 'presets' directory found"
echo
echo "=== Searching for settings.ts files ==="
fd -t f settings.ts || echo "No settings.ts files found"
echo
echo "=== Searching for settings.js files ==="
fd -t f settings.js || echo "No settings.js files found"
echo
echo "=== Showing contents of all settings.* files (first 200 lines) ==="
for f in $(fd -t f 'settings\.(ts|js)$'); do
echo "----- File: $f -----"
sed -n '1,200p' "$f"
echo
doneLength of output: 4891 🏁 Script executed: #!/bin/bash
set -e
echo "=== Locating packages/tego/src/utils.ts ==="
UTILS_FILE=$(fd -t f utils.ts | grep "packages/tego/src/utils.ts" || echo "")
if [[ -n "$UTILS_FILE" ]]; then
echo "Found utils.ts at: $UTILS_FILE"
echo "----- First 200 lines of utils.ts -----"
sed -n '1,200p' "$UTILS_FILE"
else
echo "packages/tego/src/utils.ts not found"
fiLength of output: 6534 Load legacy The • File: Suggested changes:
This ensures legacy environment variables under 🤖 Prompt for AI Agents |
||
| TachybaseGlobal.settings = convertEnvToSettings(process.env); | ||
| } | ||
| TachybaseGlobal.settings = require(`${process.env.TEGO_RUNTIME_HOME}/settings.js`); | ||
|
|
||
| for (const key in TachybaseGlobal.settings.env) { | ||
| if (!process.env[key]) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,12 +9,14 @@ import { access, mkdir, readFile, rm, unlink, writeFile } from 'node:fs/promises | |||||||||||||||||||||
| import os from 'node:os'; | ||||||||||||||||||||||
| import { dirname, join, resolve } from 'node:path'; | ||||||||||||||||||||||
| import { pipeline } from 'node:stream/promises'; | ||||||||||||||||||||||
| import TachybaseGlobal from '@tachybase/globals'; | ||||||||||||||||||||||
| import TachybaseGlobal, { Settings } from '@tachybase/globals'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import { config } from 'dotenv'; | ||||||||||||||||||||||
| import { cloneDeep } from 'lodash'; | ||||||||||||||||||||||
| import npmRegistryFetch from 'npm-registry-fetch'; | ||||||||||||||||||||||
| import * as tar from 'tar'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import defaultSettings from '../presets/settings'; | ||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix TS build: avoid importing files outside rootDir (pipeline error) The CI error shows TypeScript rejects importing
Apply this diff here, plus add the small shim file below. -import defaultSettings from '../presets/settings';
+import defaultSettings from './presets/settings';Add new file at packages/tego/src/presets/settings.ts: // Re-export to satisfy tsc rootDir constraints
export { default } from '../../presets/settings';This resolves the CI error without changing build config. 🧰 Tools🪛 GitHub Actions: Full Build[error] 19-19: File '/home/runner/work/tego/tego/packages/tego/presets/settings.js' is not under 'rootDir' '/home/runner/work/tego/tego/packages/tego/src'. 'rootDir' is expected to contain all source files. 🤖 Prompt for AI Agents |
||||||||||||||||||||||
| import { DEFAULT_WEB_PACKAGE_NAME, LAST_UPDATE_FILE_SUFFIX } from './constants'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export function parseEnvironment() { | ||||||||||||||||||||||
|
|
@@ -30,15 +32,6 @@ export function parseEnvironment() { | |||||||||||||||||||||
| }); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // 如果存在 storage 的话,TEGO_RUNTIME_HOME 默认指向当前路径 | ||||||||||||||||||||||
| if ( | ||||||||||||||||||||||
| !process.env.TEGO_RUNTIME_HOME && | ||||||||||||||||||||||
| !process.env.TEGO_RUNTIME_NAME && | ||||||||||||||||||||||
| fs.existsSync(resolve(process.cwd(), 'storage')) | ||||||||||||||||||||||
| ) { | ||||||||||||||||||||||
| process.env.TEGO_RUNTIME_HOME = process.cwd(); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| for (const key in env) { | ||||||||||||||||||||||
| if (!process.env[key]) { | ||||||||||||||||||||||
| process.env[key] = env[key]; | ||||||||||||||||||||||
|
|
@@ -209,62 +202,138 @@ export class TegoIndexManager { | |||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export function convertEnvToSettings(flatEnv: Record<string, string | undefined>) { | ||||||||||||||||||||||
| const settings: any = { | ||||||||||||||||||||||
| env: {}, | ||||||||||||||||||||||
| logger: {}, | ||||||||||||||||||||||
| database: {}, | ||||||||||||||||||||||
| cache: {}, | ||||||||||||||||||||||
| encryptionField: {}, | ||||||||||||||||||||||
| presets: {}, | ||||||||||||||||||||||
| worker: {}, | ||||||||||||||||||||||
| export: {}, | ||||||||||||||||||||||
| misc: {}, | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // LOGGER_ | ||||||||||||||||||||||
| for (const key in flatEnv) { | ||||||||||||||||||||||
| const value = flatEnv[key]; | ||||||||||||||||||||||
| if (value === undefined) continue; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (key.startsWith('LOGGER_')) { | ||||||||||||||||||||||
| const subKey = key.replace('LOGGER_', '').toLowerCase(); | ||||||||||||||||||||||
| if (subKey === 'transport') { | ||||||||||||||||||||||
| settings.logger.transport = value.split(',').map((x) => x.trim()); | ||||||||||||||||||||||
| } else if (subKey === 'maxfiles') { | ||||||||||||||||||||||
| const settings: Settings = cloneDeep(defaultSettings); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| for (const [key, value] of Object.entries(flatEnv)) { | ||||||||||||||||||||||
| if (!value) continue; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| switch (key) { | ||||||||||||||||||||||
|
Comment on lines
+207
to
+210
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Trim values before processing; skip only when empty after trim Prevents accidental ignoring when values contain whitespace. - for (const [key, value] of Object.entries(flatEnv)) {
- if (!value) continue;
+ for (const [key, value] of Object.entries(flatEnv)) {
+ if (value == null) continue;
+ const v = String(value).trim();
+ if (!v) continue;Follow-up: use 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| /** ================= LOGGER ================= */ | ||||||||||||||||||||||
| case 'LOGGER_TRANSPORT': | ||||||||||||||||||||||
| settings.logger.transport = value.split(',') as any; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
Comment on lines
+212
to
+214
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Harden LOGGER_TRANSPORT parsing (trim, dedupe, validate) Trim tokens, dedupe, and ignore invalid entries to avoid type casting to - case 'LOGGER_TRANSPORT':
- settings.logger.transport = value.split(',') as any;
+ case 'LOGGER_TRANSPORT': {
+ const allowed = new Set(['console', 'file', 'dailyRotateFile']);
+ const transports = Array.from(
+ new Set(
+ v
+ .split(',')
+ .map((t) => t.trim())
+ .filter((t) => allowed.has(t)),
+ ),
+ );
+ if (transports.length) {
+ settings.logger.transport = transports as any;
+ }
+ break;
+ }
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| case 'LOGGER_BASE_PATH': | ||||||||||||||||||||||
| settings.logger.basePath = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'LOGGER_LEVEL': | ||||||||||||||||||||||
| settings.logger.level = value as any; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'LOGGER_MAX_FILES': | ||||||||||||||||||||||
| settings.logger.maxFiles = value; | ||||||||||||||||||||||
| } else if (subKey === 'maxsize') { | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'LOGGER_MAX_SIZE': | ||||||||||||||||||||||
| settings.logger.maxSize = value; | ||||||||||||||||||||||
| } else if (subKey === 'format') { | ||||||||||||||||||||||
| settings.logger.format = value; | ||||||||||||||||||||||
| } else { | ||||||||||||||||||||||
| settings.logger[subKey] = value; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| continue; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // DB_ | ||||||||||||||||||||||
| if (key.startsWith('DB_')) { | ||||||||||||||||||||||
| const subKey = key.replace('DB_', '').toLowerCase(); | ||||||||||||||||||||||
| if (subKey.startsWith('dialect_options_ssl_')) { | ||||||||||||||||||||||
| // e.g. DB_DIALECT_OPTIONS_SSL_CA | ||||||||||||||||||||||
| const sslKey = subKey.replace('dialect_options_ssl_', ''); | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'LOGGER_FORMAT': | ||||||||||||||||||||||
| settings.logger.format = value as any; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** ================= DATABASE ================= */ | ||||||||||||||||||||||
| case 'DB_DIALECT': | ||||||||||||||||||||||
| settings.database.dialect = value as any; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'DB_STORAGE': | ||||||||||||||||||||||
| settings.database.storage = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'DB_HOST': | ||||||||||||||||||||||
| settings.database.host = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'DB_PORT': | ||||||||||||||||||||||
| settings.database.port = +value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'DB_DATABASE': | ||||||||||||||||||||||
| settings.database.database = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'DB_USER': | ||||||||||||||||||||||
| settings.database.user = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'DB_PASSWORD': | ||||||||||||||||||||||
| settings.database.password = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'DB_LOGGING': | ||||||||||||||||||||||
| settings.database.logging = value === 'on'; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
Comment on lines
+254
to
+255
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion DB_LOGGING: accept common truthy values ('on', 'true', '1', 'yes') More forgiving parsing helps compatibility. - case 'DB_LOGGING':
- settings.database.logging = value === 'on';
+ case 'DB_LOGGING': {
+ const truthy = new Set(['on', 'true', '1', 'yes']);
+ settings.database.logging = truthy.has(v.toLowerCase());
+ break;
+ }
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| case 'DB_TABLE_PREFIX': | ||||||||||||||||||||||
| settings.database.tablePrefix = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'DB_UNDERSCORED': | ||||||||||||||||||||||
| settings.database.underscored = value === 'true'; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
Comment on lines
+260
to
+261
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion DB_UNDERSCORED: normalize common boolean values Follow same boolean normalization as DB_LOGGING. - case 'DB_UNDERSCORED':
- settings.database.underscored = value === 'true';
+ case 'DB_UNDERSCORED': {
+ const truthy = new Set(['on', 'true', '1', 'yes']);
+ settings.database.underscored = truthy.has(v.toLowerCase());
+ break;
+ }
🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case 'DB_DIALECT_OPTIONS_SSL_CA': | ||||||||||||||||||||||
| settings.database.ssl = settings.database.ssl || {}; | ||||||||||||||||||||||
| settings.database.ssl[sslKey] = value; | ||||||||||||||||||||||
| } else { | ||||||||||||||||||||||
| settings.database[subKey] = value; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| continue; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // CACHE_ | ||||||||||||||||||||||
| if (key.startsWith('CACHE_')) { | ||||||||||||||||||||||
| const subKey = key.replace('CACHE_', '').toLowerCase(); | ||||||||||||||||||||||
| settings.cache[subKey] = value; | ||||||||||||||||||||||
| continue; | ||||||||||||||||||||||
| settings.database.ssl.ca = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'DB_DIALECT_OPTIONS_SSL_KEY': | ||||||||||||||||||||||
| settings.database.ssl = settings.database.ssl || {}; | ||||||||||||||||||||||
| settings.database.ssl.key = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'DB_DIALECT_OPTIONS_SSL_CERT': | ||||||||||||||||||||||
| settings.database.ssl = settings.database.ssl || {}; | ||||||||||||||||||||||
| settings.database.ssl.cert = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'DB_DIALECT_OPTIONS_SSL_REJECT_UNAUTHORIZED': | ||||||||||||||||||||||
| settings.database.ssl = settings.database.ssl || {}; | ||||||||||||||||||||||
| settings.database.ssl.rejectUnauthorized = value === 'true'; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** ================= CACHE ================= */ | ||||||||||||||||||||||
| case 'CACHE_DEFAULT_STORE': | ||||||||||||||||||||||
| settings.cache.defaultStore = value as any; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'CACHE_MEMORY_MAX': | ||||||||||||||||||||||
| settings.cache.memoryMax = +value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'CACHE_REDIS_URL': | ||||||||||||||||||||||
| settings.cache.redisUrl = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** ================= ENCRYPTION ================= */ | ||||||||||||||||||||||
| case 'ENCRYPTION_FIELD_KEY': | ||||||||||||||||||||||
| settings.encryptionField.key = value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** ================= PRESETS ================= */ | ||||||||||||||||||||||
| case 'PRESETS_BULTIN_PLUGINS': | ||||||||||||||||||||||
| settings.presets.builtinPlugins = value.split(','); | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'PRESETS_EXTERNAL_PLUGINS': | ||||||||||||||||||||||
| settings.presets.externalPlugins = value.split(',').map((name) => { | ||||||||||||||||||||||
| if (name.startsWith('!')) { | ||||||||||||||||||||||
| return { name: name.slice(1), enabledByDefault: false }; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| if (name.startsWith('|')) { | ||||||||||||||||||||||
| return { name: name.slice(1), enabledByDefault: false }; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return { name, enabledByDefault: true }; | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** ================= WORKER ================= */ | ||||||||||||||||||||||
| case 'WORKER_COUNT': | ||||||||||||||||||||||
| settings.worker.count = +value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'WORKER_COUNT_MAX': | ||||||||||||||||||||||
| settings.worker.countMax = +value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** ================= EXPORT ================= */ | ||||||||||||||||||||||
| case 'EXPORT_LENGTH_MAX': | ||||||||||||||||||||||
| settings.export.lengthMax = +value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| case 'EXPORT_WORKER_PAGESIZE': | ||||||||||||||||||||||
| settings.export.workerPageSize = +value; | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** ================= MISC ================= */ | ||||||||||||||||||||||
| case 'FORBID_SUB_APP_PLUGINS': | ||||||||||||||||||||||
| settings.misc.forbidSubAppPlugins = value.split(','); | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| default: | ||||||||||||||||||||||
| // 不处理未知 key | ||||||||||||||||||||||
| break; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // 其它 => 默认归到 settings.env | ||||||||||||||||||||||
| settings.env[key] = value; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return settings; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hard-coded secret committed
APP_KEYcontains a real-looking key. Even in test fixtures, commit history persists and may leak into production workflows. Replace with an obvious placeholder (e.g.CHANGE_ME) or load from CI secrets.🤖 Prompt for AI Agents