import logging from fastapi import APIRouter, Depends, HTTPException, status, Query, Path import psycopg from psycopg import AsyncConnection from sqlalchemy import text from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.ext.asyncio import AsyncSession from app.auth.project_dependencies import ( ProjectContext, get_project_context, get_project_pg_session, get_project_timescale_connection, get_metadata_repository, ) from app.auth.metadata_dependencies import get_current_metadata_user from app.core.config import settings from app.domain.schemas.metadata import ( GeoServerConfigResponse, ProjectMetaResponse, ProjectSummaryResponse, ) from app.infra.db.metadb.repositories.metadata_repository import MetadataRepository router = APIRouter() logger = logging.getLogger(__name__) @router.get("/meta/project", summary="获取项目元数据", description="获取当前项目的元数据和配置信息", response_model=ProjectMetaResponse) async def get_project_metadata( ctx: ProjectContext = Depends(get_project_context), metadata_repo: MetadataRepository = Depends(get_metadata_repository), ): """ 获取项目元数据 返回当前项目的完整元数据,包括项目基本信息和GeoServer配置 """ project = await metadata_repo.get_project_by_id(ctx.project_id) if not project: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Project not found" ) geoserver = await metadata_repo.get_geoserver_config(ctx.project_id) geoserver_payload = ( GeoServerConfigResponse( gs_base_url=geoserver.gs_base_url, gs_admin_user=geoserver.gs_admin_user, gs_datastore_name=geoserver.gs_datastore_name, default_extent=geoserver.default_extent, srid=geoserver.srid, ) if geoserver else None ) return ProjectMetaResponse( project_id=project.id, name=project.name, code=project.code, description=project.description, gs_workspace=project.gs_workspace, map_extent=project.map_extent, status=project.status, project_role=ctx.project_role, geoserver=geoserver_payload, ) @router.get("/meta/projects", summary="列出用户项目", description="获取当前用户有权限的所有项目列表", response_model=list[ProjectSummaryResponse]) async def list_user_projects( current_user=Depends(get_current_metadata_user), metadata_repo: MetadataRepository = Depends(get_metadata_repository), ): """ 列出用户的所有项目 返回当前用户有权限访问的项目摘要列表 """ try: projects = await metadata_repo.list_projects_for_user(current_user.id) except SQLAlchemyError as exc: logger.error( "Metadata DB error while listing projects for user %s", current_user.id, exc_info=True, ) raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"Metadata database error: {exc}", ) from exc return [ ProjectSummaryResponse( project_id=project.project_id, name=project.name, code=project.code, description=project.description, gs_workspace=project.gs_workspace, status=project.status, project_role=project.project_role, ) for project in projects ] @router.get("/meta/db/health", summary="检查数据库健康状态", description="检查项目数据库连接的健康状况") async def project_db_health( pg_session: AsyncSession = Depends(get_project_pg_session), ts_conn: AsyncConnection = Depends(get_project_timescale_connection), ): """ 检查数据库健康状态 检查PostgreSQL和TimescaleDB数据库的连接状态 """ try: await pg_session.execute(text("SELECT 1")) except SQLAlchemyError as exc: logger.error("Project PostgreSQL health check failed", exc_info=True) raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"Project PostgreSQL health check failed: {exc}", ) from exc try: async with ts_conn.cursor() as cur: await cur.execute("SELECT 1") except psycopg.Error as exc: logger.error("Project TimescaleDB health check failed", exc_info=True) raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=f"Project TimescaleDB health check failed: {exc}", ) from exc return {"postgres": "ok", "timescale": "ok"}