<!DOCTYPE html>

<html lang="ru">

<head>

    <meta charset="UTF-8" />

    <link rel="icon" href="/favicon.ico" />

    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <title>SEO Backlink Checker</title>

    <!-- Подключение Tailwind CSS для стилей -->

    <script src="https://cdn.tailwindcss.com"></script>

    <!-- Подключение библиотек React -->

    <script src="https://unpkg.com/react@18/umd/react.development.js"></script>

    <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

    <!-- Подключение Babel для того, чтобы браузер понимал синтаксис JSX -->

    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

<script type="importmap">

{

  "imports": {

    "react": "https://aistudiocdn.com/react@^19.1.1",

    "react-dom/": "https://aistudiocdn.com/react-dom@^19.1.1/",

    "react/": "https://aistudiocdn.com/react@^19.1.1/"

  }

}

</script>

</head>

<body class="bg-gray-50">

    <div id="root"></div>


    <!-- Весь код приложения находится здесь -->

    <script type="text/babel" data-type="module">

        // --- Начало кода приложения ---


        // =================================================================

        // !!! ВАЖНОЕ ПРЕДУПРЕЖДЕНИЕ О БЕЗОПАСНОСТИ !!!

        // =================================================================

        // API-ключ и ID поисковой системы указаны здесь для демонстрации,

        // так как вы просили простое решение.

        //

        // В настоящем публичном приложении НИКОГДА не размещайте API-ключ

        // в коде, который виден пользователям в браузере. Его могут украсть.

        //

        // Для личного инструмента, которым пользуетесь только вы, это допустимый компромисс.

        // =================================================================

        const API_KEY = 'AIzaSyBBrVJvUWviD-eD3wy0E2Htd5uM8Ze3HFU';

        const SEARCH_ENGINE_ID = '338eeb9b4e5d140c4';


        // --- Сервис для проверки ссылок ---

        const checkerService = {

            async checkGoogleIndex(url) {

                const query = `site:${url}`;

                const endpoint = `https://www.googleapis.com/customsearch/v1?key=${API_KEY}&cx=${SEARCH_ENGINE_ID}&q=${encodeURIComponent(query)}`;


                try {

                    const response = await fetch(endpoint);

                    if (!response.ok) {

                        console.error(`Ошибка Google Search API для ${url}:`, response.status, await response.text());

                        return 'Error';

                    }

                    const data = await response.json();

                    if (data.searchInformation && data.searchInformation.totalResults && parseInt(data.searchInformation.totalResults, 10) > 0) {

                        return 'Indexed';

                    } else {

                        return 'Not Indexed';

                    }

                } catch (error) {

                    console.error(`Не удалось проверить статус индексации для ${url}:`, error);

                    return 'Error';

                }

            },

            

            // Эта функция имитирует проверку, так как из браузера нельзя напрямую проверить чужой сайт из-за ограничений безопасности (CORS).

            // Для полноценной проверки статуса страницы и наличия на ней ссылки нужен серверный компонент (например, Cloudflare Worker).

            // Но для вашей задачи главная функция - проверка индекса, и она будет работать.

            async checkPageAndLink(url, anchor) {

                return new Promise(resolve => {

                    setTimeout(() => {

                        // Имитация ответа: 90% страниц доступны, на 70% из них ссылка найдена.

                        if (Math.random() > 0.1) {

                             resolve({ pageStatus: 200, linkStatus: 'N/A' });

                        } else {

                             resolve({ pageStatus: 404, linkStatus: 'N/A' });

                        }

                    }, 500 + Math.random() * 1000);

                });

            },


            async processBacklinks(initialResults, onResultUpdate) {

                const promises = initialResults.map(async (result) => {

                    let currentResult = { ...result, pageStatus: 'Checking...', linkStatus: 'Checking...', indexStatus: 'Checking...' };

                    onResultUpdate(currentResult);

                    

                    const [pageAndLinkResult, indexResult] = await Promise.all([

                        this.checkPageAndLink(result.url, result.anchor),

                        this.checkGoogleIndex(result.url)

                    ]);

                    

                    currentResult = {

                        ...currentResult,

                        pageStatus: pageAndLinkResult.pageStatus,

                        linkStatus: pageAndLinkResult.linkStatus,

                        indexStatus: indexResult,

                    };

                    onResultUpdate(currentResult);

                });

                await Promise.allSettled(promises);

            }

        };


        // --- Иконки (SVG) ---

        const InfoIcon = (props) => (

            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}>

                <path strokeLinecap="round" strokeLinejoin="round" d="m11.25 11.25.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />

            </svg>

        );

        const ChevronDownIcon = (props) => (

          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}>

            <path strokeLinecap="round" strokeLinejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />

          </svg>

        );

        const CogIcon = (props) => (

          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}>

            <path strokeLinecap="round" strokeLinejoin="round" d="M9.594 3.94c.09-.542.56-1.007 1.11-1.226.55-.22 1.156-.22 1.706 0 .55.22 1.02.684 1.11 1.226l.094.542-.094.542c-.09.542-.56 1.007-1.11 1.226-.55.22-1.156.22-1.706 0-.55-.22-1.02-.684-1.11-1.226l-.094-.542.094-.542z" />

            <path strokeLinecap="round" strokeLinejoin="round" d="M12 21a9 9 0 110-18 9 9 0 010 18z" />

            <path strokeLinecap="round" strokeLinejoin="round" d="M12 15a3 3 0 100-6 3 3 0 000 6z" />

          </svg>

        );

        const FilterIcon = (props) => (

          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}>

            <path strokeLinecap="round" strokeLinejoin="round" d="M3 4.5h18m-18 6h12m-12 6h6" />

          </svg>

        );

        const SearchIcon = (props) => (

          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}>

            <path strokeLinecap="round" strokeLinejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />

          </svg>

        );

        const PlusIcon = (props) => (

          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" {...props}>

            <path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" />

          </svg>

        );

         const CloudIcon = (props) => (

          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 12" fill="currentColor" {...props}>

            <path d="M10.748 1.054a4.5 4.5 0 018.198 3.013A3.25 3.25 0 0115.75 11h-12a3.5 3.5 0 01-1.12-6.815 4.002 4.002 0 016.92-3.189l.2-.53c.27-.72.639-1.396 1.098-2.008z"></path>

          </svg>

        );


        // --- Компоненты React ---


        const Header = ({ domain }) => {

          return (

            <header>

              <div className="flex flex-col md:flex-row justify-between md:items-center">

                <div>

                  <h1 className="text-3xl font-bold text-gray-900">

                    DNS management for <span className="font-extrabold">{domain}</span>

                  </h1>

                  <p className="mt-2 text-gray-500">

                    Review, add, and edit DNS records. Edits will go into effect once saved.

                  </p>

                </div>

                <div className="mt-4 md:mt-0 flex flex-wrap gap-x-6 gap-y-2 text-sm text-blue-600 font-semibold">

                  <a href="#" className="flex items-center gap-1 hover:text-blue-800">

                    <span>DNS Setup:Full</span>

                    <InfoIcon className="w-4 h-4 text-gray-400" />

                  </a>

                  <a href="#" className="flex items-center gap-1 hover:text-blue-800">

                    <span>Import and Export</span>

                    <ChevronDownIcon className="w-4 h-4" />

                  </a>

                  <a href="#" className="flex items-center gap-1 hover:text-blue-800">

                    <CogIcon className="w-4 h-4" />

                    <span>Dashboard Display Settings</span>

                  </a>

                </div>

              </div>

            </header>

          );

        };


        const BacklinkInput = ({ onCheck, isChecking }) => {

          const [inputText, setInputText] = React.useState('');

          const placeholderText = `Вставьте ссылки, по одной на строку. Формат: URL,Анкор\n\nПример:\nhttps://example-blog.com/great-post,отличный пост\nhttps://another-site.net/article`;

          

          const handleCheck = () => {

            if (!isChecking) {

              onCheck(inputText);

            }

          };


          return (

            <div className="bg-white border border-gray-200 rounded-lg shadow-sm p-6">

              <div className="grid grid-cols-1 md:grid-cols-3 gap-4 items-end">

                <div className="md:col-span-3">

                  <label htmlFor="backlinks" className="block text-sm font-medium text-gray-700 mb-1">

                    Ссылки для проверки

                  </label>

                  <textarea

                    id="backlinks"

                    rows={8}

                    className="w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition duration-150 ease-in-out text-sm shadow-sm"

                    placeholder={placeholderText}

                    value={inputText}

                    onChange={(e) => setInputText(e.target.value)}

                    disabled={isChecking}

                  />

                   <p className="mt-2 text-xs text-gray-500">

                    Формат: `URL` или `URL,Анкор`. Если анкор не указан, он будет `N/A`.

                   </p>

                </div>

                <div className="md:col-span-3 flex flex-col sm:flex-row gap-2 justify-between">

                   <div className="flex gap-2">

                    <button className="flex items-center gap-2 px-4 py-2 text-sm font-semibold text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 transition">

                      <FilterIcon className="w-4 h-4"/> Add filter

                    </button>

                    <div className="relative flex-grow">

                       <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">

                         <SearchIcon className="w-4 h-4 text-gray-400" />

                       </div>

                       <input type="text" placeholder="Search DNS Records" className="w-full pl-9 pr-3 py-2 border border-gray-300 rounded-md focus:ring-1 focus:ring-blue-500 focus:border-blue-500 text-sm"/>

                    </div>

                     <button className="px-4 py-2 text-sm font-semibold text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 transition">

                        Search

                    </button>

                   </div>

                   <button

                    onClick={handleCheck}

                    disabled={isChecking}

                    className="flex items-center justify-center gap-2 px-6 py-2 text-sm font-semibold text-white bg-blue-600 border border-transparent rounded-md shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:bg-blue-300 disabled:cursor-not-allowed transition"

                  >

                    {isChecking ? (

                      <>

                        <svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">

                          <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>

                          <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>

                        </svg>

                        Проверка...

                      </>

                    ) : (

                     <>

                      <PlusIcon className="w-4 h-4"/>

                      Проверить ссылки

                     </>

                    )}

                  </button>

                </div>

              </div>

            </div>

          );

        };

        

        const StatusBadge = ({ status }) => {

            let colorClasses = 'bg-gray-100 text-gray-600';

            const s = String(status);

            if (s === 'Indexed' || s === 'Found' || s.startsWith('2')) {

                colorClasses = 'bg-green-100 text-green-800';

            } else if (s === 'Not Indexed' || s === 'Not Found' || s.startsWith('4') || s.startsWith('5') || s === 'Error' || s === 'CORS Error') {

                colorClasses = 'bg-red-100 text-red-700';

            } else if (s === 'Checking...') {

                colorClasses = 'bg-blue-100 text-blue-800 animate-pulse';

            } else if (s === 'Queued') {

                colorClasses = 'bg-yellow-100 text-yellow-800';

            } else if (s === 'N/A') {

                 colorClasses = 'bg-gray-100 text-gray-500';

            }


            return (

                <span className={`px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full ${colorClasses}`}>

                    {status}

                </span>

            );

        };


        const ResultsTable = ({ results, isChecking }) => {

            if (results.length === 0 && !isChecking) {

                return (

                    <div className="text-center py-12 bg-white border border-gray-200 rounded-lg shadow-sm">

                        <h3 className="text-lg font-medium text-gray-900">Нет результатов для отображения</h3>

                        <p className="mt-1 text-sm text-gray-500">Вставьте ссылки в поле выше и нажмите "Проверить ссылки", чтобы начать.</p>

                    </div>

                );

            }

            

          return (

            <div className="bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden">

                <div className="overflow-x-auto">

                    <table className="min-w-full divide-y divide-gray-200">

                        <thead className="bg-gray-50">

                        <tr>

                            <th scope="col" className="p-4 w-4"><input type="checkbox" className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" /></th>

                            <th scope="col" className="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase tracking-wider flex items-center gap-1">URL <InfoIcon className="w-3.5 h-3.5 text-gray-400" /></th>

                            <th scope="col" className="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase tracking-wider flex items-center gap-1">Анкор <InfoIcon className="w-3.5 h-3.5 text-gray-400" /></th>

                            <th scope="col" className="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase tracking-wider flex items-center gap-1">Статус индекса <InfoIcon className="w-3.5 h-3.5 text-gray-400" /></th>

                            <th scope="col" className="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase tracking-wider flex items-center gap-1">Статус страницы <InfoIcon className="w-3.5 h-3.5 text-gray-400" /></th>

                            <th scope="col" className="px-4 py-3 text-left text-xs font-bold text-gray-600 uppercase tracking-wider">Действия</th>

                        </tr>

                        </thead>

                        <tbody className="bg-white divide-y divide-gray-200">

                        {results.map((result) => (

                            <tr key={result.id} className="hover:bg-gray-50">

                                <td className="p-4 w-4"><input type="checkbox" className="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" /></td>

                                <td className="px-4 py-4 whitespace-nowrap text-sm font-medium text-gray-900 truncate max-w-sm" title={result.url}>

                                  <a href={result.url} target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline">{result.url}</a>

                                </td>

                                <td className="px-4 py-4 whitespace-nowrap text-sm text-gray-500 truncate max-w-xs" title={result.anchor}>{result.anchor}</td>

                                <td className="px-4 py-4 whitespace-nowrap text-sm text-gray-500">

                                     <div className="flex items-center gap-2">

                                        <CloudIcon className="w-8 h-5 text-orange-500"/>

                                        <StatusBadge status={String(result.indexStatus)} />

                                    </div>

                                </td>

                                <td className="px-4 py-4 whitespace-nowrap text-sm text-gray-500"><StatusBadge status={String(result.pageStatus)} /></td>

                                <td className="px-4 py-4 whitespace-nowrap text-right text-sm font-medium">

                                    <a href="#" className="text-blue-600 hover:text-blue-900">Edit</a>

                                </td>

                            </tr>

                        ))}

                        </tbody>

                    </table>

                </div>

            </div>

          );

        };



        const App = () => {

          const [results, setResults] = React.useState([]);

          const [isChecking, setIsChecking] = React.useState(false);

          const [error, setError] = React.useState(null);


          const handleCheckLinks = React.useCallback(async (inputText) => {

            setIsChecking(true);

            setError(null);


            const lines = inputText.trim().split('\n');

            if (lines.length === 0 || (lines.length === 1 && lines[0].trim() === '')) {

              setError("Пожалуйста, введите хотя бы один URL для проверки.");

              setIsChecking(false);

              return;

            }

            

            const initialResults = lines

              .map(line => line.trim())

              .filter(line => line)

              .map((line, index) => {

                const parts = line.split(',');

                const url = (parts.shift() || '').trim();

                const anchor = parts.join(',').trim() || 'N/A';

                return {

                  id: `${new Date().getTime()}-${index}`,

                  url,

                  anchor,

                  pageStatus: 'Queued',

                  linkStatus: 'Queued',

                  indexStatus: 'Queued',

                };

              });


            setResults(initialResults);


            const updateCallback = (updatedResult) => {

              setResults(prevResults =>

                prevResults.map(r => (r.id === updatedResult.id ? updatedResult : r))

              );

            };


            try {

              await checkerService.processBacklinks(initialResults, updateCallback);

            } catch (e) {

              console.error("Произошла непредвиденная ошибка:", e);

              setError("Произошла ошибка во время процесса. Проверьте консоль для деталей.");

            } finally {

              setIsChecking(false);

            }

          }, []);


          return (

            <div className="min-h-screen bg-gray-50 text-gray-800 font-sans">

              <div className="container mx-auto p-4 md:p-8">

                <Header domain="bingoonline.lat" />

                <main className="mt-8">

                  <BacklinkInput onCheck={handleCheckLinks} isChecking={isChecking} />

                  {error && <div className="mt-4 p-4 bg-red-100 text-red-700 border border-red-200 rounded-md">{error}</div>}

                  <div className="mt-6">

                    <ResultsTable results={results} isChecking={isChecking}/>

                  </div>

                </main>

              </div>

            </div>

          );

        };


        const rootElement = document.getElementById('root');

        const root = ReactDOM.createRoot(rootElement);

        root.render(<App />);


        // --- Конец кода приложения ---

    </script>

</body>

</html>