180 lines
5.6 KiB
Python
180 lines
5.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from pydantic import BaseModel, Field
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from .auth import current_user
|
|
from .database import get_db
|
|
from .models import UrmiportReport, UrmiportReportGroup, UserAccount
|
|
from .urmiport_service import (
|
|
CreateUrmiportGroupPayload,
|
|
UpdateUrmiportGroupPayload,
|
|
build_urmiport_api_docs,
|
|
get_group_by_slug,
|
|
get_report,
|
|
get_user_group,
|
|
hash_password,
|
|
normalize_group_slug,
|
|
serialize_group,
|
|
serialize_report,
|
|
verify_password,
|
|
)
|
|
|
|
router = APIRouter(prefix="/api/v1/urmiport", tags=["urmiport"])
|
|
|
|
|
|
class UnlockReportRequest(BaseModel):
|
|
password: str = Field(min_length=1, max_length=120)
|
|
|
|
|
|
@router.get("/docs")
|
|
def urmiport_docs() -> dict:
|
|
return build_urmiport_api_docs()
|
|
|
|
|
|
@router.get("/groups")
|
|
def list_report_groups(
|
|
db: Session = Depends(get_db),
|
|
user: UserAccount = Depends(current_user),
|
|
) -> list[dict]:
|
|
groups = db.scalars(
|
|
select(UrmiportReportGroup)
|
|
.where(UrmiportReportGroup.user_id == user.id)
|
|
.order_by(UrmiportReportGroup.created_at.desc())
|
|
).all()
|
|
counts = dict(
|
|
db.execute(
|
|
select(UrmiportReport.group_id, func.count(UrmiportReport.id))
|
|
.where(UrmiportReport.user_id == user.id)
|
|
.group_by(UrmiportReport.group_id)
|
|
).all()
|
|
)
|
|
return [serialize_group(group, counts.get(group.id, 0)) for group in groups]
|
|
|
|
|
|
@router.post("/groups")
|
|
def create_report_group(
|
|
payload: CreateUrmiportGroupPayload,
|
|
db: Session = Depends(get_db),
|
|
user: UserAccount = Depends(current_user),
|
|
) -> dict:
|
|
try:
|
|
slug = normalize_group_slug(payload.report_group)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(exc)) from exc
|
|
|
|
existing = db.scalar(
|
|
select(UrmiportReportGroup).where(
|
|
UrmiportReportGroup.user_id == user.id,
|
|
UrmiportReportGroup.slug == slug,
|
|
)
|
|
)
|
|
if existing:
|
|
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="Report group already exists")
|
|
|
|
if payload.default_access == "password" and not payload.password:
|
|
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="password is required")
|
|
|
|
group = UrmiportReportGroup(
|
|
user_id=user.id,
|
|
slug=slug,
|
|
display_name=payload.display_name or slug,
|
|
default_access=payload.default_access,
|
|
password_hash=hash_password(payload.password) if payload.default_access == "password" else None,
|
|
id_mode=payload.id_mode,
|
|
)
|
|
db.add(group)
|
|
db.commit()
|
|
db.refresh(group)
|
|
return serialize_group(group, 0)
|
|
|
|
|
|
@router.patch("/groups/{slug}")
|
|
def update_report_group(
|
|
slug: str,
|
|
payload: UpdateUrmiportGroupPayload,
|
|
db: Session = Depends(get_db),
|
|
user: UserAccount = Depends(current_user),
|
|
) -> dict:
|
|
group = get_user_group(db, user, slug)
|
|
if payload.display_name is not None:
|
|
group.display_name = payload.display_name.strip() or group.slug
|
|
if payload.default_access is not None:
|
|
group.default_access = payload.default_access
|
|
if payload.default_access == "password":
|
|
if payload.password:
|
|
group.password_hash = hash_password(payload.password)
|
|
elif not group.password_hash:
|
|
raise HTTPException(status_code=422, detail="password is required")
|
|
else:
|
|
group.password_hash = None
|
|
elif payload.password:
|
|
group.password_hash = hash_password(payload.password)
|
|
if payload.id_mode is not None:
|
|
group.id_mode = payload.id_mode
|
|
db.commit()
|
|
db.refresh(group)
|
|
count = db.scalar(
|
|
select(func.count(UrmiportReport.id)).where(UrmiportReport.group_id == group.id)
|
|
) or 0
|
|
return serialize_group(group, count)
|
|
|
|
|
|
@router.delete("/groups/{slug}")
|
|
def delete_report_group(
|
|
slug: str,
|
|
db: Session = Depends(get_db),
|
|
user: UserAccount = Depends(current_user),
|
|
) -> dict:
|
|
group = get_user_group(db, user, slug)
|
|
db.delete(group)
|
|
db.commit()
|
|
return {"success": True}
|
|
|
|
|
|
@router.get("/groups/{slug}/reports")
|
|
def list_group_reports(
|
|
slug: str,
|
|
db: Session = Depends(get_db),
|
|
user: UserAccount = Depends(current_user),
|
|
) -> list[dict]:
|
|
group = get_user_group(db, user, slug)
|
|
reports = db.scalars(
|
|
select(UrmiportReport)
|
|
.where(UrmiportReport.group_id == group.id)
|
|
.order_by(UrmiportReport.updated_at.desc())
|
|
).all()
|
|
return [serialize_report(report, group) for report in reports]
|
|
|
|
|
|
@router.get("/view/{group_slug}/{report_id}")
|
|
def view_report_metadata(
|
|
group_slug: str,
|
|
report_id: str,
|
|
db: Session = Depends(get_db),
|
|
) -> dict:
|
|
group = get_group_by_slug(db, group_slug)
|
|
report = get_report(db, group, report_id)
|
|
return {
|
|
"report_group": group.slug,
|
|
"report_id": report.report_id,
|
|
"access": report.access_mode,
|
|
"requires_password": report.access_mode == "password",
|
|
"html": report.html_body if report.access_mode == "public" else None,
|
|
}
|
|
|
|
|
|
@router.post("/view/{group_slug}/{report_id}/unlock")
|
|
def unlock_report(
|
|
group_slug: str,
|
|
report_id: str,
|
|
payload: UnlockReportRequest,
|
|
db: Session = Depends(get_db),
|
|
) -> dict:
|
|
group = get_group_by_slug(db, group_slug)
|
|
report = get_report(db, group, report_id)
|
|
if report.access_mode != "password":
|
|
return {"html": report.html_body}
|
|
if not verify_password(payload.password, report.password_hash):
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid password")
|
|
return {"html": report.html_body}
|