feat(deploy): 接入生产密钥与数据库迁移
Deploy Production / deploy (push) Successful in 1m42s

This commit is contained in:
yuxuanhui
2026-08-05 22:45:57 +08:00
parent d7d5ed8979
commit afdc5ca909
9 changed files with 139 additions and 33 deletions
+4 -3
View File
@@ -5,12 +5,13 @@ from logging.config import fileConfig
from alembic import context
from sqlalchemy import engine_from_config, pool
from zhixing_server.bootstrap.config import get_settings
from zhixing_server.bootstrap.config import get_settings, sqlalchemy_database_url
from zhixing_server.modules.market_data.infrastructure.schema import metadata
config = context.config
settings = get_settings()
config.set_main_option("sqlalchemy.url", settings.database_url.replace("%", "%%"))
sqlalchemy_url = sqlalchemy_database_url(settings.database_url)
config.set_main_option("sqlalchemy.url", sqlalchemy_url.replace("%", "%%"))
if config.config_file_name is not None:
fileConfig(config.config_file_name)
@@ -21,7 +22,7 @@ def run_migrations_offline() -> None:
"""Run migrations without opening a database connection."""
context.configure(
url=settings.database_url,
url=sqlalchemy_url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
@@ -31,6 +31,31 @@ class Settings(BaseSettings):
)
def sqlalchemy_database_url(database_url: str) -> str:
"""Add SQLAlchemy's Psycopg 3 driver to a PostgreSQL URL.
``Settings.database_url`` remains a plain ``postgresql://`` URL because
Psycopg 3 accepts that form directly. Alembic and SQLAlchemy need the
explicit ``postgresql+psycopg://`` dialect, while the rest of the URL
(including query parameters such as ``sslmode``) must remain unchanged.
Args:
database_url: PostgreSQL connection URL from the process environment.
Returns:
The URL with SQLAlchemy's Psycopg 3 dialect prefix.
Raises:
ValueError: If the URL does not use a supported PostgreSQL scheme.
"""
if database_url.startswith("postgresql+psycopg://"):
return database_url
if database_url.startswith("postgresql://"):
return "postgresql+psycopg://" + database_url.removeprefix("postgresql://")
raise ValueError("database_url must use postgresql:// or postgresql+psycopg://")
@lru_cache
def get_settings() -> Settings:
"""Return one immutable-by-convention configuration object per process.
@@ -6,17 +6,24 @@ from alembic import command
from alembic.config import Config
from sqlalchemy import Engine, create_engine, inspect
from zhixing_server.bootstrap.config import get_settings, sqlalchemy_database_url
@pytest.mark.integration
def test_postgres_migration_creates_market_data_contract() -> None:
def test_postgres_migration_creates_market_data_contract(
monkeypatch: pytest.MonkeyPatch,
) -> None:
database_url = os.getenv("ZHIXING_TEST_DATABASE_URL")
if not database_url:
pytest.skip("set ZHIXING_TEST_DATABASE_URL to run PostgreSQL integration tests")
monkeypatch.setenv("ZHIXING_DATABASE_URL", database_url)
get_settings.cache_clear()
server_root = Path(__file__).parents[2]
config = Config(str(server_root / "alembic.ini"))
config.set_main_option("sqlalchemy.url", database_url.replace("%", "%%"))
engine: Engine = create_engine(database_url)
sqlalchemy_url = sqlalchemy_database_url(database_url)
config.set_main_option("sqlalchemy.url", sqlalchemy_url.replace("%", "%%"))
engine: Engine = create_engine(sqlalchemy_url)
command.upgrade(config, "head")
try:
tables = set(inspect(engine).get_table_names())
@@ -29,3 +36,4 @@ def test_postgres_migration_creates_market_data_contract() -> None:
} <= tables
finally:
engine.dispose()
get_settings.cache_clear()
@@ -0,0 +1,26 @@
import pytest
from zhixing_server.bootstrap.config import sqlalchemy_database_url
def test_sqlalchemy_url_adds_psycopg_driver_and_preserves_query() -> None:
database_url = (
"postgresql://zhixing-system@1Panel-postgresql-5Fc7:5432/zhixing-system"
"?sslmode=require&connect_timeout=5"
)
assert sqlalchemy_database_url(database_url) == (
"postgresql+psycopg://zhixing-system@1Panel-postgresql-5Fc7:5432/zhixing-system"
"?sslmode=require&connect_timeout=5"
)
def test_sqlalchemy_url_does_not_duplicate_psycopg_driver() -> None:
database_url = "postgresql+psycopg://user@localhost:5432/db?sslmode=disable"
assert sqlalchemy_database_url(database_url) == database_url
def test_sqlalchemy_url_rejects_non_postgresql_scheme() -> None:
with pytest.raises(ValueError, match="postgresql"):
sqlalchemy_database_url("mysql://user@localhost/db")