Fix PDF viewing: cookie auth fallback + .mjs MIME type

- /file and /pdf endpoints: make token optional, fall back to hnf_session
  cookie auth when no valid JWT token provided. Kitchen frontend passes the
  literal '__session__' compat shim so token-only auth always 401'd.
- Add Request injection so cookie-based fallback path can read hnf_session.
- nginx: add types block mapping .mjs → text/javascript so pdf.worker.min.mjs
  passes browser strict MIME check for ES module scripts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-12 21:34:51 +00:00
parent cd9e7dbfef
commit 742bc91b3b
2 changed files with 31 additions and 12 deletions

View file

@ -6,7 +6,7 @@ from datetime import date, timedelta
from decimal import Decimal from decimal import Decimal
from typing import Optional from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, BackgroundTasks from fastapi import APIRouter, Depends, HTTPException, Request, UploadFile, File, BackgroundTasks
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, text from sqlalchemy import select, func, text
@ -1233,17 +1233,25 @@ async def get_invoice_image(
@router.get("/{invoice_id}/file") @router.get("/{invoice_id}/file")
async def get_invoice_file( async def get_invoice_file(
invoice_id: int, invoice_id: int,
token: str, request: Request,
token: Optional[str] = None,
db: AsyncSession = Depends(get_db) db: AsyncSession = Depends(get_db)
): ):
"""Get invoice file (image or PDF) with token in query param - works through proxies""" """Get invoice file (image or PDF) — supports both cookie auth and ?token= JWT"""
from starlette.responses import Response from starlette.responses import Response
from services.file_archival_service import FileArchivalService from services.file_archival_service import FileArchivalService
# Verify token and get user # Try JWT token param first, fall back to hnf_session cookie
current_user = None
if token:
current_user = await get_current_user_from_token(token, db) current_user = await get_current_user_from_token(token, db)
if not current_user: if not current_user:
raise HTTPException(status_code=401, detail="Invalid token") try:
current_user = await get_current_user(request)
except HTTPException:
pass
if not current_user:
raise HTTPException(status_code=401, detail="Authentication required")
invoice = await get_invoice_or_404(invoice_id, current_user, db) invoice = await get_invoice_or_404(invoice_id, current_user, db)
@ -1282,17 +1290,23 @@ async def get_invoice_file(
@router.get("/{invoice_id}/pdf") @router.get("/{invoice_id}/pdf")
async def get_invoice_pdf( async def get_invoice_pdf(
invoice_id: int, invoice_id: int,
token: str, request: Request,
token: Optional[str] = None,
db: AsyncSession = Depends(get_db) db: AsyncSession = Depends(get_db)
): ):
"""Get invoice PDF with token in query param (for iframe embedding) - DEPRECATED, use /file""" """Get invoice PDF — DEPRECATED, use /file"""
from auth import get_current_user, require_cap_from_token
from starlette.responses import Response from starlette.responses import Response
# Verify token and get user current_user = None
if token:
current_user = await get_current_user_from_token(token, db) current_user = await get_current_user_from_token(token, db)
if not current_user: if not current_user:
raise HTTPException(status_code=401, detail="Invalid token") try:
current_user = await get_current_user(request)
except HTTPException:
pass
if not current_user:
raise HTTPException(status_code=401, detail="Authentication required")
invoice = await get_invoice_or_404(invoice_id, current_user, db) invoice = await get_invoice_or_404(invoice_id, current_user, db)

View file

@ -1,3 +1,8 @@
types {
text/javascript js mjs;
application/pdf pdf;
}
server { server {
listen 80; listen 80;
server_name _; server_name _;