Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions backend/app/api/audit_logs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import uuid

from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.database.session import get_db
from app.models.audit_log import AuditLogResponse, CreateAuditLogRequest
from app.repositories.audit_log_repository import AuditLogRepository

router = APIRouter(prefix="/audit-logs", tags=["audit-logs"])


@router.post("", response_model=AuditLogResponse, status_code=status.HTTP_201_CREATED)
async def create_audit_log(body: CreateAuditLogRequest, db: AsyncSession = Depends(get_db)) -> AuditLogResponse:
repo = AuditLogRepository(db)
return await repo.create(
action=body.action,
resource=body.resource,
user_id=body.user_id,
transaction_id=body.transaction_id,
details=body.details,
)


@router.get("", response_model=list[AuditLogResponse])
async def list_audit_logs(
user_id: uuid.UUID | None = Query(default=None),
db: AsyncSession = Depends(get_db),
) -> list[AuditLogResponse]:
repo = AuditLogRepository(db)
return await repo.get_all(user_id=user_id)


@router.get("/{log_id}", response_model=AuditLogResponse)
async def get_audit_log(log_id: uuid.UUID, db: AsyncSession = Depends(get_db)) -> AuditLogResponse:
repo = AuditLogRepository(db)
log = await repo.get_by_id(log_id)
if not log:
raise HTTPException(status_code=404, detail="Audit log not found")
return log


@router.delete("/{log_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_audit_log(log_id: uuid.UUID, db: AsyncSession = Depends(get_db)) -> None:
repo = AuditLogRepository(db)
log = await repo.get_by_id(log_id)
if not log:
raise HTTPException(status_code=404, detail="Audit log not found")
await repo.delete(log)
60 changes: 60 additions & 0 deletions backend/app/api/fraud_actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import uuid

from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.database.session import get_db
from app.models.fraud_action import CreateFraudActionRequest, FraudActionResponse, UpdateFraudActionRequest
from app.repositories.fraud_action_repository import FraudActionRepository

router = APIRouter(prefix="/fraud-actions", tags=["fraud-actions"])


@router.post("", response_model=FraudActionResponse, status_code=status.HTTP_201_CREATED)
async def create_fraud_action(
body: CreateFraudActionRequest, db: AsyncSession = Depends(get_db)
) -> FraudActionResponse:
repo = FraudActionRepository(db)
return await repo.create(
transaction_id=body.transaction_id, action_type=body.action_type, status=body.status
)


@router.get("", response_model=list[FraudActionResponse])
async def list_fraud_actions(
transaction_id: uuid.UUID | None = Query(default=None),
db: AsyncSession = Depends(get_db),
) -> list[FraudActionResponse]:
repo = FraudActionRepository(db)
if transaction_id:
return await repo.get_by_transaction(transaction_id)
return await repo.get_all()


@router.get("/{action_id}", response_model=FraudActionResponse)
async def get_fraud_action(action_id: uuid.UUID, db: AsyncSession = Depends(get_db)) -> FraudActionResponse:
repo = FraudActionRepository(db)
action = await repo.get_by_id(action_id)
if not action:
raise HTTPException(status_code=404, detail="Fraud action not found")
return action


@router.patch("/{action_id}", response_model=FraudActionResponse)
async def update_fraud_action(
action_id: uuid.UUID, body: UpdateFraudActionRequest, db: AsyncSession = Depends(get_db)
) -> FraudActionResponse:
repo = FraudActionRepository(db)
action = await repo.get_by_id(action_id)
if not action:
raise HTTPException(status_code=404, detail="Fraud action not found")
return await repo.update(action, action_type=body.action_type, status=body.status)


@router.delete("/{action_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_fraud_action(action_id: uuid.UUID, db: AsyncSession = Depends(get_db)) -> None:
repo = FraudActionRepository(db)
action = await repo.get_by_id(action_id)
if not action:
raise HTTPException(status_code=404, detail="Fraud action not found")
await repo.delete(action)
74 changes: 74 additions & 0 deletions backend/app/api/ip_reputations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import uuid

from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession

from app.database.session import get_db
from app.models.ip_reputation import CreateIpReputationRequest, IpReputationResponse, UpdateIpReputationRequest
from app.repositories.ip_reputation_repository import IpReputationRepository

router = APIRouter(prefix="/ip-reputations", tags=["ip-reputations"])


@router.post("", response_model=IpReputationResponse, status_code=status.HTTP_201_CREATED)
async def create_ip_reputation(
body: CreateIpReputationRequest, db: AsyncSession = Depends(get_db)
) -> IpReputationResponse:
repo = IpReputationRepository(db)
try:
return await repo.create(
ip=body.ip, status=body.status, failed_attempts=body.failed_attempts, risk_score=body.risk_score
)
except IntegrityError:
raise HTTPException(status_code=409, detail="IP already registered")


@router.get("", response_model=list[IpReputationResponse])
async def list_ip_reputations(db: AsyncSession = Depends(get_db)) -> list[IpReputationResponse]:
repo = IpReputationRepository(db)
return await repo.get_all()


@router.get("/by-ip/{ip}", response_model=IpReputationResponse)
async def get_by_ip(ip: str, db: AsyncSession = Depends(get_db)) -> IpReputationResponse:
repo = IpReputationRepository(db)
entry = await repo.get_by_ip(ip)
if not entry:
raise HTTPException(status_code=404, detail="IP not found")
return entry


@router.get("/{entry_id}", response_model=IpReputationResponse)
async def get_ip_reputation(entry_id: uuid.UUID, db: AsyncSession = Depends(get_db)) -> IpReputationResponse:
repo = IpReputationRepository(db)
entry = await repo.get_by_id(entry_id)
if not entry:
raise HTTPException(status_code=404, detail="IP not found")
return entry


@router.patch("/{entry_id}", response_model=IpReputationResponse)
async def update_ip_reputation(
entry_id: uuid.UUID, body: UpdateIpReputationRequest, db: AsyncSession = Depends(get_db)
) -> IpReputationResponse:
repo = IpReputationRepository(db)
entry = await repo.get_by_id(entry_id)
if not entry:
raise HTTPException(status_code=404, detail="IP not found")
return await repo.update(
entry,
risk_score=body.risk_score,
status=body.status,
failed_attempts=body.failed_attempts,
last_seen=body.last_seen,
)


@router.delete("/{entry_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_ip_reputation(entry_id: uuid.UUID, db: AsyncSession = Depends(get_db)) -> None:
repo = IpReputationRepository(db)
entry = await repo.get_by_id(entry_id)
if not entry:
raise HTTPException(status_code=404, detail="IP not found")
await repo.delete(entry)
64 changes: 64 additions & 0 deletions backend/app/api/otp_challenges.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import uuid

from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.database.session import get_db
from app.models.otp_challenge import CreateOtpChallengeRequest, OtpChallengeResponse, UpdateOtpChallengeRequest
from app.repositories.otp_challenge_repository import OtpChallengeRepository

router = APIRouter(prefix="/otp-challenges", tags=["otp-challenges"])


@router.post("", response_model=OtpChallengeResponse, status_code=status.HTTP_201_CREATED)
async def create_otp_challenge(
body: CreateOtpChallengeRequest, db: AsyncSession = Depends(get_db)
) -> OtpChallengeResponse:
repo = OtpChallengeRepository(db)
return await repo.create(
user_id=body.user_id,
transaction_id=body.transaction_id,
code_hash=body.code_hash,
channel=body.channel,
status=body.status,
max_attempts=body.max_attempts,
expires_at=body.expires_at,
)


@router.get("", response_model=list[OtpChallengeResponse])
async def list_otp_challenges(
user_id: uuid.UUID | None = Query(default=None),
db: AsyncSession = Depends(get_db),
) -> list[OtpChallengeResponse]:
repo = OtpChallengeRepository(db)
return await repo.get_all(user_id=user_id)


@router.get("/{challenge_id}", response_model=OtpChallengeResponse)
async def get_otp_challenge(challenge_id: uuid.UUID, db: AsyncSession = Depends(get_db)) -> OtpChallengeResponse:
repo = OtpChallengeRepository(db)
challenge = await repo.get_by_id(challenge_id)
if not challenge:
raise HTTPException(status_code=404, detail="OTP challenge not found")
return challenge


@router.patch("/{challenge_id}", response_model=OtpChallengeResponse)
async def update_otp_challenge(
challenge_id: uuid.UUID, body: UpdateOtpChallengeRequest, db: AsyncSession = Depends(get_db)
) -> OtpChallengeResponse:
repo = OtpChallengeRepository(db)
challenge = await repo.get_by_id(challenge_id)
if not challenge:
raise HTTPException(status_code=404, detail="OTP challenge not found")
return await repo.update(challenge, status=body.status, attempts=body.attempts, verified_at=body.verified_at)


@router.delete("/{challenge_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_otp_challenge(challenge_id: uuid.UUID, db: AsyncSession = Depends(get_db)) -> None:
repo = OtpChallengeRepository(db)
challenge = await repo.get_by_id(challenge_id)
if not challenge:
raise HTTPException(status_code=404, detail="OTP challenge not found")
await repo.delete(challenge)
72 changes: 72 additions & 0 deletions backend/app/api/risk_assessments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import uuid

from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy.ext.asyncio import AsyncSession

from app.database.session import get_db
from app.models.risk_assessment import CreateRiskAssessmentRequest, RiskAssessmentResponse, UpdateRiskAssessmentRequest
from app.repositories.risk_assessment_repository import RiskAssessmentRepository

router = APIRouter(prefix="/risk-assessments", tags=["risk-assessments"])


@router.post("", response_model=RiskAssessmentResponse, status_code=status.HTTP_201_CREATED)
async def create_risk_assessment(
body: CreateRiskAssessmentRequest, db: AsyncSession = Depends(get_db)
) -> RiskAssessmentResponse:
repo = RiskAssessmentRepository(db)
return await repo.create(
transaction_id=body.transaction_id,
risk_score=body.risk_score,
risk_level=body.risk_level,
decision=body.decision,
reason=body.reason,
)


@router.get("", response_model=list[RiskAssessmentResponse])
async def list_risk_assessments(
transaction_id: uuid.UUID | None = Query(default=None),
db: AsyncSession = Depends(get_db),
) -> list[RiskAssessmentResponse]:
repo = RiskAssessmentRepository(db)
if transaction_id:
return await repo.get_by_transaction(transaction_id)
return await repo.get_all()


@router.get("/{assessment_id}", response_model=RiskAssessmentResponse)
async def get_risk_assessment(
assessment_id: uuid.UUID, db: AsyncSession = Depends(get_db)
) -> RiskAssessmentResponse:
repo = RiskAssessmentRepository(db)
assessment = await repo.get_by_id(assessment_id)
if not assessment:
raise HTTPException(status_code=404, detail="Risk assessment not found")
return assessment


@router.patch("/{assessment_id}", response_model=RiskAssessmentResponse)
async def update_risk_assessment(
assessment_id: uuid.UUID, body: UpdateRiskAssessmentRequest, db: AsyncSession = Depends(get_db)
) -> RiskAssessmentResponse:
repo = RiskAssessmentRepository(db)
assessment = await repo.get_by_id(assessment_id)
if not assessment:
raise HTTPException(status_code=404, detail="Risk assessment not found")
return await repo.update(
assessment,
risk_score=body.risk_score,
risk_level=body.risk_level,
decision=body.decision,
reason=body.reason,
)


@router.delete("/{assessment_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_risk_assessment(assessment_id: uuid.UUID, db: AsyncSession = Depends(get_db)) -> None:
repo = RiskAssessmentRepository(db)
assessment = await repo.get_by_id(assessment_id)
if not assessment:
raise HTTPException(status_code=404, detail="Risk assessment not found")
await repo.delete(assessment)
Loading
Loading