diff --git a/api/configs/extend/__init__.py b/api/configs/extend/__init__.py index 3e4fed59d..b094af17f 100644 --- a/api/configs/extend/__init__.py +++ b/api/configs/extend/__init__.py @@ -6,7 +6,6 @@ from pydantic_settings import BaseSettings class ExtendInfo(BaseSettings): - OAUTH2_CLIENT_ID: Optional[str] = Field( description="OA client id for OAuth", default=None, @@ -74,6 +73,16 @@ class ExtendInfo(BaseSettings): ) # Extend: 记忆上下文功能 + # Extend: 控制台首次安装完成后,向内网 Admin 服务触发 /init/initdb(与 DB_* 同源,无需额外密钥文件) + ADMIN_INITDB_ENABLED: bool = Field( + description="Dify 安装向导完成后是否请求 Admin 初始化业务库表", + default=False, + ) + ADMIN_INITDB_URL: str = Field( + description="Admin InitDB 接口完整 URL(Docker 默认可为 http://admin-server:8888/admin/api/init/initdb)", + default="http://admin-server:8888/admin/api/init/initdb", + ) + class ExtendConfig(ExtendInfo): pass diff --git a/api/controllers/console/setup.py b/api/controllers/console/setup.py index e1ea00723..c8de4b53d 100644 --- a/api/controllers/console/setup.py +++ b/api/controllers/console/setup.py @@ -9,6 +9,7 @@ from libs.helper import EmailStr, extract_remote_ip from libs.password import valid_password from models.model import DifySetup, db from services.account_service import RegisterService, TenantService +from services.admin_initdb_service import trigger_admin_initdb_if_configured from .error import AlreadySetupError, NotInitValidateError from .init_validate import get_init_validate_status @@ -82,6 +83,8 @@ def setup_system(payload: SetupRequestPayload) -> SetupResponse: language=payload.language, ) + trigger_admin_initdb_if_configured(admin_password=payload.password) + return SetupResponse(result="success") diff --git a/api/services/admin_initdb_service.py b/api/services/admin_initdb_service.py new file mode 100644 index 000000000..7d6980968 --- /dev/null +++ b/api/services/admin_initdb_service.py @@ -0,0 +1,105 @@ +""" +Trigger gin-vue-admin InitDB after Dify console setup. + +Uses the same DB_* settings as the API container and the admin password from the setup form. +Does not read or write local config files; outbound call only if ADMIN_INITDB_ENABLED is true. +""" + +from __future__ import annotations + +import logging +from typing import Any, Literal + +import httpx + +from configs import dify_config + +logger = logging.getLogger(__name__) + +_ADMIN_ALREADY_INIT_MSG = "已存在数据库配置" +_DEFAULT_TIMEOUT = httpx.Timeout(120.0, connect=10.0) + + +def _admin_db_type(db_type: str) -> Literal["pgsql", "mysql"]: + if db_type == "postgresql": + return "pgsql" + return "mysql" + + +def _build_payload(admin_password: str) -> dict[str, Any]: + db_type = _admin_db_type(dify_config.DB_TYPE) + return { + "adminPassword": admin_password, + "dbType": db_type, + "host": dify_config.DB_HOST, + "port": str(dify_config.DB_PORT), + "userName": dify_config.DB_USERNAME, + "password": dify_config.DB_PASSWORD, + "dbName": dify_config.DB_DATABASE, + "dbPath": "", + } + + +def _is_acceptable_admin_response(body: dict[str, Any]) -> bool: + code = body.get("code") + msg = body.get("msg") or "" + if code == 0: + return True + if msg == _ADMIN_ALREADY_INIT_MSG: + return True + return False + + +def trigger_admin_initdb_if_configured(*, admin_password: str) -> None: + """ + Best-effort POST to Admin InitDB. Logs warnings on failure; never raises. + """ + if not dify_config.ADMIN_INITDB_ENABLED: + return + if dify_config.DB_TYPE not in ("postgresql", "mysql", "oceanbase", "seekdb"): + logger.warning( + "skip admin initdb: unsupported DB_TYPE for admin bridge: %s", + dify_config.DB_TYPE, + ) + return + + url = (dify_config.ADMIN_INITDB_URL or "").strip() + if not url: + logger.warning("ADMIN_INITDB_ENABLED is true but ADMIN_INITDB_URL is empty, skip admin initdb") + return + + payload = _build_payload(admin_password) + try: + with httpx.Client(timeout=_DEFAULT_TIMEOUT, follow_redirects=True) as client: + response = client.post( + url, + json=payload, + headers={"Content-Type": "application/json", "Accept": "application/json"}, + ) + except httpx.RequestError as exc: + logger.warning("admin initdb request failed: %s", exc) + return + + if response.status_code != 200: + logger.warning( + "admin initdb HTTP %s: %s", + response.status_code, + (response.text or "")[:500], + ) + return + + try: + body = response.json() + except ValueError: + logger.warning("admin initdb returned non-JSON body: %s", (response.text or "")[:500]) + return + + if not isinstance(body, dict): + logger.warning("admin initdb returned unexpected JSON: %s", body) + return + + if _is_acceptable_admin_response(body): + logger.info("admin initdb finished: %s", body.get("msg")) + return + + logger.warning("admin initdb rejected: %s", body) diff --git a/docker/.env.example b/docker/.env.example index cbfe6aa5c..254131693 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -693,7 +693,7 @@ ALIBABACLOUD_MYSQL_MAX_CONNECTION=5 ALIBABACLOUD_MYSQL_HNSW_M=6 # relyt configurations, only available when VECTOR_STORE is `relyt` -RELYT_HOST=db +RELYT_HOST=db_postgres RELYT_PORT=5432 RELYT_USER=postgres RELYT_PASSWORD=difyai123456 diff --git a/docker/docker-compose.dify-plus.yaml b/docker/docker-compose.dify-plus.yaml index 4935e2daa..2c615bf40 100644 --- a/docker/docker-compose.dify-plus.yaml +++ b/docker/docker-compose.dify-plus.yaml @@ -717,7 +717,7 @@ services: # API service api: - image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-api:1.12.1 + image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-api:1.12.1.fix.3.2 restart: always environment: # Use the shared environment variables. @@ -734,6 +734,9 @@ services: INNER_API_KEY_FOR_PLUGIN: ${PLUGIN_DIFY_INNER_API_KEY:-QaHbTe77CtuXmsfyhR7+vRjI/+XbV1AaFy691iy+kGDv2Jvy0/eAh8Y1} FULL_CODE_EXECUTION_ENDPOINT: ${FULL_CODE_EXECUTION_ENDPOINT:-http://sandbox-full:8194} ALLOW_REGISTER: ${ALLOW_REGISTER:-True} + # 安装向导完成后由内网调用 Admin InitDB(库连接与 DB_* 一致,无需用户改本地配置文件) + ADMIN_INITDB_ENABLED: ${ADMIN_INITDB_ENABLED:-true} + ADMIN_INITDB_URL: ${ADMIN_INITDB_URL:-http://admin-server:8888/admin/api/init/initdb} depends_on: init_permissions: condition: service_completed_successfully @@ -761,7 +764,7 @@ services: # worker service # The Celery worker for processing all queues (dataset, workflow, mail, etc.) worker: - image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-api:1.12.1 + image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-api:1.12.1.fix.3.2 restart: always environment: # Use the shared environment variables. @@ -800,7 +803,7 @@ services: # worker-gaia service # The Celery worker-gaia for processing the queue. worker-gaia: - image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-api:1.12.1 + image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-api:1.12.1.fix.3.2 restart: always environment: # Use the shared environment variables. @@ -826,7 +829,7 @@ services: # worker-dataset service # The Celery worker-dataset for processing the queue. worker-dataset: - image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-api:1.12.1 + image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-api:1.12.1.fix.3.2 restart: always environment: # Use the shared environment variables. @@ -852,7 +855,7 @@ services: # worker_beat service # Celery beat for scheduling periodic tasks. worker_beat: - image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-api:1.12.1 + image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-api:1.12.1.fix.3.2 restart: always environment: # Use the shared environment variables. @@ -882,7 +885,7 @@ services: # Frontend web application. web: - image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-web:1.12.1 + image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-web:1.12.1.fix.3.2 restart: always environment: CONSOLE_API_URL: ${CONSOLE_API_URL:-} @@ -1663,7 +1666,7 @@ services: # Extend - admin-web admin-web: - image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-admin-web:1.12.1 + image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-admin-web:1.12.1.fix.3.2 restart: always ports: - '8081:8081' @@ -1675,7 +1678,7 @@ services: # Extend - admin-server admin-server: - image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-admin-server:1.12.1.fix.3.1 + image: ccr.ccs.tencentyun.com/yfgaia/dify-plus-admin-server:1.12.1.fix.3.2 restart: always environment: # Use the shared environment variables.