F6.2 Lote F: RPCs anon/token resolvem tenant por token/slug + roteiam

DB (supabase_admin, manual/f6_2f_anon_token_rpcs.supabase_admin.sql):
- Documentos anon (token): validate_share_token, get_signable_document_by_token,
  sign_document_by_token resolvem tenant de document_share_links.tenant_id
  (public/F1b) -> set_config search_path; documents/document_signatures/
  document_access_logs no schema (RECORD em vez de %ROWTYPE; RETURNS
  document_signatures->jsonb; document_access_logs sem tenant_id).
  document_share_links continua public.
- sign_document_by_signature_id (paciente LOGADO, nao e tenant_member):
  +p_tenant_id via _tenant_schema_unchecked + autorizacao por LINHA (signatario_id
  =uid OU email OU documento do paciente). RETURNS->jsonb.
- agendador_dias/slots_disponiveis (anon): resolvem tenant de agendador_
  configuracoes.tenant_id (public/F1b); agenda/recurrence no schema;
  agendador_configuracoes/solicitacoes ficam public.
- match_patient_by_phone (edge service): _tenant_schema_unchecked + REVOKE de
  anon/authenticated, GRANT service_role.
- list_my_signatures: CROSS-TENANT -> fan-out por schema (RETURN QUERY EXECUTE
  por tenant_schemas, tenant_id injetado; document_share_links global).
- RPCs public-only (create_patient_intake_request, get_patient_intake_invite_info,
  issue_patient_invite, rotate_*, agendador_gerar_slug): SEM mudanca (F1b public).

Frontend: signByPortal(tenantId, signatureId, hash) + composable resolve
tenant_id da linha (list_my_signatures retorna tenant_id). Build passa.

Gotcha: paciente assinante NAO e tenant_member -> auth por linha, nao membership.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Leonardo
2026-06-13 15:51:22 -03:00
parent f079192698
commit 1243a12ced
3 changed files with 258 additions and 4 deletions
+3 -1
View File
@@ -174,10 +174,12 @@ export async function refuseSignature(signatureId) {
// só passa o hash SHA-256 do PDF (gerado via hashDocument()) pra
// garantir integridade do documento no momento da assinatura.
//
export async function signByPortal(signatureId, hashDocumento = null) {
export async function signByPortal(tenantId, signatureId, hashDocumento = null) {
if (!signatureId) throw new Error('ID da assinatura inválido.');
if (!tenantId) throw new Error('Tenant da assinatura não resolvido.');
const { data, error } = await supabase.rpc('sign_document_by_signature_id', {
p_tenant_id: tenantId,
p_signature_id: signatureId,
p_hash_documento: hashDocumento || null
});