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 existecredential: 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.
Nota: esquema customizado (deep link) também funciona
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