Viewer Token e Webhook
Esta seção cobre endpoints de suporte à infraestrutura: geração de tokens temporários para o OHIF Viewer acessar o Orthanc via proxy CORS, e o webhook que o Orthanc chama quando um estudo DICOM é estabilizado.
POST /api/viewer/token
Gera um token temporário (10 min) que permite ao OHIF acessar o Orthanc através do proxy nginx autenticado.
Auth: auth:api
Request body
ID do estudo (tabela studies). Precisa pertencer ao tenant do usuário autenticado.
Response 200
{
"token": "550e8400-e29b-41d4-a716-446655440000"
}
Erros
| Status | Situação | Body |
|---|
| 401 | Token JWT inválido ou ausente | {"message": "Unauthenticated."} |
| 404 | Estudo não existe ou pertence a outro tenant | {"error": "Study not found"} |
Notas
- O token é um UUID armazenado em Redis sob a chave
viewer_token:{uuid} com TTL de 600 segundos (10 min)
- Payload armazenado no Redis:
{ user_id, tenant_id, unit_id } (JSON)
- O frontend passa o token na URL do OHIF (
?token=uuid); o app-config.js do OHIF patcha XHR/fetch para injetar o header X-Viewer-Token em cada requisição ao proxy
- Isolamento multi-tenant: como
Study::find() usa o scope BelongsToTenantAndUnit, estudos de outro tenant retornam 404 automaticamente
GET /api/viewer/token/validate
Valida um token de viewer e o tenant do estudo que está sendo acessado. Endpoint interno, chamado pelo nginx via auth_request antes de proxear requisições ao Orthanc.
Auth: público (não usa JWT — usa header custom X-Viewer-Token)
| Header | Descrição |
|---|
X-Viewer-Token | UUID do token gerado em POST /api/viewer/token (obrigatório) |
X-Original-URI | URI original que o nginx vai proxear (ex.: /dicom-web/studies/1.2.3.4.5). Usado para extrair o study_instance_uid e validar o tenant |
Response 200
O nginx só olha o código HTTP — o body é ignorado.
Erros
| Status | Situação | Body |
|---|
| 401 | Header X-Viewer-Token ausente | {"error": "Missing token"} |
| 401 | Token não encontrado no Redis (expirado ou inválido) | {"error": "Invalid or expired token"} |
| 403 | study_instance_uid extraído da URI não pertence ao tenant_id do token | {"error": "Forbidden"} |
Notas
- Extrai
study_instance_uid da URI via regex — suporta dois formatos DicomWeb:
- Path-based:
/dicom-web/studies/1.2.3.4.5
- Query-based:
/dicom-web/studies?0020000D=1.2.3.4.5 ou ?StudyInstanceUIDs=1.2.3.4.5
- Só valida
tenant_id — unit_id não é checado nesta camada
- Não é chamado diretamente pelo frontend — é apenas para o
auth_request do nginx
POST /api/orthanc/webhook
Recebe notificações do Orthanc quando um estudo DICOM é estabilizado (todas as imagens recebidas, 10s sem nova instância). Enfileira um job para processar o estudo.
Auth: público — protegido via header X-Webhook-Secret
| Header | Descrição |
|---|
X-Webhook-Secret | Secret compartilhado entre o hook Lua do Orthanc e o backend. Configurado via env ORTHANC_WEBHOOK_SECRET (tanto na raiz do projeto quanto em backend/.env) |
Request body
{
"ID": "orthanc_study_id_string",
"...": "outras chaves do evento Orthanc"
}
O único campo usado pelo backend é ID (ID do estudo no Orthanc).
Response 200
Erros
| Status | Situação | Body |
|---|
| 401 | Secret inválido ou ausente | {"error": "Unauthorized"} |
| 400 | Campo ID não fornecido no payload | {"error": "ID not provided"} |
Notas
- Dispara o job
ProcessStudyMetadata::dispatch($payload['ID']) assíncrono
- O job:
- Busca detalhes do estudo no Orthanc via REST API
- Extrai
RemoteAET e RemoteIP para identificar o aparelho emissor
- Resolve tenant/unit via
AETitle cadastrado em modalities
- Fallback para tenant de “quarentena” se o AET não estiver cadastrado
- Cria/atualiza o
Study em PostgreSQL
- Enfileira payload enxuto em
pacs_python_queue (Redis) para o worker Python processar séries, instâncias e thumbnail
- Implementação robusta: se o JSON vier malformado, tenta recuperar o
ID a partir das chaves do body (edge case documentado no controller)