Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions packages/devkit/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,6 @@ export function initEnv() {
process.env.APP_BASE_URL = `http://127.0.0.1:${process.env.APP_PORT}`;
}

if (
!process.env.TEGO_RUNTIME_HOME &&
!process.env.TEGO_RUNTIME_NAME &&
_existsSync(resolve(process.cwd(), 'storage'))
) {
process.env.TEGO_RUNTIME_HOME = process.cwd();
}

for (const key in env) {
if (!process.env[key]) {
// @ts-ignore
Expand Down
2 changes: 1 addition & 1 deletion packages/globals/src/settings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface Settings {
};
logger: {
/** console | file | dailyRotateFile */
transport: ('console' | 'file' | 'dailyRotateFile')[];
transport?: ('console' | 'file' | 'dailyRotateFile')[];
basePath: string;
level?: 'error' | 'warn' | 'info' | 'debug';
maxFiles?: string | number; // e.g. "14d"
Expand Down
92 changes: 92 additions & 0 deletions packages/tego/src/__tests__/env
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Hard-coded secret committed

APP_KEY contains 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
In packages/tego/src/__tests__/env at line 4, the APP_KEY is hard-coded with a
real-looking secret key, which risks leaking sensitive information through
commit history. Replace the current value with a clear placeholder like
CHANGE_ME or configure the test environment to load this key securely from CI
secrets instead of hard-coding it.


# 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Root account credentials exposed

INIT_ROOT_PASSWORD (and related user details) are in plain text. This is high-risk if the file ever ships outside test scope. Use dummy values or remove the variable entirely from VCS.

🤖 Prompt for AI Agents
In packages/tego/src/__tests__/env around lines 51 to 56, the root account
credentials including INIT_ROOT_PASSWORD are exposed in plain text, which is a
security risk. Replace the real password and related user details with dummy
placeholder values or remove these variables entirely from version control to
prevent accidental exposure outside the test environment.

################# 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
4 changes: 1 addition & 3 deletions packages/tego/src/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ describe('convertEnvToSettings', () => {
const result = convertEnvToSettings(input as any);

expect(result.logger.transport).toEqual(['console', 'dailyRotateFile']);
expect(result.logger.max_files).toBeUndefined(); // 未提供
expect(result.logger.maxFiles).toBe('7d');
expect(result.database.storage).toBe('storage/db/tachybase.sqlite');
expect(result.cache.default_store).toBe('memory');
expect(result.env.INIT_APP_LANG).toBe('zh-CN');
expect(result.cache.defaultStore).toBe('memory');
});
});
18 changes: 13 additions & 5 deletions packages/tego/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Legacy .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"
fi

Length 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
done

Length 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"
fi

Length of output: 6534


Load legacy .env and streamline settings.js logic

The presets/settings.js file does not invoke dotenv, so legacy .env values under TEGO_RUNTIME_HOME are never applied. We should explicitly load that file before generating settings.js, remove the redundant inner existsSync check, and consistently use path.join.

• File: packages/tego/src/constants.ts (lines 11–21)
• Preserves the existing else branch that falls back to convertEnvToSettings.

Suggested changes:

  1. Add at the top of the file:
    import { config as dotenvConfig } from 'dotenv';
  2. Replace the block starting at line 11 with:
    -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 so its variables are available to the generated settings
    +  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'));
    }

This ensures legacy environment variables under TEGO_RUNTIME_HOME/.env are loaded before the fallback settings file is generated.

🤖 Prompt for AI Agents
In packages/tego/src/constants.ts around lines 11 to 21, the logic that
generates a settings.js from presets when TEGO_RUNTIME_HOME/.env exists fails to
load legacy .env values and contains a redundant inner existsSync; import dotenv
and explicitly load the .env file at TEGO_RUNTIME_HOME before creating/copying
settings.js, remove the duplicate fs.existsSync check, and consistently use
path.join for paths (create the runtime dir if needed, copy presets/settings.js
into path.join(process.env.TEGO_RUNTIME_HOME, 'settings.js'), then require that
file into TachybaseGlobal.settings).

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]) {
Expand Down
193 changes: 131 additions & 62 deletions packages/tego/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix TS build: avoid importing files outside rootDir (pipeline error)

The CI error shows TypeScript rejects importing ../presets/settings because it’s outside packages/tego/src. Two low-risk options:

  1. Re-export defaultSettings via an in-src shim and import from there (recommended).
  2. Adjust tsconfig rootDir/includes (higher blast radius).

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
In packages/tego/src/utils.ts around line 19, the file imports
../presets/settings which violates TypeScript rootDir constraints; change the
import to point to a new in-src shim (./presets/settings) and add a new file
packages/tego/src/presets/settings.ts that re-exports the original default
export from ../../presets/settings so tsc sees the module inside the package
root. Ensure the utils.ts import is updated to import defaultSettings from
'./presets/settings' and create the shim file that simply re-exports the default
export from the upstream presets path.

import { DEFAULT_WEB_PACKAGE_NAME, LAST_UPDATE_FILE_SUFFIX } from './constants';

export function parseEnvironment() {
Expand All @@ -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];
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The 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 v instead of value below.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (const [key, value] of Object.entries(flatEnv)) {
if (!value) continue;
switch (key) {
for (const [key, value] of Object.entries(flatEnv)) {
if (value == null) continue;
const v = String(value).trim();
if (!v) continue;
switch (key) {
🤖 Prompt for AI Agents
In packages/tego/src/utils.ts around lines 216 to 219, the loop currently skips
entries based on truthiness of value which will miss non-empty strings
containing only whitespace; convert the entry to a trimmed string first (e.g.
const v = String(value).trim()) and change the guard to if (!v) continue; then
use v (not value) in the subsequent switch/processing so all whitespace-only
values are treated as empty.

/** ================= LOGGER ================= */
case 'LOGGER_TRANSPORT':
settings.logger.transport = value.split(',') as any;
break;
Comment on lines +212 to +214
Copy link
Contributor

Choose a reason for hiding this comment

The 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 any.

-      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;
+      }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/tego/src/utils.ts around lines 221 to 223, the LOGGER_TRANSPORT
branch currently casts the split result to any; change it to robustly parse the
comma-separated string by splitting, trimming each token, filtering out empty
strings, deduplicating entries, and validating each token against the allowed
transport names (e.g., the enum or union type used for logger transports);
finally assign the resulting typed array (not any) to settings.logger.transport
so invalid entries are dropped and types are preserved.

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
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
+      }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/tego/src/utils.ts around lines 263-264, the code currently sets
settings.database.logging only when value === 'on'; change it to accept common
truthy strings (case-insensitive) — e.g. 'on', 'true', '1', 'yes' — by
normalizing the value (toString(), trim(), toLowerCase()) and checking
membership in that set, then set settings.database.logging to true for matches
and false otherwise (handling undefined/null safely).

case 'DB_TABLE_PREFIX':
settings.database.tablePrefix = value;
break;
case 'DB_UNDERSCORED':
settings.database.underscored = value === 'true';
break;
Comment on lines +260 to +261
Copy link
Contributor

Choose a reason for hiding this comment

The 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;
+      }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/tego/src/utils.ts around lines 269-270, the assignment
settings.database.underscored = value === 'true' only accepts the literal 'true'
string; replace it with the same boolean normalization used for DB_LOGGING
(i.e., normalize common boolean representations from the env value such as
'true'/'false', '1'/'0', 'yes'/'no' in a case-insensitive way) so underscored is
reliably set to true or false for those inputs; implement by reusing or
duplicating the DB_LOGGING normalization helper logic so value is converted to a
boolean before assignment.


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;
Expand Down
Loading