Sofya Developers

Quickstart — host

Quickstart — host

Pergunta: "eu sou o host, como mostro um QR e recebo evento?"

Pré-requisitos:

  • Harness rodando — setup-harness.md.
  • Uma credencial de transporte (credential): uma string não vazia, opaca para esta lib — a sessão logada da sua própria conta. Não existe credential: none.
  • Um guestUrl: a URL da sua própria tela de guest (a página/app que o guest vai abrir depois de escanear o QR ou digitar o código). Contra o harness local, qualquer URL absoluta serve como exemplo — em produção é o endereço real do seu guest.

Passo a passo

import { createHost, buildJoinUrl, type JoinTarget } from '@sofya-sdk/event-room';
import { generateQrImageDataUrl } from '@sofya-sdk/event-room/qr';

const { host, room } = await createHost({
  url: 'ws://localhost:8080/ws',
  credential: 'dev-host-credential',
  guestUrl: 'https://guest.example.com/join',
  room: {
    accessMode: 'approval_and_code',
  },
});

console.log('sala criada:', room.roomId, room.accessMode);

// room.joinPayload já é {roomId, accessMode, joinCode?} — o alvo que o guest
// precisa. buildJoinUrl nunca acrescenta caminho a guestUrl (ver nota abaixo)
// — só pendura o alvo na query string do endereço que você já escolheu.
const joinUrl = buildJoinUrl('https://guest.example.com/join', room.joinPayload as unknown as JoinTarget);
const qrDataUrl = await generateQrImageDataUrl(joinUrl);

// qrDataUrl é "data:image/png;base64,..." — use direto num <img src={qrDataUrl}>.
console.log('QR pronto:', qrDataUrl.slice(0, 40) + '...');

room.onJoinRequest((request) => {
  console.log('pedido de entrada de', request.displayName ?? '(sem nome)');
  // `request.displayName` é auto-declarado, nunca verificado (ver
  // seguranca.md) — decida com base na sua própria tela/fluxo, não só nele.
  const aprovado = true; // decisão do app
  if (aprovado) {
    room.approveJoin(request.id);
  } else {
    room.rejectJoin(request.id);
  }
});

room.session; // RoomSessionStateMachine — ver maquina-de-estados.md

// Quando terminar de usar a sala:
await room.close();
// Quando terminar de usar o host inteiro (nenhuma sala será reaberta):
await host.close();

Nota: buildJoinUrl nunca acrescenta caminho

buildJoinUrl(guestUrl, target) sempre usa guestUrl exatamente como o endereço final — nunca resolve um caminho relativo contra ele, nunca acrescenta segmento. Ele só chama searchParams.set para pendurar v/roomId/accessMode/joinCode? na query string. Se guestUrl já tiver caminho e query próprios (https://app.exemplo.com/entrar?tema=escuro), eles são preservados — só os quatro parâmetros reservados são adicionados/sobrescritos, e nenhum outro.

guestUrl não precisa ser http(s): um esquema de app próprio (myapp://join) é aceito do mesmo jeito, contanto que não esteja na lista de recusa abaixo.

A lista de esquemas recusados é fechada, e por quê

buildJoinUrl recusa exatamente cinco esquemas: javascript:, data:, blob:, vbscript:, file: — cada um executa conteúdo, embute conteúdo, ou não é um destino de aplicação de verdade. A lista não cresce por reflexo: about:, view-source:, chrome:, ms-settings: ficam de fora deliberadamente.

A razão: essa guarda defende contra erro do integrador (um guestUrl que virou data: por acidente), não contra um adversário — quem controla o guestUrl já controla o código do host. Por isso a lib também não pede (nem oferece) uma lista branca de esquemas. Essa validação acontece dentro de buildJoinUrl, é síncrona e falha antes de qualquer I/O — descubra o erro no instante mais cedo possível, não num reinício.

Próximo passo

receita-microfone.md — o fluxo completo com um guest de verdade do outro lado, incluindo o login que o guest precisa fazer antes de escanear.

Last updated on

On this page