Foto e assinatura do usuário — API
Lê, substitui e remove a foto de perfil e a imagem de assinatura de um usuário. É o caminho
direto de upload (multipart, um arquivo por vez, sem sessão prévia) mantido pelo pacote
user. Existe também um caminho versionado e mais recente para os mesmos dois tipos de
imagem — ver Ativos do usuário — que convive com este no código atual;
nenhuma migração entre os dois foi encontrada neste levantamento.
Funcionamento
- Toda operação passa primeiro por
IdentityUserBrandingAccessService.assertActorCanManageUserBranding: o próprio usuário sempre pode gerenciar sua identidade visual; para gerenciar a de um terceiro, o ator precisa ser um administrador de plataforma (isPlatformAdmin: true) — não há permissão nomeada intermediária nesta checagem. - Substituir (
PUT .../photoou.../signature): o arquivo é obrigatório (multipart/form-data, campofile); o backend inspeciona os bytes reais (assinatura de arquivo PNG/JPEG/WebP) e valida contra o tipo declarado — umContent-Typeque não bate com o conteúdo real é rejeitado. Faz upload para o armazenamento antes de trocar a referência no banco; se a gravação no banco falhar depois do upload, o arquivo recém-enviado é removido. - Depois de trocar a referência com sucesso, o arquivo anterior (se havia um) é apagado do armazenamento — nunca fica órfão.
- Ler (
GET .../branding): gera uma URL de download assinada e de curta duração (5 minutos) para cada imagem configurada; se não há foto/assinatura, o campo correspondente vemnull. - Remover (
DELETE .../photoou.../signature): zera a referência no banco e depois apaga o arquivo do armazenamento; não falha se já não havia imagem configurada.
Endpoints
| Método | Rota | Descrição |
|---|---|---|
| GET | /v1/users/:userId/branding | Lê as URLs atuais de foto e assinatura |
| PUT | /v1/users/:userId/photo | Substitui a foto do usuário |
| PUT | /v1/users/:userId/signature | Substitui a assinatura do usuário |
| DELETE | /v1/users/:userId/photo | Remove a foto do usuário |
| DELETE | /v1/users/:userId/signature | Remove a assinatura do usuário |
Versão: v1
Swagger: Identity — User administration and profile · Rota (Dev): http://localhost:3000/v1/users/:userId/photo
Permissões
| Rota | Guards | Regra de acesso |
|---|---|---|
GET/PUT/DELETE .../photo, .../signature, GET .../branding | IdentityOAuthAccessTokenGuard | o próprio usuário, ou um administrador de plataforma (isPlatformAdmin: true) para gerenciar a de terceiros |
Headers
| Header | Obrigatório | Descrição |
|---|---|---|
Authorization | Sim | Bearer <access_token> |
Content-Type | Sim (PUT) | multipart/form-data |
Path parameters
| Nome | Tipo | Obrigatório | Descrição |
|---|---|---|---|
userId | uuid | Sim | Usuário dono da imagem |
Body
PUT .../photo / .../signature: formulário multipart com um único campo:
| Campo | Tipo | Obrigatório | Validação |
|---|---|---|---|
file | binário | Sim | até 5 MB (foto) / 2 MB (assinatura); PNG/JPEG/WebP com bytes reais coerentes com o Content-Type declarado |
Response
200 — GET .../branding (IdentityUserBrandingResponse):
json{ "photo_url": "https://storage.exemplo.com/users/photo.jpg", "signature_url": null }
200 — demais operações (IdentityUserChangeResponse):
json{ "message": "User photo updated." }
Erros
| Classe de erro | errorCode | Status | Quando ocorre |
|---|---|---|---|
ForbiddenAction | IDENTITY_USER_BRANDING_FORBIDDEN | 403 | ator sem permissão para gerenciar/ler a identidade visual do alvo |
IdentityUserNotFoundError | IDENTITY_USER_NOT_FOUND | 404 | usuário alvo inexistente, excluído ou inativo |
| ValidationError | IDENTITY_USER_BRANDING_FILE_REQUIRED | 400 | file ausente ou vazio |
| — (validação de imagem) | — | 400 | arquivo não reconhecido como PNG/JPEG/WebP válido, ou Content-Type não corresponde aos bytes |
| — | IDENTITY_INVALID_OAUTH_ACCESS_TOKEN | 401 | token ausente, inválido, expirado ou revogado |
Regras de negócio
| ID | Regra | Comportamento esperado |
|---|---|---|
| RN-01 | Substituir nunca deixa arquivo órfão | upload primeiro; se a troca de referência falhar, o novo arquivo é removido; se tiver sucesso, o arquivo antigo é removido |
| RN-02 | Limites de tamanho por tipo | 5 MB para foto, 2 MB para assinatura, aplicados tanto no FileInterceptor do NestJS quanto na validação de domínio |
| RN-03 | Download nunca expõe URL permanente | toda leitura gera uma URL assinada nova, válida por 5 minutos |
| RN-04 | Remover é idempotente | remover uma imagem que já não existe não falha |
Compliance
| Órgão / norma | Exigência | Como a rota atende |
|---|---|---|
| LGPD | armazenamento seguro e acesso restrito | download só via URL assinada de curta duração, nunca link direto e permanente |
| HIPAA (parcial) | transmissão segura e trilha de troca | upload via multipart/form-data sobre TLS; toda troca/remoção gera evento de auditoria |
| ANVISA | integridade da assinatura associada ao laudo | evento de auditoria registra a troca; a associação da assinatura ao laudo em si pertence ao domínio de Laudo, fora deste módulo |
Variáveis de ambiente
Nenhuma específica a esta rota (usa a configuração geral de armazenamento do módulo identity).
Requisitos não funcionais
| Requisito | Definição |
|---|---|
| Idempotência | DELETE é idempotente; PUT sempre gera uma nova versão do arquivo |
| Auditoria | Sim — identity.user.photo.updated/.removed, identity.user.signature.updated/.removed |
Relacionado
- 🖥️ Tela:
A confirmar — responsável: time de frontend; data: 24/09/2026. - 📂 Módulo: Usuário
- 🖼️ Ativos do usuário — caminho versionado que também cobre
PROFILE_PHOTO/SIGNATURE_IMAGE