Formação — Edge Functions (PR-2)
Todas as functions seguem ADR-003 (body.params ?? body) e ADR-018 (Least Privilege).
especialista-modulos (novo)
Auth: requireRole('especialista'). CORS padrão _shared/cors.ts.
| Action | Payload | Retorno |
|---|---|---|
list | { curso_id } | CursoModulo[] |
create | { curso_id, titulo, descricao? } | CursoModulo |
update | { modulo_id, titulo?, descricao? } | CursoModulo |
delete | { modulo_id } | { ok: true } |
reorder | { curso_id, ordens: [{ modulo_id, ordem }] } | CursoModulo[] |
reorderusa two-phase (ordens negativas temporárias) para respeitarUNIQUE(curso_id, ordem).deletefalha (FK RESTRICT) se houver vídeos vinculados — UI deve mover/excluir aulas antes.
especialista-trilhas (novo)
Auth: requireRole('especialista').
| Action | Payload |
|---|---|
list | {} → Trilha[] (com cursos[] hidratado) |
create | { titulo, descricao? } |
update | { trilha_id, titulo?, descricao?, ativo? } |
delete | { trilha_id } |
attach_curso | { trilha_id, curso_id, ordem? } |
detach_curso | { trilha_id, curso_id } |
reorder | { ordens: [{ trilha_id, ordem }] } — two-phase |
cursos-imagens-signed-url (novo)
Auth: requireAnyRole(['especialista','coordenador']). Validação anti-traversal via _shared/cursos-imagens.ts::isSafeImagePath.
| Action | Payload | Retorno |
|---|---|---|
sign | { path, ttl? } (≤1h) | { signed_url, expires_at } |
especialista-cursos (estendida)
| Action novo/atualizado | Mudança |
|---|---|
create / update | Aceita imagem_url, video_intro_url, status. 22023 → 400. |
upload_imagem | { curso_id, path } → valida path e persiste cursos-imagens/<path>. |
update_status | { curso_id, status } (publicado | em_breve | rascunho). |
especialista-curso-videos (estendida)
createaceitamodulo_id(opcional — default = módulo de menorordem).- Ordem auto agora é por módulo (
UNIQUE(modulo_id, ordem)).
- Ordem auto agora é por módulo (
updateaceitamodulo_id(mover aula entre módulos).
coordenador-videos (estendida)
| Action | Payload | Retorno |
|---|---|---|
list_curso_completo | { curso_id } | { curso, modulos: [{ ..., aulas: [...] }] } |
Filtros aplicados pelo backend (coordenador é consumidor):
curso.status === 'rascunho'→ 404 (não enxerga).aulas.status !== 'publicado'→ omitidas.ativo = false→ omitidas.
Validação Zod por action (PR-6.5 hardening)
Todas as Edge Functions abaixo seguem ADR-003 — extractParams(body) = body.params ?? body aplicado antes de qualquer safeParse. O hook do frontend (invokeAction) envia { action, params: {...} }; tooling/curl envia flat. Ambos válidos.
| Edge function | Action | Schema Zod | Notas |
|---|---|---|---|
especialista-cursos | create | CreateCursoSchema | titulo obrigatório; categoria_visual enum; status default rascunho. |
especialista-cursos | update | UpdateCursoSchema | id UUID + partial. |
especialista-cursos | upload_imagem | UploadImagemSchema | mime ∈ {jpeg,png,webp}, base64 ≤ 5 MB. |
especialista-cursos | update_status | UpdateStatusSchema | status ∈ {publicado,em_breve,rascunho}. |
especialista-modulos | create/update/delete/reorder | inline | reorder é two-phase para respeitar UNIQUE (curso_id, ordem). |
especialista-curso-videos | create | CreateVideoSchema | modulo_id UUID obrigatório (FK NOT NULL desde PR-1). |
especialista-curso-videos | update | UpdateVideoSchema | partial; mantém FK RESTRICT se trocar modulo_id. |
especialista-trilhas | create/update/delete/attach_curso/detach_curso/reorder_cursos | inline | escopo global. |
coordenador-videos | list_curso_completo | ListCursoCompletoSchema | curso_id UUID; aplica filtros do §"Filtros aplicados pelo backend". |
coordenador-videos | list_trilhas | ListTrilhasSchema | sem parâmetros; retorna apenas ativo = true. |
cursos-imagens-signed-url | sign | inline (Zod) | TTL ≤ 3600 s; bucket privado cursos-imagens. |
Resposta de erro padrão (400):
{ "success": false, "error": "VALIDATION_ERROR", "details": { "fieldErrors": { "modulo_id": ["Invalid uuid"] } } }Gerada via schema.safeParse().error.flatten(). Cobertura: 43 testes de parser puros (deno test em cada __tests__/parser.test.ts), incluindo regressão explícita do Caso 21 (fatura_id: Required 2026-06-19) em cada função afetada.