подключения между а и b автоматическое <!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>P2P Сеть на WebRTC без Серверов</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f0f2f5;
            margin: 0;
            padding: 20px;
        }
        .container {
            max-width: 1000px;
            margin: auto;
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        }
        h1, h2 {
            text-align: center;
            color: #333;
        }
        .section {
            margin-bottom: 30px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            color: #555;
        }
        textarea, input[type="text"] {
            width: 100%;
            height: 100px;
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid #ccc;
            border-radius: 4px;
            resize: vertical;
            font-family: monospace;
        }
        button {
            background-color: #007bff;
            color: white;
            border: none;
            padding: 10px 15px;
            border-radius: 4px;
            cursor: pointer;
            margin-right: 10px;
            margin-bottom: 10px;
        }
        button:hover {
            background-color: #0056b3;
        }
        #siteFrame {
            width: 100%;
            height: 500px;
            border: none;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        }
        .status {
            text-align: center;
            margin-bottom: 15px;
            padding: 10px;
            border-radius: 4px;
        }
        .info {
            background-color: #d1ecf1;
            color: #0c5460;
            border: 1px solid #bee5eb;
        }
        .success {
            background-color: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        .error {
            background-color: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        .flex {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
        }
        .copy-btn {
            background-color: #28a745;
            margin-top: -10px;
        }
        .copy-btn:hover {
            background-color: #1e7e34;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>P2P Сеть на WebRTC без Серверов</h1>
        
        <!-- Раздел для обмена предложениями, ответами и ICE кандидатами -->
        <div class="section">
            <h2>Обмен Соединениями</h2>
            
            <!-- Создание Предложения (Offer) - Только для Узла (Человек A) -->
            <div id="offerSection">
                <label for="localOffer">Ваш Предложение (Offer):</label>
                <textarea id="localOffer" readonly></textarea>
                <button id="createOffer">Создать Предложение</button>
                <button class="copy-btn" id="copyOffer">Скопировать Offer</button>
            </div>
            
            <!-- Вставка Предложения Другого Пользователя (Для Клиентов) -->
            <div id="clientOfferSection">
                <label for="remoteOffer">Вставьте Предложение Узла (Offer):</label>
                <textarea id="remoteOffer" placeholder="Вставьте сюда предложение"></textarea>
                <button id="setRemoteOffer">Установить Предложение</button>
            </div>
            
            <!-- Создание Ответа (Answer) - Только для Клиентов -->
            <div id="answerSection">
                <label for="localAnswer">Ваш Ответ (Answer):</label>
                <textarea id="localAnswer" readonly></textarea>
                <button id="createAnswer">Создать Ответ</button>
                <button class="copy-btn" id="copyAnswer">Скопировать Answer</button>
            </div>
            
            <!-- Вставка Ответа Узла (Для Узла) -->
            <div id="nodeAnswerSection">
                <label for="remoteAnswer">Вставьте Ответ Клиента (Answer):</label>
                <textarea id="remoteAnswer" placeholder="Вставьте сюда ответ"></textarea>
                <button id="setRemoteAnswer">Установить Ответ</button>
            </div>
            
            <!-- Обмен ICE Кандидатами - Общий Раздел -->
            <div>
                <h3>Обмен ICE Кандидатами</h3>
                <!-- Для Узла -->
                <label for="localCandidates">Ваши ICE Кандидаты:</label>
                <textarea id="localCandidates" readonly></textarea>
                <button class="copy-btn" id="copyLocalCandidates">Скопировать ICE Кандидаты</button>
                
                <!-- Для Клиента -->
                <label for="remoteCandidates">Вставьте ICE Кандидаты Другого Пользователя:</label>
                <textarea id="remoteCandidates" placeholder="Вставьте сюда ICE кандидаты"></textarea>
                <button id="addRemoteCandidates">Добавить ICE Кандидаты</button>
            </div>
            
            <div id="connectionStatus" class="status info">Статус соединения: Ожидание действий.</div>
        </div>
        
        <!-- Раздел для загрузки сайта -->
        <div class="section">
            <h2>Загрузка Сайта</h2>
            <label for="domainInput">Введите домен (например, 100.RR):</label>
            <input type="text" id="domainInput" placeholder="100.RR">
            <button id="loadSiteBtn">Загрузить сайт</button>
            <div id="loadStatus" class="status"></div>
        </div>
        
        <!-- Раздел для просмотра сайта -->
        <div class="section">
            <h2>Просмотр Сайта</h2>
            <iframe id="siteFrame" src=""></iframe>
        </div>
    </div>

    <!-- Обработка WebRTC -->
    <script>
        // Элементы управления
        const localOfferTextarea = document.getElementById('localOffer');
        const remoteOfferTextarea = document.getElementById('remoteOffer');
        const localAnswerTextarea = document.getElementById('localAnswer');
        const remoteAnswerTextarea = document.getElementById('remoteAnswer');
        const createOfferBtn = document.getElementById('createOffer');
        const setRemoteOfferBtn = document.getElementById('setRemoteOffer');
        const createAnswerBtn = document.getElementById('createAnswer');
        const setRemoteAnswerBtn = document.getElementById('setRemoteAnswer');
        const connectionStatus = document.getElementById('connectionStatus');
        const loadSiteBtn = document.getElementById('loadSiteBtn');
        const domainInput = document.getElementById('domainInput');
        const siteFrame = document.getElementById('siteFrame');
        const loadStatus = document.getElementById('loadStatus');

        const copyOfferBtn = document.getElementById('copyOffer');
        const copyAnswerBtn = document.getElementById('copyAnswer');
        const copyLocalCandidatesBtn = document.getElementById('copyLocalCandidates');
        const addRemoteCandidatesBtn = document.getElementById('addRemoteCandidates');

        let peerConnection;
        let dataChannel;
        let remoteCandidates = [];

        const configuration = {
            iceServers: [
                { urls: 'stun:stun.l.google.com:19302' }, // Стандартный STUN сервер
                { urls: 'stun:stun1.l.google.com:19302' },
                { urls: 'stun:stun2.l.google.com:19302' }
                // При необходимости добавьте TURN серверы
            ]
        };

        // Объект для хранения содержимого сайтов по доменным именам
        const sites = {
            '100.RR': `
                <!DOCTYPE html>
                <html lang="ru">
                <head>
                    <meta charset="UTF-8">
                    <title>Сайт 100.RR</title>
                    <style>
                        body { font-family: Arial, sans-serif; background-color: #e3f2fd; text-align: center; padding-top: 50px; }
                        h1 { color: #0d47a1; }
                        p { color: #1565c0; }
                    </style>
                </head>
                <body>
                    <h1>Добро пожаловать на сайт 100.RR!</h1>
                    <p>Это сайт, хостящийся внутри P2P-сети.</p>
                </body>
                </html>
            `,
            '200.RR': `
                <!DOCTYPE html>
                <html lang="ru">
                <head>
                    <meta charset="UTF-8">
                    <title>Сайт 200.RR</title>
                    <style>
                        body { font-family: Arial, sans-serif; background-color: #fff3e0; text-align: center; padding-top: 50px; }
                        h1 { color: #e65100; }
                        p { color: #ef6c00; }
                    </style>
                </head>
                <body>
                    <h1>Добро пожаловать на сайт 200.RR!</h1>
                    <p>Ещё один сайт внутри P2P-сети.</p>
                </body>
                </html>
            `,
            // Добавьте другие домены и их содержимое по необходимости
        };

        // Функция для создания Предложения (Offer) - Для Узла
        createOfferBtn.addEventListener('click', async () => {
            peerConnection = new RTCPeerConnection(configuration);
            dataChannel = peerConnection.createDataChannel("dataChannel");

            dataChannel.onopen = () => {
                console.log("Data channel открыт");
                connectionStatus.textContent = "DataChannel открыт и готов к использованию.";
                connectionStatus.classList.remove('info');
                connectionStatus.classList.add('success');
            };

            dataChannel.onmessage = (event) => {
                console.log("Получено сообщение:", event.data);
                try {
                    const data = JSON.parse(event.data);
                    if (data.type === 'siteContent') {
                        siteFrame.srcdoc = data.content;
                        loadStatus.textContent = `Сайт "${data.domain}" загружен.`;
                        loadStatus.classList.add('success');
                    } else {
                        console.log("Неизвестный тип данных:", data.type);
                    }
                } catch (e) {
                    console.error("Ошибка разбора сообщения:", e);
                }
            };

            // Обработка ICE кандидатов
            const localCandidates = [];
            peerConnection.onicecandidate = (event) => {
                if (event.candidate) {
                    localCandidates.push(event.candidate);
                } else {
                    // ICE сборка завершена
                    localCandidatesTextarea.value = JSON.stringify(localCandidates);
                }
            };

            try {
                const offer = await peerConnection.createOffer();
                await peerConnection.setLocalDescription(offer);
                localOfferTextarea.value = JSON.stringify(peerConnection.localDescription);
                connectionStatus.textContent = "Предложение создано. Поделитесь Offer с клиентом.";
                connectionStatus.classList.remove('info');
                connectionStatus.classList.add('success');
            } catch (error) {
                console.error("Ошибка создания предложения:", error);
                connectionStatus.textContent = "Ошибка при создании предложения.";
                connectionStatus.classList.remove('info');
                connectionStatus.classList.add('error');
            }
        });

        // Функция для копирования содержимого текстового поля
        function addCopyFunctionality(button, textarea) {
            button.addEventListener('click', () => {
                textarea.select();
                document.execCommand('copy');
                alert('Скопировано в буфер обмена!');
            });
        }

        addCopyFunctionality(copyOfferBtn, localOfferTextarea);
        addCopyFunctionality(copyAnswerBtn, localAnswerTextarea);
        addCopyFunctionality(copyLocalCandidatesBtn, document.getElementById('localCandidates'));

        // Установка удаленного Предложения (Offer) и создание Ответа (Answer) - Для Клиентов
        setRemoteOfferBtn.addEventListener('click', async () => {
            const remoteOffer = remoteOfferTextarea.value;
            if (!remoteOffer) {
                alert("Пожалуйста, вставьте предложение узла.");
                return;
            }

            try {
                const offer = JSON.parse(remoteOffer);
                peerConnection = new RTCPeerConnection(configuration);

                // Обработка входящих DataChannel
                peerConnection.ondatachannel = (event) => {
                    dataChannel = event.channel;

                    dataChannel.onopen = () => {
                        console.log("Data channel открыт");
                        connectionStatus.textContent = "DataChannel открыт и готов к использованию.";
                        connectionStatus.classList.remove('info');
                        connectionStatus.classList.add('success');
                    };

                    dataChannel.onmessage = (event) => {
                        console.log("Получено сообщение:", event.data);
                        try {
                            const data = JSON.parse(event.data);
                            if (data.type === 'siteContent') {
                                siteFrame.srcdoc = data.content;
                                loadStatus.textContent = `Сайт "${data.domain}" загружен.`;
                                loadStatus.classList.add('success');
                            } else {
                                console.log("Неизвестный тип данных:", data.type);
                            }
                        } catch (e) {
                            console.error("Ошибка разбора сообщения:", e);
                        }
                    };
                };

                // Обработка ICE кандидатов
                const localCandidates = [];
                peerConnection.onicecandidate = (event) => {
                    if (event.candidate) {
                        localCandidates.push(event.candidate);
                    } else {
                        // ICE сборка завершена
                        localCandidatesTextarea.value = JSON.stringify(localCandidates);
                    }
                };

                await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
                const answer = await peerConnection.createAnswer();
                await peerConnection.setLocalDescription(answer);
                localAnswerTextarea.value = JSON.stringify(peerConnection.localDescription);
                connectionStatus.textContent = "Ответ создан. Поделитесь Answer с узлом.";
                connectionStatus.classList.remove('info');
                connectionStatus.classList.add('success');
            } catch (error) {
                console.error("Ошибка установки удаленного предложения:", error);
                connectionStatus.textContent = "Ошибка при установке предложения.";
                connectionStatus.classList.remove('info');
                connectionStatus.classList.add('error');
            }
        });

        // Установка удаленного Ответа (Answer) - Для Узла
        setRemoteAnswerBtn.addEventListener('click', async () => {
            const remoteAnswer = remoteAnswerTextarea.value;
            if (!remoteAnswer) {
                alert("Пожалуйста, вставьте ответ клиента.");
                return;
            }

            try {
                const answer = JSON.parse(remoteAnswer);
                await peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
                connectionStatus.textContent = "Соединение установлено!";
                connectionStatus.classList.remove('info');
                connectionStatus.classList.add('success');
            } catch (error) {
                console.error("Ошибка установки удаленного ответа:", error);
                connectionStatus.textContent = "Ошибка при установке ответа.";
                connectionStatus.classList.remove('info');
                connectionStatus.classList.add('error');
            }
        });

        // Функция для загрузки сайта через DataChannel
        loadSiteBtn.addEventListener('click', () => {
            const domain = domainInput.value.trim();
            if (domain === '') {
                alert('Введите доменное имя.');
                return;
            }
            const content = sites[domain];
            if (!content) {
                alert('Сайт не найден в P2P-сети.');
                return;
            }
            if (dataChannel && dataChannel.readyState === 'open') {
                dataChannel.send(JSON.stringify({ type: 'siteContent', domain, content }));
                siteFrame.srcdoc = content;
                loadStatus.textContent = `Сайт "${domain}" загружен.`;
                loadStatus.classList.add('success');
            } else {
                alert('DataChannel не открыт. Убедитесь, что соединение установлено.');
            }
        });

        // Добавление удаленных ICE кандидатов - Оба Участника
        addRemoteCandidatesBtn.addEventListener('click', async () => {
            const remoteCandidatesText = document.getElementById('remoteCandidates').value;
            if (!remoteCandidatesText) {
                alert("Пожалуйста, вставьте ICE кандидаты другого пользователя.");
                return;
            }
            try {
                const candidates = JSON.parse(remoteCandidatesText);
                for (const candidate of candidates) {
                    await peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
                }
                connectionStatus.textContent = "Удаленные ICE кандидаты добавлены.";
                connectionStatus.classList.remove('info');
                connectionStatus.classList.add('success');
            } catch (error) {
                console.error("Ошибка при добавлении удаленных ICE кандидатов:", error);
                connectionStatus.textContent = "Ошибка при добавлении ICE кандидатов.";
                connectionStatus.classList.remove('info');
                connectionStatus.classList.add('error');
            }
        });
    </script>
</body>
</html>