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:
@@ -66,12 +66,17 @@ export function useDocumentSignatures() {
|
||||
}
|
||||
}
|
||||
|
||||
async function sign(signatureId, { hashDocumento = null } = {}) {
|
||||
async function sign(signatureId, { hashDocumento = null, tenantId = null } = {}) {
|
||||
loading.value = true;
|
||||
error.value = '';
|
||||
try {
|
||||
const updated = await signByPortal(signatureId, hashDocumento);
|
||||
const idx = signatures.value.findIndex(s => s.id === signatureId);
|
||||
// schema-per-tenant: a assinatura vive no schema do tenant. Resolve o
|
||||
// tenant_id da própria linha (list_my_signatures retorna tenant_id) ou
|
||||
// do parâmetro explícito.
|
||||
const row = signatures.value.find(s => (s.signature_id ?? s.id) === signatureId);
|
||||
const tid = tenantId || row?.tenant_id;
|
||||
const updated = await signByPortal(tid, signatureId, hashDocumento);
|
||||
const idx = signatures.value.findIndex(s => (s.signature_id ?? s.id) === signatureId);
|
||||
if (idx >= 0) signatures.value.splice(idx, 1, updated);
|
||||
return updated;
|
||||
} catch (e) {
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user