Files
Nexus/server/infrastructure/database/ssh_session_repo.py
T
Your Name 938d26927f P1/P2: 后端安全与稳定性修复
- Agent per-server API Key认证 (agent.py)
- JWT updated_at校验与会话超时 (auth_jwt.py)
- 服务器凭据Fernet加密存储+密码脱敏 (servers.py)
- WebSSH服务器级授权检查 (webssh.py)
- Config同步去重+回滚机制 (sync_engine_v2.py)
- DB层分页offset/limit (server_repo.py)
- session.py logger修复 + @property死代码清理
- 心跳刷入rollback误用修复 (heartbeat_flush.py)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 22:28:57 +08:00

86 lines
3.2 KiB
Python

"""Nexus — SSH Session & Command Log Repository (Async SQLAlchemy)"""
from typing import Optional, List
from datetime import datetime, timezone, timedelta
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from server.domain.models import SshSession, CommandLog
class SshSessionRepositoryImpl:
def __init__(self, session: AsyncSession):
self.session = session
async def get_by_id(self, session_id: str) -> Optional[SshSession]:
result = await self.session.execute(select(SshSession).where(SshSession.id == session_id))
return result.scalar_one_or_none()
async def get_active_by_admin(self, admin_id: int) -> List[SshSession]:
result = await self.session.execute(
select(SshSession)
.where(SshSession.admin_id == admin_id, SshSession.status == "active")
.order_by(SshSession.started_at.desc())
)
return list(result.scalars().all())
async def get_by_server(self, server_id: int, limit: int = 50) -> List[SshSession]:
result = await self.session.execute(
select(SshSession)
.where(SshSession.server_id == server_id)
.order_by(SshSession.started_at.desc())
.limit(limit)
)
return list(result.scalars().all())
async def create(self, ssh_session: SshSession) -> SshSession:
self.session.add(ssh_session)
await self.session.commit()
await self.session.refresh(ssh_session)
return ssh_session
async def close_session(self, session_id: str) -> Optional[SshSession]:
ssh_session = await self.get_by_id(session_id)
if ssh_session:
ssh_session.status = "closed"
ssh_session.closed_at = datetime.now(timezone.utc)
await self.session.commit()
await self.session.refresh(ssh_session)
return ssh_session
class CommandLogRepositoryImpl:
def __init__(self, session: AsyncSession):
self.session = session
async def get_by_session(self, session_id: str, limit: int = 200) -> List[CommandLog]:
result = await self.session.execute(
select(CommandLog)
.where(CommandLog.session_id == session_id)
.order_by(CommandLog.created_at.asc())
.limit(limit)
)
return list(result.scalars().all())
async def get_by_server(self, server_id: int, limit: int = 200) -> List[CommandLog]:
result = await self.session.execute(
select(CommandLog)
.where(CommandLog.server_id == server_id)
.order_by(CommandLog.created_at.desc())
.limit(limit)
)
return list(result.scalars().all())
async def create(self, log: CommandLog) -> CommandLog:
self.session.add(log)
await self.session.commit()
await self.session.refresh(log)
return log
async def count_by_admin(self, admin_id: int, hours: int = 24) -> int:
cutoff = datetime.now(timezone.utc) - timedelta(hours=hours)
result = await self.session.execute(
select(func.count(CommandLog.id))
.where(CommandLog.admin_id == admin_id, CommandLog.created_at >= cutoff)
)
return result.scalar_one() or 0