Реалізація віртуальних кімнат для відеоконференцій на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація віртуальних кімнат для відеоконференцій на сайті
Середня
~3-5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка виртуальних комнат для відеоконференцій

Виртуальні комнати — це постійні простори з унікальними URL, куди учасники можуть заходити та виходити в будь-який час, як у фізичний переговорний зал. На відміну від звичайного звонку, комната існує постійно, не прив'язана до конкретної події, зберігає налаштування та історію.

Модель даних

CREATE TABLE virtual_rooms (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  slug VARCHAR(100) UNIQUE NOT NULL,   -- /room/team-standup
  name VARCHAR(255) NOT NULL,
  owner_id UUID REFERENCES users(id),
  organization_id UUID,
  access_type VARCHAR(50) DEFAULT 'invite_only',
  -- 'public' | 'organization' | 'invite_only'
  password_hash TEXT,
  max_participants INTEGER DEFAULT 20,
  enable_waiting_room BOOLEAN DEFAULT false,
  enable_recording BOOLEAN DEFAULT false,
  lobby_message TEXT,
  last_active_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE room_members (
  room_id UUID REFERENCES virtual_rooms(id),
  user_id UUID REFERENCES users(id),
  role VARCHAR(50) DEFAULT 'member',  -- 'host' | 'moderator' | 'member'
  can_always_join BOOLEAN DEFAULT true,
  PRIMARY KEY (room_id, user_id)
);

Постійна комната в LiveKit

async function getOrCreateVirtualRoom(slug: string): Promise<string> {
  const roomName = `virtual-${slug}`;

  try {
    await svc.getRoom(roomName);
    return roomName;
  } catch {
    // Створити з довгим timeout
    await svc.createRoom({
      name: roomName,
      emptyTimeout: 24 * 60 * 60,  // 24 години
      maxParticipants: 50,
    });
    return roomName;
  }
}

Лобі з очіканням одобрення

const lobbyParticipants = new Map<string, {
  userId: string;
  displayName: string;
  roomSlug: string;
  resolve: (allowed: boolean) => void;
}>();

app.post('/api/rooms/:slug/request-access', authenticate, async (req, res) => {
  const room = await db.virtualRooms.findBySlug(req.params.slug);
  if (!room) return res.status(404).end();

  const isMember = await db.roomMembers.isMember(room.id, req.user.id);

  if (!room.enable_waiting_room || isMember) {
    const token = generateRoomToken(req.params.slug, req.user);
    return res.json({ status: 'admitted', token });
  }

  const permission = await new Promise<boolean>((resolve) => {
    lobbyParticipants.set(req.user.id, {
      userId: req.user.id,
      displayName: req.user.name,
      roomSlug: req.params.slug,
      resolve,
    });

    io.to(`room-host-${room.id}`).emit('lobby_request', {
      userId: req.user.id,
      displayName: req.user.name,
    });

    setTimeout(() => resolve(false), 120_000);
  });

  if (permission) {
    const token = generateRoomToken(req.params.slug, req.user);
    res.json({ status: 'admitted', token });
  } else {
    res.json({ status: 'denied' });
  }
});

// Хост приймає/відхиляє
app.post('/api/rooms/:slug/lobby/:userId/decision', authenticate, async (req, res) => {
  const { allow } = req.body;
  const entry = lobbyParticipants.get(req.params.userId);
  if (!entry) return res.status(404).end();

  entry.resolve(allow);
  lobbyParticipants.delete(req.params.userId);
  res.json({ ok: true });
});

React компонент виртуальної комнати

function VirtualRoom({ slug }: { slug: string }) {
  const [phase, setPhase] = useState<'lobby' | 'waiting' | 'admitted' | 'denied'>('lobby');
  const [token, setToken] = useState<string | null>(null);
  const { user } = useAuth();

  const requestAccess = async () => {
    setPhase('waiting');

    const { status, token: t } = await fetch(
      `/api/rooms/${slug}/request-access`,
      { method: 'POST' }
    ).then(r => r.json());

    if (status === 'admitted') {
      setToken(t);
      setPhase('admitted');
    } else {
      setPhase('denied');
    }
  };

  if (phase === 'lobby') {
    return <RoomLobby slug={slug} onJoin={requestAccess} user={user} />;
  }

  if (phase === 'waiting') {
    return (
      <div className="text-center py-20">
        <div className="animate-pulse text-4xl mb-4">⌛</div>
        <p className="text-lg text-gray-700">Очікуємо одобрення ведучого...</p>
      </div>
    );
  }

  if (phase === 'denied') {
    return <p className="text-center text-red-600 py-20">Доступ заборонено.</p>;
  }

  return (
    <LiveKitRoom token={token!} serverUrl={process.env.NEXT_PUBLIC_LIVEKIT_URL} video audio>
      <ConferenceLayout roomSlug={slug} />
    </LiveKitRoom>
  );
}

Терміни

Виртуальні комнати з постійним URL, лобі, управлінням учасниками — 1–1.5 тижня.