diff --git a/agent/common/database/db_session_manager.py b/agent/common/database/db_session_manager.py index b61efd3..6cdfd8a 100644 --- a/agent/common/database/db_session_manager.py +++ b/agent/common/database/db_session_manager.py @@ -54,6 +54,13 @@ class DBSessionManager(Singleton): db_url = f"{self.__DB_URL_MAP[db_config.db_type]}://{db_config.write_id}{pw}@{db_config.write_host}:{db_config.write_port}/{db_config.name}" LOG.i(f"Write DB create engine url : {db_url}") + # SSL/TLS: 관리형 DB(RDS/Aurora/Azure)는 보통 TLS 필수. sslmode 가 설정되면 asyncpg 에 전달. + # ""/"disable" → 미적용(로컬). "require"/"verify-ca"/"verify-full" → asyncpg ssl 사용. + connect_args = {} + sslmode = (getattr(db_config, "sslmode", "") or "").lower() + if sslmode and sslmode != "disable": + connect_args["ssl"] = sslmode + engine = create_async_engine( db_url, echo=db_config.show_log, @@ -61,6 +68,7 @@ class DBSessionManager(Singleton): max_overflow=db_config.max_overflow, pool_pre_ping=True, pool_recycle=600, + connect_args=connect_args, ) self.__engines.append(engine) scoped_session = async_scoped_session( diff --git a/agent/config/config.local.toml.example b/agent/config/config.local.toml.example new file mode 100644 index 0000000..c2006f6 --- /dev/null +++ b/agent/config/config.local.toml.example @@ -0,0 +1,46 @@ +# 복사해서 사용: cp config.local.toml.example config.local.toml +# 실제 config.local.toml 은 시크릿(DB 비번·OpenAI 키) 포함이라 커밋하지 않는다(.gitignore: *.toml). +# 모든 서버는 APP_ENV=local 로 띄우며 이 파일(config.local.toml)을 읽는다. +[WebServerConfig] +server_name = "NegosiumAgentServer" +port = 9500 +process_count = 1 +is_ssl = false +is_test = true + +[LogConfig] +print_console = true +log_level = "debug" + +# negosium_db 를 backend 와 공유. agent 는 learning 스키마에 RL 자산을 둔다. +# 도커 실행 시 host 는 docker-compose 가 DB_HOST 환경변수로 override 한다(컨테이너→호스트 DB). +# 관리형 DB(RDS/Aurora/Azure)는 host 에 엔드포인트, sslmode="require" 로 설정한다. +[MainDBConfig] +db_type = "postgresql" +name = "negosium_db" +write_host = "127.0.0.1" +write_port = 5432 +write_id = "" +write_pw = "" +read_host = "127.0.0.1" +read_port = 5432 +read_id = "" +read_pw = "" +show_log = false +pool_size = 10 +max_overflow = 20 +sslmode = "" # 로컬: "" 또는 "disable" / 관리형 DB: "require"|"verify-ca"|"verify-full" + +[AgentConfig] +tenants_dir = "tenants" +config_cache_ttl_seconds = 300 +allow_default_tenant = false +auto_seed_base = true +base_action_space = 9 +base_seed_episodes = 300 + +# LLM(OpenAI). api_key 는 시크릿 — 실제 키를 채우고 커밋 금지. +[OpenAIConfig] +provider = "openai" +api_key = "" +model = "gpt-4o-mini" diff --git a/agent/config/config_models.py b/agent/config/config_models.py index 2ae3a0d..46363c6 100644 --- a/agent/config/config_models.py +++ b/agent/config/config_models.py @@ -29,6 +29,8 @@ class MainDBConfig(ConfigModel): show_log: bool = False pool_size: int = 10 max_overflow: int = 20 + # SSL/TLS 모드: ""/"disable"=미사용(로컬), "require"/"verify-ca"/"verify-full"=관리형 DB(RDS/Aurora/Azure). + sslmode: str = "" class OpenAIConfig(ConfigModel): diff --git a/agent/config/server_configs.py b/agent/config/server_configs.py index fab2fe6..ad3717c 100644 --- a/agent/config/server_configs.py +++ b/agent/config/server_configs.py @@ -20,3 +20,23 @@ log_config: LogConfig = configs.get(LogConfig) main_db_config: MainDBConfig = configs.get(MainDBConfig) agent_config: AgentConfig = configs.get(AgentConfig) openai_config: OpenAIConfig = configs.get(OpenAIConfig) or OpenAIConfig() + + +# DB 접속 env override (config.local.toml 은 그대로 두고, 도커 등에서 host 만 바꾸기 위함). +# 컨테이너 안에서는 127.0.0.1 이 자기 자신이라 호스트 DB 를 못 찾으므로, +# docker-compose 가 DB_HOST=host.docker.internal 을 주입한다. 로컬 실행은 env 미설정 → toml 그대로. +def _apply_db_env_override(cfg: MainDBConfig): + h = os.environ.get("DB_HOST") + if h: + cfg.write_host = cfg.read_host = h + if os.environ.get("DB_PORT"): + cfg.write_port = cfg.read_port = int(os.environ["DB_PORT"]) + if os.environ.get("DB_USER"): + cfg.write_id = cfg.read_id = os.environ["DB_USER"] + if os.environ.get("DB_PASSWORD"): + cfg.write_pw = cfg.read_pw = os.environ["DB_PASSWORD"] + if os.environ.get("DB_NAME"): + cfg.name = os.environ["DB_NAME"] + + +_apply_db_env_override(main_db_config) diff --git a/backend/common/database/db_session_manager.py b/backend/common/database/db_session_manager.py index c73a83e..31d9d2f 100644 --- a/backend/common/database/db_session_manager.py +++ b/backend/common/database/db_session_manager.py @@ -61,6 +61,12 @@ class DBSessionManager(Singleton): db_url = f"{self.__DB_URL_MAP[db_config.db_type]}://{db_config.write_id}{pw}@{db_config.write_host}:{db_config.write_port}/{db_config.name}" LOG.i(f"Write DB create engine url : {db_url}") + # SSL/TLS: 관리형 DB(RDS/Aurora/Azure)는 보통 TLS 필수. sslmode 가 설정되면 asyncpg 에 전달. + connect_args = {} + sslmode = (getattr(db_config, "sslmode", "") or "").lower() + if sslmode and sslmode != "disable": + connect_args["ssl"] = sslmode + engine = create_async_engine( db_url, echo=db_config.show_log, @@ -68,6 +74,7 @@ class DBSessionManager(Singleton): max_overflow=db_config.max_overflow, pool_pre_ping=True, pool_recycle=600, + connect_args=connect_args, ) self.__engines.append(engine) scoped_session = async_scoped_session( diff --git a/backend/config/config.local.toml.example b/backend/config/config.local.toml.example new file mode 100644 index 0000000..adb45b8 --- /dev/null +++ b/backend/config/config.local.toml.example @@ -0,0 +1,37 @@ +# 복사해서 사용: cp config.local.toml.example config.local.toml +# 실제 config.local.toml 은 시크릿 포함이라 커밋하지 않는다(.gitignore: *.toml). +# 모든 서버는 APP_ENV=local 로 띄우며 이 파일을 읽는다. +[WebServerConfig] +server_name = "NegosiumServer" +port = 9300 +process_count = 1 +is_ssl = false +is_test = true + +[LogConfig] +print_console = true +log_level = "debug" + +# DB Read/Write 분리. 도커 실행 시 host 는 docker-compose 의 DB_HOST 로 override. +# 관리형 DB(RDS/Aurora/Azure)는 host 에 엔드포인트, sslmode="require". +[MainDBConfig] +db_type = "postgresql" +name = "negosium_db" +write_host = "127.0.0.1" +write_port = 5432 +write_id = "" +write_pw = "" +read_host = "127.0.0.1" +read_port = 5432 +read_id = "" +read_pw = "" +show_log = false +pool_size = 10 +max_overflow = 20 +sslmode = "" # 로컬: "" / 관리형 DB: "require"|"verify-ca"|"verify-full" + +[JwtToken] +access_key = "" +refresh_key = "" +access_expire_min = 30 +refresh_expire_day = 7 diff --git a/backend/config/config_models.py b/backend/config/config_models.py index 822d9d3..d5af607 100644 --- a/backend/config/config_models.py +++ b/backend/config/config_models.py @@ -32,6 +32,8 @@ class MainDBConfig(ConfigModel): # PostgreSQL max_connections 를 넘지 않도록 설정해야 한다. (예: 10+20=30 x 2 x 5워커 = 300) pool_size: int = 10 max_overflow: int = 20 + # SSL/TLS 모드: ""/"disable"=미사용(로컬), "require"/"verify-ca"/"verify-full"=관리형 DB(RDS/Aurora/Azure). + sslmode: str = "" class JwtToken(ConfigModel): diff --git a/backend/config/server_configs.py b/backend/config/server_configs.py index fed6d5c..73201f3 100644 --- a/backend/config/server_configs.py +++ b/backend/config/server_configs.py @@ -19,3 +19,21 @@ web_server_config: WebServerConfig = configs.get(WebServerConfig) log_config: LogConfig = configs.get(LogConfig) main_db_config: MainDBConfig = configs.get(MainDBConfig) jwt_token_config: JwtToken = configs.get(JwtToken) + + +# DB 접속 env override (config.local.toml 유지, 도커에서 host 만 교체). 로컬은 env 미설정 → toml 그대로. +def _apply_db_env_override(cfg: MainDBConfig): + h = os.environ.get("DB_HOST") + if h: + cfg.write_host = cfg.read_host = h + if os.environ.get("DB_PORT"): + cfg.write_port = cfg.read_port = int(os.environ["DB_PORT"]) + if os.environ.get("DB_USER"): + cfg.write_id = cfg.read_id = os.environ["DB_USER"] + if os.environ.get("DB_PASSWORD"): + cfg.write_pw = cfg.read_pw = os.environ["DB_PASSWORD"] + if os.environ.get("DB_NAME"): + cfg.name = os.environ["DB_NAME"] + + +_apply_db_env_override(main_db_config) diff --git a/docker-compose.yml b/docker-compose.yml index 64f53db..ef24f8a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,7 @@ services: container_name: negosium-backend environment: APP_ENV: local + DB_HOST: host.docker.internal # 컨테이너→호스트 DB (config.local.toml의 127.0.0.1 override) ports: - "9300:9300" # 컨테이너에서 호스트의 DB 로 접근 (config.docker.toml 의 host.docker.internal) @@ -30,6 +31,7 @@ services: container_name: negodata-backend environment: APP_ENV: local + DB_HOST: host.docker.internal # 컨테이너→호스트 DB (config.local.toml의 127.0.0.1 override) ports: - "9400:9400" extra_hosts: @@ -42,6 +44,7 @@ services: container_name: negosium-agent environment: APP_ENV: local + DB_HOST: host.docker.internal # 컨테이너→호스트 DB (config.local.toml의 127.0.0.1 override) ports: - "9500:9500" extra_hosts: diff --git a/negodata/backend/common/database/db_session_manager.py b/negodata/backend/common/database/db_session_manager.py index c73a83e..31d9d2f 100644 --- a/negodata/backend/common/database/db_session_manager.py +++ b/negodata/backend/common/database/db_session_manager.py @@ -61,6 +61,12 @@ class DBSessionManager(Singleton): db_url = f"{self.__DB_URL_MAP[db_config.db_type]}://{db_config.write_id}{pw}@{db_config.write_host}:{db_config.write_port}/{db_config.name}" LOG.i(f"Write DB create engine url : {db_url}") + # SSL/TLS: 관리형 DB(RDS/Aurora/Azure)는 보통 TLS 필수. sslmode 가 설정되면 asyncpg 에 전달. + connect_args = {} + sslmode = (getattr(db_config, "sslmode", "") or "").lower() + if sslmode and sslmode != "disable": + connect_args["ssl"] = sslmode + engine = create_async_engine( db_url, echo=db_config.show_log, @@ -68,6 +74,7 @@ class DBSessionManager(Singleton): max_overflow=db_config.max_overflow, pool_pre_ping=True, pool_recycle=600, + connect_args=connect_args, ) self.__engines.append(engine) scoped_session = async_scoped_session( diff --git a/negodata/backend/config/config.local.toml.example b/negodata/backend/config/config.local.toml.example new file mode 100644 index 0000000..3c8c6ae --- /dev/null +++ b/negodata/backend/config/config.local.toml.example @@ -0,0 +1,37 @@ +# 복사해서 사용: cp config.local.toml.example config.local.toml +# 실제 config.local.toml 은 시크릿 포함이라 커밋하지 않는다(.gitignore: *.toml). +# 모든 서버는 APP_ENV=local 로 띄우며 이 파일을 읽는다. +[WebServerConfig] +server_name = "NegodataServer" +port = 9400 +process_count = 1 +is_ssl = false +is_test = true + +[LogConfig] +print_console = true +log_level = "debug" + +# DB Read/Write 분리. 도커 실행 시 host 는 docker-compose 의 DB_HOST 로 override. +# 관리형 DB(RDS/Aurora/Azure)는 host 에 엔드포인트, sslmode="require". +[MainDBConfig] +db_type = "postgresql" +name = "negodata_db" +write_host = "127.0.0.1" +write_port = 5432 +write_id = "" +write_pw = "" +read_host = "127.0.0.1" +read_port = 5432 +read_id = "" +read_pw = "" +show_log = false +pool_size = 10 +max_overflow = 20 +sslmode = "" # 로컬: "" / 관리형 DB: "require"|"verify-ca"|"verify-full" + +[JwtToken] +access_key = "" +refresh_key = "" +access_expire_min = 30 +refresh_expire_day = 7 diff --git a/negodata/backend/config/config_models.py b/negodata/backend/config/config_models.py index 822d9d3..d5af607 100644 --- a/negodata/backend/config/config_models.py +++ b/negodata/backend/config/config_models.py @@ -32,6 +32,8 @@ class MainDBConfig(ConfigModel): # PostgreSQL max_connections 를 넘지 않도록 설정해야 한다. (예: 10+20=30 x 2 x 5워커 = 300) pool_size: int = 10 max_overflow: int = 20 + # SSL/TLS 모드: ""/"disable"=미사용(로컬), "require"/"verify-ca"/"verify-full"=관리형 DB(RDS/Aurora/Azure). + sslmode: str = "" class JwtToken(ConfigModel): diff --git a/negodata/backend/config/server_configs.py b/negodata/backend/config/server_configs.py index fed6d5c..73201f3 100644 --- a/negodata/backend/config/server_configs.py +++ b/negodata/backend/config/server_configs.py @@ -19,3 +19,21 @@ web_server_config: WebServerConfig = configs.get(WebServerConfig) log_config: LogConfig = configs.get(LogConfig) main_db_config: MainDBConfig = configs.get(MainDBConfig) jwt_token_config: JwtToken = configs.get(JwtToken) + + +# DB 접속 env override (config.local.toml 유지, 도커에서 host 만 교체). 로컬은 env 미설정 → toml 그대로. +def _apply_db_env_override(cfg: MainDBConfig): + h = os.environ.get("DB_HOST") + if h: + cfg.write_host = cfg.read_host = h + if os.environ.get("DB_PORT"): + cfg.write_port = cfg.read_port = int(os.environ["DB_PORT"]) + if os.environ.get("DB_USER"): + cfg.write_id = cfg.read_id = os.environ["DB_USER"] + if os.environ.get("DB_PASSWORD"): + cfg.write_pw = cfg.read_pw = os.environ["DB_PASSWORD"] + if os.environ.get("DB_NAME"): + cfg.name = os.environ["DB_NAME"] + + +_apply_db_env_override(main_db_config)