Jak zbudować prywatne konsultacje wideo w Next.js

Konsultacja wideo wygląda jak jedna rozmowa, a w aplikacji Next.js to kilka funkcji. Ktoś rezerwuje pół godziny u lekarki, prawnika, korepetytorki albo doradcy, a ten termin od razu znika ze wszystkich stron, żeby nikt inny go nie zarezerwował. O umówionej porze osoba, która zarezerwowała termin, czeka, a specjalista, na jakiejkolwiek stronie akurat jest, widzi to i wpuszcza ją do środka. Wizyta to rozmowa wideo tych dwóch osób, do której nikt inny nie może dołączyć i której usługa przesyłająca ją nie może ani zobaczyć, ani usłyszeć. Obok wiadomości i pliki wizyty, przechowywane przez Twój własny serwer, a po niej notatki specjalisty.
Ten wpis omawia każdą część, której potrzebuje prawdziwa aplikacja, na pełnym, działającym przykładzie: Consultations, serwisie prywatnych konsultacji z naszego przewodnika po Next.js, w którym rezerwuje się specjalistów, czeka w poczekalni i spotyka z nimi na wideo. Możesz wypróbować go na żywo: bądź Avą, z wizytą u Leny, która jest właśnie otwarta, i kliknij I'm ready. Potem otwórz serwis w oknie prywatnym jako Lena, zobacz Avę w poczekalni i wpuść Avę do środka.
Czego potrzebuje konsultacja wideo
- Rezerwacji, która się trzyma. Dostępnych terminów na każdej stronie i terminu, który znika ze wszystkich stron, gdy tylko ktoś go zarezerwuje.
- Poczekalni. Osoba, która zarezerwowała termin, daje znać, że jest gotowa, a specjalista widzi to na każdej stronie, nie tylko na stronie wizyty.
- Wpuszczania. Tylko dwie osoby z tej wizyty, tylko w okolicach jej terminu, i osoba, która zarezerwowała termin, dopiero gdy specjalista ją wpuści.
- Prywatności. Kamery i mikrofonu szyfrowanych end-to-end, tak by serwery, które przesyłają rozmowę, przekazywały dalej to, czego nie mogą ani zobaczyć, ani usłyszeć.
- Wiadomości i plików obok, przechowywanych tam, gdzie trzymasz swoje dane, i notatek na później.
- Końca. Wizyta zamyka się po swoich pół godzinach i nikt już do niej nie wraca.
Wszystko to możesz zbudować sam, na WebRTC, serwerach mediów i własnym serwerze websocket. Albo oddajesz media, poczekalnię i ekran rozmowy chmurze czasu rzeczywistego, a Twój serwer zatrzymuje to, co naprawdę Twoje: rezerwacje, reguły i pliki. Tak właśnie działa Consultations, przez WordPlus Cloud.
Przykład: serwis konsultacji
Specjaliści oferują półgodzinne terminy: lekarka rodzinna, prawnik, korepetytorka, doradca. Osoby szukające porady rezerwują termin albo od razu rozmawiają ze specjalistą, który jest teraz dostępny. Każda wizyta ma własną stronę: samą wizytę, jej wiadomości i, po zakończeniu, notatki specjalisty. To aplikacja Next.js z App Routerem, Better Auth i Postgresem, a jej część czasu rzeczywistego to kilka plików. Pięć kroków buduje jej wizyty.
1. Terminy, które znikają po rezerwacji
Rezerwuje się przez server action. Serwer zapisuje rezerwację, a pół godziny, które jest już zajęte, odrzuca sama baza danych. Potem serwer ogłasza to przez WordPlus Cloud, dwa razy:
export async function bookVisit(expert: string, startsAt: number, topic: string): Promise<{ error: string }> {
const me = await currentUserId();
if (!me) return { error: 'Sign in first.' };
const booked = await book(me, expert, startsAt, topic);
if (typeof booked === 'string') return { error: booked };
await realtime.toUser(expert).emit('booked', { appointment: booked.id, name: booked.clientName, startsAt: booked.startsAt });
// Every page showing this expert's times takes this one away.
await realtime.to('experts').emit('changed');
redirect(`/appointments/${booked.id}`);
}toUser() dociera do każdej strony, którą specjalista ma otwartą, na każdym urządzeniu, z powiadomieniem o nowej wizycie. experts to pokój, do którego dołącza każda strona zalogowanej osoby i w którym mówi tylko serwer. Każda strona słyszy changed i prosi swój serwer o siebie ponownie, bez przeładowania:
useRoom('experts', { changed: () => router.refresh() });Kto właśnie przeglądał terminy tego specjalisty, widzi, jak pół godziny znika. Ten sam pokój daje znać stronom, gdy wizyta się zaczyna lub kończy, więc aktualne jest też to, kto jest teraz dostępny. WordPlus Cloud wie, które wizyty trwają (realtime.listCalls()), a serwis nie prowadzi do tego żadnej tabeli.
2. Poczekalnia
Każda wizyta ma poczekalnię, pokój obecności nazwany od niej:
realtime.room('waiting:{id}', { presence: true }, async (user, { id }) => mayChat(user, id));Wchodzą do niej tylko dwie osoby z tej wizyty. Gdy osoba, która zarezerwowała termin, kliknie I'm ready, jej strona dołącza do pokoju i zostaje w nim, dopóki strona jest otwarta. Strona specjalisty odczytuje, kto jest w środku:
function useWaiting(id: string, client: string): boolean {
return usePresence(`waiting:${id}`).some((member) => String(member.id) === client);
}Obecność idzie za stroną: wystarczy zamknąć kartę, by wyjść z poczekalni. A specjalista nie musi być na stronie wizyty. Serwer daje znać zdarzeniem waiting przez realtime.toUser(), i na każdej stronie pojawia się powiadomienie „Ava is in the waiting room”, które otwiera wizytę.
3. Wpuszczanie
Wizyta to rozmowa grupowa nazwana od wizyty, visit:81, zadeklarowana raz, według tego wzorca, razem ze swoją regułą:
realtime.groupCalls('visit:{id}', async (user, { id }) => mayJoinVisit(user, id), { encrypt: true });export async function mayJoinVisit(user: string | number | null, id: string): Promise<boolean> {
const appointment = await appointmentOf(id);
if (!appointment || !isParty(appointment, user) || !visitOpen(appointment)) return false;
return String(user) === appointment.expert || appointment.admittedAt !== null;
}Dwie osoby z tej wizyty i nikt inny, dopóki wizyta jest otwarta: od dziesięciu minut przed jej terminem do końca jej pół godziny, chyba że skończyła się wcześniej. Specjalista wchodzi, kiedy tylko wizyta jest otwarta, a osoba, która zarezerwowała termin, dopiero po wpuszczeniu. Wpuszczanie to server action, która oznacza wizytę i powiadamia tę osobę:
export async function letIn(id: string): Promise<boolean> {
const me = await currentUserId();
const appointment = me ? await admit(id, me) : null;
if (!appointment) return false;
await realtime.toUser(appointment.client).emit('admitted', { appointment: id, name: appointment.expertName });
return true;
}Od tej chwili reguła mówi tak, a czekająca strona sama wchodzi na wizytę. Gdy strona chce dołączyć, najpierw pyta własny serwer serwisu, pod /api/realtime/calls: to ten sam route handler, przez który przechodzą rozmowy serwisu randkowego (jak wdrożyć rozmowy wideo w Next.js). Reguła działa z sesją serwisu, a strona dostaje przepustkę na wizytę, podpisaną sekretem aplikacji, albo powód odmowy.
4. Wizyta szyfrowana end-to-end
{ encrypt: true } w regule szyfruje wizytę end-to-end. Każda przeglądarka szyfruje kamerę i mikrofon kluczem, który tworzy serwer serwisu i który dostają tylko ci, których reguła wpuszcza. Klucze zostają w serwisie i w obu przeglądarkach, a serwery mediów WordPlus Cloud przekazują dalej to, czego nie mogą ani zobaczyć, ani usłyszeć (jak działają serwery mediów).
Wizyta pojawia się na samej stronie wizyty, tam gdzie strona ją umieszcza:
<CallSpot rt={rt} call={`visit:${id}`} join={join} joinWith="microphone camera" className="h-[32rem] rounded-2xl">
{/* The waiting room as each of the two sees it, until the page joins */}
</CallSpot>Przed wizytą CallSpot pokazuje to, co jest w środku: poczekalnię tak, jak widzi ją każda z dwóch osób, z przyciskiem Let Ava in dla specjalisty. join wprowadza stronę na wizytę, gdy tylko specjalista wpuści czekającą osobę albo gdy czekająca osoba zostanie wpuszczona. joinWith="microphone camera" przy wejściu włącza jedno i drugie, bo na wizycie chce się od razu widzieć i słyszeć. Na innej stronie aplikacji wizyta trwa dalej w okienku w rogu i wraca tutaj, gdy strona wizyty znów się otworzy.
Wizyta też się kończy. Specjalista kończy ją przyciskiem, a serwis kończy ją po jej pół godzinach, zegarem ustawianym, gdy wizyta się zaczyna:
export async function endVisit(appointment: Appointment) {
await realtime.call(visitOf(appointment.id)).end().catch(() => {}); // not on, if nobody came in
await markEnded(appointment.id);
await realtime.toUser([appointment.expert, appointment.client]).emit('ended', { appointment: appointment.id });
await realtime.to('experts').emit('changed');
}end() wyprowadza obie osoby z rozmowy, reguła, widząc zakończoną wizytę, nikogo już nie wpuszcza, a obie strony pokazują, że wizyta się skończyła.
5. Wiadomości i pliki, a potem notatki
Każda wizyta ma rozmowę, pokój chat:{id}, tylko dla swoich dwóch osób, przed wizytą, w jej trakcie i po niej. Gotowy Chat pokazuje ją obok wizyty, ze wskaźnikiem pisania, potwierdzeniami przeczytania i plikami:
<Chat
rt={useRealtime()}
room={room}
messages={`/api/messages?room=${room}`}
upload="/api/files"
accept="image/*,.pdf,.doc,.docx,.txt"
maxFileSize={10 * 1024 * 1024}
/>Zdjęcie, skan czy umowa trafia do własnej trasy serwisu w chwili wybrania, a trasa pyta regułę pokoju, zanim plik zapisze:
if (!me || !appointment || !(file instanceof File) || !(await realtime.mayJoin(room, me))) {
return Response.json({ error: 'This file is not for this conversation.' }, { status: 403 });
}Serwis przechowuje plik we własnej bazie danych i otwiera go tylko tym dwóm osobom. WordPlus Cloud przenosi tylko wiadomość, która mówi, gdzie plik leży, zapieczętowaną kluczem, który ma serwis, i nigdy sam plik. Serwis zapisuje też każdą wiadomość, z webhooka message, więc rozmowa zostaje dla obu osób także po wizycie. Po zakończeniu wizyty specjalista pisze notatki, serwis je zapisuje, a strona osoby, która zarezerwowała termin, od razu je pokazuje, znowu przez toUser().
Co jest w zestawie
- Rozmowa od razu. Osoba szukająca porady prosi dostępnego teraz specjalistę o rozmowę od razu. Strona specjalisty pokazuje prośbę z minutą na jej przyjęcie, a obie osoby wchodzą prosto na nową wizytę.
- Kto jest dostępny, aktualne na każdej stronie. Specjalista na wizycie jest pokazany jako zajęty, według
realtime.listCalls(), bez Twojej tabeli. - Online czy nie. Poczekalnia pokazuje, czy specjalista jest już online, przez
useOnline(). - Imiona i twarze z Twojej aplikacji. Każdy kafelek i każda wiadomość pokazują imię i zdjęcie tak, jak podaje je
profileserwisu, bez listy użytkowników trzymanej w WordPlus Cloud.
Wypróbuj, a potem zbuduj własny
W demo na żywo specjaliści i osoby szukające porady należą tylko do Ciebie przez jeden dzień, więc tego, co tam napiszesz albo z kim się spotkasz, nie widzi nikt inny, a wizyta kończy się sama po 10 minutach. Pod każdą stroną How this page works pokazuje krok po kroku kod, który ją napędza, z wartościami strony, którą masz otwartą.
Przewodnik po prywatnych konsultacjach buduje cały serwis, z każdym plikiem jego części czasu rzeczywistego, rozmowy w Next.js pokazują wszystkie sposoby umieszczenia rozmowy we własnej aplikacji, a szyfrowanie wyjaśnia, co jest zapieczętowane i kto ma który klucz. Jeśli zamiast dwóch osób spotyka się cały zespół, przeczytaj jak wdrożyć wideokonferencje w Next.js.
WordPlus Cloud for Apps ma cenę według jednoczesnych połączeń, od 1000 wzwyż, rozliczaną miesięcznie lub rocznie. Wizyta to rozmowa grupowa dwóch osób. Przy 1000 połączeń rozmowy grupowe nie są naliczane do 32 osób z wideo jednocześnie, czyli 16 wizyt, przy czym osoba z samym dźwiękiem liczy się za pół, a każde kolejne 1000 połączeń dodaje 12. Powyżej każda dodatkowa osoba z wideo zużywa kredyt na minutę, a rozmowy między dwiema osobami nie zużywają kredytów. Zobacz rozmowy wideo dla aplikacji albo cennik dla aplikacji. Chcesz zapytać o swoją integrację? Napisz do nas.