feat(projects): automate project infrastructure provisioning
Generic Container CI/CD / test-build-publish (push) Successful in 1m13s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 1m13s

This commit is contained in:
2026-09-11 10:57:51 +08:00
parent 10a7a66a41
commit 90b02057bc
38 changed files with 2345 additions and 189 deletions
@@ -241,6 +241,67 @@ class MetadataRepository:
await self.session.refresh(project)
return project
async def create_provisioned_project(
self,
*,
name: str,
code: str,
description: str | None,
gs_workspace: str,
map_extent: dict,
creator_user_id: UUID,
business_dsn: str,
timescale_dsn: str,
pool_min_size: int = 1,
pool_max_size: int = 4,
) -> models.Project:
"""Atomically expose a fully provisioned project and both DB routes."""
business_secret = _encrypt_database_secret(business_dsn)
timescale_secret = _encrypt_database_secret(timescale_dsn)
project = models.Project(
id=uuid4(),
name=name,
code=code,
description=description,
gs_workspace=gs_workspace,
map_extent=map_extent,
status="active",
created_at=_utcnow(),
updated_at=_utcnow(),
)
records = (
project,
models.ProjectDatabase(
id=uuid4(),
project_id=project.id,
db_role="biz_data",
db_type="postgresql",
dsn_encrypted=business_secret,
pool_min_size=pool_min_size,
pool_max_size=pool_max_size,
),
models.ProjectDatabase(
id=uuid4(),
project_id=project.id,
db_role="iot_data",
db_type="timescaledb",
dsn_encrypted=timescale_secret,
pool_min_size=pool_min_size,
pool_max_size=pool_max_size,
),
models.UserProjectMembership(
id=uuid4(),
user_id=creator_user_id,
project_id=project.id,
project_role="member",
),
)
for record in records:
self.session.add(record)
await self.session.commit()
await self.session.refresh(project)
return project
async def update_project(
self,
project_id: UUID,