Cybersecurity-Projects/PROJECTS/advanced/bug-bounty-platform/backend/app/admin/repository.py

315 lines
9.1 KiB
Python

"""
ⒸAngelaMos | 2026
repository.py
"""
from typing import Any
from collections.abc import Sequence
from datetime import UTC, datetime, timedelta
from uuid import UUID
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from config import ProgramStatus, ReportStatus, UserRole
from program.Program import Program
from report.Report import Report
from user.User import User
class AdminRepository:
"""
Repository for admin-specific database operations
"""
@classmethod
async def get_all_programs(
cls,
session: AsyncSession,
skip: int = 0,
limit: int = 20,
status_filter: ProgramStatus | None = None,
) -> Sequence[tuple[Program,
User,
int]]:
"""
Get all programs with company info and report count
"""
report_count_subq = (
select(
Report.program_id,
func.count(Report.id).label("report_count")
).group_by(Report.program_id).subquery()
)
query = (
select(
Program,
User,
func.coalesce(report_count_subq.c.report_count,
0).label("report_count")
).join(User,
Program.company_id == User.id).outerjoin(
report_count_subq,
Program.id == report_count_subq.c.program_id
)
)
if status_filter:
query = query.where(Program.status == status_filter)
result = await session.execute(
query.order_by(Program.created_at.desc()
).offset(skip).limit(limit)
)
return [tuple(row) for row in result.all()]
@classmethod
async def count_all_programs(
cls,
session: AsyncSession,
status_filter: ProgramStatus | None = None,
) -> int:
"""
Count all programs
"""
query = select(func.count()).select_from(Program)
if status_filter:
query = query.where(Program.status == status_filter)
result = await session.execute(query)
return result.scalar_one()
@classmethod
async def get_program_by_id(
cls,
session: AsyncSession,
program_id: UUID,
) -> Program | None:
"""
Get program by ID for admin operations
"""
return await session.get(Program, program_id)
@classmethod
async def get_all_reports(
cls,
session: AsyncSession,
skip: int = 0,
limit: int = 20,
status_filter: ReportStatus | None = None,
severity_filter: str | None = None,
) -> Sequence[tuple[Report,
Program,
User]]:
"""
Get all reports with program and researcher info
"""
query = (
select(Report,
Program,
User).join(Program,
Report.program_id == Program.id).join(
User,
Report.researcher_id == User.id
)
)
if status_filter:
query = query.where(Report.status == status_filter)
if severity_filter:
query = query.where(
Report.severity_submitted == severity_filter
)
result = await session.execute(
query.order_by(Report.created_at.desc()
).offset(skip).limit(limit)
)
return [tuple(row) for row in result.all()]
@classmethod
async def count_all_reports(
cls,
session: AsyncSession,
status_filter: ReportStatus | None = None,
severity_filter: str | None = None,
) -> int:
"""
Count all reports
"""
query = select(func.count()).select_from(Report)
if status_filter:
query = query.where(Report.status == status_filter)
if severity_filter:
query = query.where(
Report.severity_submitted == severity_filter
)
result = await session.execute(query)
return result.scalar_one()
@classmethod
async def get_report_by_id(
cls,
session: AsyncSession,
report_id: UUID,
) -> Report | None:
"""
Get report by ID for admin operations
"""
return await session.get(Report, report_id)
@classmethod
async def get_all_users_with_stats(
cls,
session: AsyncSession,
skip: int = 0,
limit: int = 20,
role_filter: UserRole | None = None,
) -> Sequence[tuple[User,
int,
int]]:
"""
Get all users with program and report counts
"""
program_count_subq = (
select(
Program.company_id,
func.count(Program.id).label("program_count")
).group_by(Program.company_id).subquery()
)
report_count_subq = (
select(
Report.researcher_id,
func.count(Report.id).label("report_count")
).group_by(Report.researcher_id).subquery()
)
query = (
select(
User,
func.coalesce(program_count_subq.c.program_count,
0).label("program_count"),
func.coalesce(report_count_subq.c.report_count,
0).label("report_count")
).outerjoin(
program_count_subq,
User.id == program_count_subq.c.company_id
).outerjoin(
report_count_subq,
User.id == report_count_subq.c.researcher_id
)
)
if role_filter:
query = query.where(User.role == role_filter)
result = await session.execute(
query.order_by(User.created_at.desc()
).offset(skip).limit(limit)
)
return [tuple(row) for row in result.all()]
@classmethod
async def count_all_users(
cls,
session: AsyncSession,
role_filter: UserRole | None = None,
) -> int:
"""
Count all users
"""
query = select(func.count()).select_from(User)
if role_filter:
query = query.where(User.role == role_filter)
result = await session.execute(query)
return result.scalar_one()
@classmethod
async def get_platform_stats(
cls,
session: AsyncSession,
) -> dict[str,
Any]:
"""
Get platform-wide statistics
"""
now = datetime.now(UTC)
month_ago = now - timedelta(days = 30)
total_users = await session.execute(
select(func.count()).select_from(User)
)
users_by_role = await session.execute(
select(User.role,
func.count()).group_by(User.role)
)
role_counts = {
str(role): count
for role, count in users_by_role.all()
}
total_programs = await session.execute(
select(func.count()).select_from(Program)
)
active_programs = await session.execute(
select(func.count()).select_from(Program).where(
Program.status == ProgramStatus.ACTIVE
)
)
total_reports = await session.execute(
select(func.count()).select_from(Report)
)
reports_by_status = await session.execute(
select(Report.status,
func.count()).group_by(Report.status)
)
status_counts = {
str(status): count
for status, count in reports_by_status.all()
}
total_bounties = await session.execute(
select(func.coalesce(func.sum(Report.bounty_amount),
0)).where(
Report.bounty_amount.isnot(None)
)
)
reports_this_month = await session.execute(
select(func.count()).select_from(Report).where(
Report.created_at >= month_ago
)
)
new_users_this_month = await session.execute(
select(func.count()
).select_from(User).where(User.created_at >= month_ago)
)
return {
"total_users": total_users.scalar_one(),
"total_researchers": role_counts.get("user",
0),
"total_companies": role_counts.get("company",
0),
"total_programs": total_programs.scalar_one(),
"active_programs": active_programs.scalar_one(),
"total_reports": total_reports.scalar_one(),
"reports_by_status": status_counts,
"total_bounties_paid": total_bounties.scalar_one(),
"reports_this_month": reports_this_month.scalar_one(),
"new_users_this_month": new_users_this_month.scalar_one(),
}