1. run.sh: start/stop/restart/status/logs/build/test - PID file management in .pids/ - Colored output, health check wait, log files - Auto alembic migrate on start 2. DB Connector (replaces mock aiomysql): - MySQL: aiomysql (connect/query/dml/test) - PostgreSQL: asyncpg (connect/query/dml/test) - SQLite: sqlite3 via executor (connect/query/dml/test) - Unified interface: DBConnector.test_connection/execute_query/execute_dml - Connection test returns version + latency 3. GitLab CI/CD pipeline: - test: backend pytest + frontend build - build: Docker images (manual trigger) - deploy: SSH deploy (manual trigger) - Cache: pip + node_modules Tests: 128 passing, frontend build: OK
50 lines
1.6 KiB
Python
50 lines
1.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.database import get_db
|
|
from app.core.security import get_current_user
|
|
from app.models.user import User
|
|
from app.models.instance import Instance
|
|
from app.models.audit_log import AuditLog
|
|
from app.schemas.query import QueryRequest, QueryResult
|
|
from app.services.db_connector import DBConnector
|
|
from app.core.crypto import decrypt_password
|
|
|
|
router = APIRouter(prefix="/query", tags=["SQL 查询"])
|
|
|
|
|
|
@router.post("", response_model=QueryResult)
|
|
async def execute_query(
|
|
req: QueryRequest,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
if current_user.role == "guest":
|
|
raise HTTPException(status_code=403, detail="访客无权执行查询")
|
|
|
|
result = await db.execute(select(Instance).where(Instance.id == req.instance_id))
|
|
instance = result.scalar_one_or_none()
|
|
if not instance:
|
|
raise HTTPException(status_code=404, detail="数据源不存在")
|
|
|
|
# Decrypt password for query execution
|
|
instance.password = decrypt_password(instance.password)
|
|
|
|
try:
|
|
query_result = await DBConnector.execute_query(instance, req.database, req.sql, current_user.id)
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
|
|
# Record audit log
|
|
log = AuditLog(
|
|
user_id=current_user.id,
|
|
action="QUERY",
|
|
target_type="query",
|
|
target_id=instance.id,
|
|
detail=req.sql[:500],
|
|
)
|
|
db.add(log)
|
|
|
|
return query_result
|