feat(l1): GET /accounts/me/seats endpoint for seat counter widget

Returns {engineer: SeatCheckResult, l1_tech: SeatCheckResult} for the
authenticated engineer's account. Powers the SeatCounterWidget UI in the
admin/users + account/users surfaces. Engineer+ access only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 13:02:20 -04:00
parent 8010da8745
commit 7056ed9e6d
2 changed files with 99 additions and 2 deletions

View File

@@ -23,8 +23,9 @@ from app.schemas.account import AccountResponse, AccountUpdate, AccountInviteCre
from app.schemas.subscription import SubscriptionResponse, PlanLimitsResponse, UsageResponse, SubscriptionDetails
from app.schemas.user import UserResponse, AccountRoleUpdate
from app.core.security import verify_password
from app.api.deps import get_current_active_user, require_account_owner
from app.services.seat_enforcement import check_seat_available
from app.api.deps import get_current_active_user, require_account_owner, require_engineer_or_admin
from app.services.seat_enforcement import check_seat_available, get_seat_usage
from app.schemas.seat_enforcement import SeatUsage
_SEAT_CHECKED_ROLES = frozenset({"engineer", "l1_tech"})
@@ -128,6 +129,41 @@ async def get_my_members(
return result.scalars().all()
@router.get("/me/seats", response_model=SeatUsage)
async def get_my_account_seat_usage(
db: Annotated[AsyncSession, Depends(get_db)],
current_user: Annotated[User, Depends(require_engineer_or_admin)],
):
"""Returns engineer + l1_tech seat-usage counts. Accessible to engineer+.
Powers the SeatCounterWidget on admin/users and account/users surfaces.
"""
account = await _load_account(db, current_user.account_id)
sub = await get_account_subscription(current_user.account_id, db)
if sub is None:
# No subscription → treat as unlimited; return live counts with no limit
from sqlalchemy import func
engineer_count = (await db.execute(
select(func.count(User.id))
.where(User.account_id == account.id)
.where(User.account_role == "engineer")
.where(User.is_active.is_(True))
)).scalar_one()
l1_count = (await db.execute(
select(func.count(User.id))
.where(User.account_id == account.id)
.where(User.account_role == "l1_tech")
.where(User.is_active.is_(True))
)).scalar_one()
from app.schemas.seat_enforcement import SeatCheckResult
return SeatUsage(
engineer=SeatCheckResult(available=True, current=engineer_count, limit=None, role="engineer"),
l1_tech=SeatCheckResult(available=True, current=l1_count, limit=None, role="l1_tech"),
)
engineer, l1_tech = await get_seat_usage(account, sub, db)
return SeatUsage(engineer=engineer, l1_tech=l1_tech)
@router.patch("/me", response_model=AccountResponse)
async def update_my_account(
data: AccountUpdate,