# Task 6 Report: Endpoint stato/download/trigger PDF

## File creato
- `app/src/routes/api/dvr/[id]/pdf/+server.ts` (66 righe)

## Implementazione

### Modalità GET status
`?mode=status` → `200 { state, generatoIl }` da `getPdfStatus`.

### Modalità GET download
- `ready`: serve sempre il PDF da R2.
- `stale + ?allowStale=1 + r2Key presente`: serve la versione precedente.
- Tutti gli altri casi (generating, error, missing, stale senza allowStale, stale senza r2Key): `409 { state }`.
- `Content-Disposition: attachment; filename="{codiceFisico}_DVR.pdf"` con `codiceFisico` da `agenzie`.

### Modalità POST trigger
- Se `state === 'generating'`: `202 { state: 'generating' }` senza ri-triggerare (niente doppio dispatch).
- Altrimenti: `markGenerating(id, currentHash)` + `triggerPdfGeneration(id)` → `202 { state: 'generating' }`.

### 400 per ID non numerico
Entrambi GET e POST validano `parseInt` con guard `Number.isNaN`.

## Adattamento getObject / streaming

`getObject` in `app/src/lib/server/r2.ts:39` ritorna `{ body: ReadableStream; contentType: string | undefined; size: number | undefined } | null`.

Il pattern è copiato esattamente da `app/src/routes/api/allegati/[id]/+server.ts:41`:
```ts
return new Response(obj.body, { headers: { 'Content-Type': ..., ... } });
```
`obj.body` è già un `ReadableStream` (via `transformToWebStream()`), passato direttamente a `new Response()`. Nessuna conversione necessaria.

Per i PDF non è previsto il fallback locale (`storage/`) né il `Cache-Control` (il PDF viene sempre rigenerato dal job GitHub Actions, non deve essere cached dal browser).

## Output npm run check
```
COMPLETED 1107 FILES 0 ERRORS 3 WARNINGS 1 FILES_WITH_PROBLEMS
```
I 3 warning preesistenti sono tutti in `ValutazioneCard.svelte` (state_referenced_locally). 0 errori su file nuovo.

## Commit
`3666b9d` — PDF: endpoint stato/download/trigger

## Concern
Nessun concern bloccante. Note:
- L'endpoint non ha guardia auth (`locals.user`): coerente col brief (la UI gestisce l'auth a livello route Svelte). Da verificare se il team vuole aggiungere protezione server-side.
- `Content-Length` non viene propagato (R2 lo fornisce via `obj.size` ma non viene passato nell'header). Non causa problemi funzionali ma impedisce la progress bar nel browser durante il download. Facile da aggiungere in seguito se richiesto.

## Fix post-review

**Minor #1 — guardia 401 in-handler**: aggiunto `if (!locals.user) throw error(401, 'Unauthorized')` all'inizio di GET e POST (con `locals` nei parametri del handler). Coerente con il pattern di `api/allegati/[id]/+server.ts:18`. Un fetch con sessione scaduta ora riceve 401 pulito invece di 302→HTML.

**Minor #2 — ordine trigger/markGenerating**: nel POST, invertito l'ordine delle chiamate. Ora: `triggerPdfGeneration(id)` prima, `markGenerating(id, currentHash)` dopo. Rationale: se il dispatch fallisce (es. `GITHUB_DISPATCH_TOKEN` assente), `markGenerating` non viene chiamato → lo stato rimane `stale`/`missing` → la UI mostra il bottone per riprovare. Il caso inverso (markGenerating fallisce dopo trigger riuscito) si auto-sana: la GitHub Action porta lo stato a `ready` a completamento.

**Output npm run check post-fix**:
```
COMPLETED 1107 FILES 0 ERRORS 3 WARNINGS 1 FILES_WITH_PROBLEMS
```
I 3 warning preesistenti in `ValutazioneCard.svelte` invariati. 0 errori.

**Commit**: `PDF: endpoint — trigger prima di markGenerating, guardia 401 in-handler`
