원클릭으로
route-analysis
Use this skill to analyse, audit, or modify HTTP and WebSocket routes in VoxBento. All routes live in `portal/routers/`.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use this skill to analyse, audit, or modify HTTP and WebSocket routes in VoxBento. All routes live in `portal/routers/`.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use this skill to evaluate proposed architecture changes against VoxBento's design principles.
Use this skill to find files, understand module ownership, and locate code in VoxBento.
Use this skill for security reviews of VoxBento code. Covers OWASP Top 10 and VoxBento-specific threat model.
Use this skill for tasks involving transcription providers, caption streaming, or the audio pipeline. Reference: `portal/transcription/`, [TRANSCRIPTION_MAP.md](../../context/TRANSCRIPTION_MAP.md).
Use this skill to analyse, audit, or modify database models, migrations, and CRUD helpers. Reference: `portal/models.py`, `portal/database.py`, `alembic/versions/`.
Use this skill to review deployments, validate production readiness, or assist with deployment procedures. Reference: `DEPLOYMENT_GUIDE.md`, `docker-compose.yml`, `Caddyfile`, `Dockerfile`.
| name | route-analysis |
| description | Use this skill to analyse, audit, or modify HTTP and WebSocket routes in VoxBento. All routes live in `portal/routers/`. |
Use this skill to analyse, audit, or modify HTTP and WebSocket routes in VoxBento. All routes live in
portal/routers/.
| Prefix | Type | Auth |
|---|---|---|
/ | Public pages | None or cookie-optional |
/interpreter/* | Booth pages | session_token or user_token cookie |
/listener/* | Listener pages | session_token or user_token cookie |
/join/* | Invite redemption | None (token in path) |
/register, /login, /logout, /account | User auth | None / user_token |
/api/* | REST API | Optional Bearer JWT or ?token= |
/admin/* | Admin panel | admin_token or user_token with is_admin |
/ws/booth/* | WebSocket coordination | Cookies + optional ?token= |
/ws/captions/* | Caption WebSocket | None |
/static/* | Static assets | None |
/healthz | Health check | None |
payload = get_booth_session(request) # checks session_token then user_token
if payload is None:
return safe_redirect(url=f'/login?next={path}', ...)
granted_role = await resolve_booth_role(payload, booth_id)
@app.get('/admin/...', dependencies=[Depends(require_admin)])
require_admin checks user_token (is_admin=True or event_admin membership) then falls back to admin_token.
_require_access(credentials, token) # passes if booth_access_token is unset
session_token then user_token.resolve_booth_role(payload, booth_id).session_token.event_slug + language_code must match the booth.portal/auth.py)resolve_booth_role(payload, booth_id) returns the most privileged applicable role:
super_admin — if is_admin=True in user token.event_admin — if user has EventMembership.role == 'event_admin' for the booth's event.BoothMembership for the specific booth.EventMembership for the booth's event.session_token — but only if event_slug + language_code match.None if no applicable role found → 403 on page routes.All redirects MUST use safe_redirect(url, status_code):
def safe_redirect(url: str, status_code: int) -> RedirectResponse:
url = url.replace('\\', '').strip()
parsed = urlparse(url)
if url and not parsed.netloc and not parsed.scheme and url.startswith('/'):
return RedirectResponse(url=url, status_code=status_code)
return RedirectResponse(url='/', status_code=status_code) # fallback
Never call RedirectResponse(url=user_input) directly.
portal/routers/.safe_redirect for all redirects.templates.TemplateResponse(request, 'template.html', context) for HTML.HTTPException for errors — never return raw error strings.tests/test_fastapi_app.py.| Endpoint | Key behavior |
|---|---|
GET /join/{token} | Validates + redeems invite token; sets session_token cookie; redirects to booth or listener |
GET /interpreter/{event_slug}/{language_code} | Resolves Jitsi URL from DB, relay WHEP, creates MediaMTX path; passes granted_role to template |
GET /api/events/{slug}/booths/{lang}/whip-url | Validates event ownership + active-interpreter status before returning WHIP URL |
WS /ws/booth/{booth_id} | Full WebSocket lifecycle; role never from client; scope validated; disconnect cleans up participant |
WS /ws/captions/{booth_id} | Open subscription; receives caption + booth:state; used by listener page |
POST /admin/.../.../transcription-settings | Updates DB config; if booth live, stops and restarts transcription worker |
safe_redirect → open redirect risk.data['role'] used directly from WS message → role injection vulnerability.token path param passed directly to DB query without validation → injection risk (mitigated by redeem_invite_token validation).next_url / next query param used in redirect without safe_redirect → open redirect.