15 Google-аккаунтов и ни рубля на ИИ: пишу VPN-сервис в одиночку
Стас, разработчик без коммерческого опыта, решил создать собственный VPN-сервис. Он использовал FastAPI, React, PostgreSQL и AI-инструменты, чтобы автоматизировать управление серверами Hysteria2. В статье он делится архитектурой, кодом и уроками, извлечёнными из проекта.
Начало пути: от OpenVPN к Hysteria2
В декабре 2025 года Стас столкнулся с проблемой доступа к привычным интернет-сервисам. Его регион одним из первых тестировал белые списки, и многие ресурсы стали недоступны. До этого он пользовался OpenVPN с открытыми ключами, но после блокировки сервиса пришлось искать альтернативу. Так он наткнулся на протокол Hysteria2, который не только решал проблему, но и предоставлял удобный API для управления.
Стас развернул VPS, настроил Hysteria2 и начал экспериментировать с API через curl. Затем автоматизировал простые задачи на Python с библиотекой requests. Это был первый шаг к созданию полноценного SaaS. Он понял, что может программно управлять VPN-сервером, и захотел сделать из этого продукт.
Вдохновением послужило видео о том, как устроена оплата картой, которое показало возможности REST API. Стас решил, что пора действовать. Он начал с изучения Docker и микросервисной архитектуры, чтобы разделить проект на независимые компоненты.
Архитектура: три независимых контейнера
С самого начала Стас разделил проект на три изолированные части: backend, frontend и hysteria. Каждая часть работает в отдельном Docker-контейнере, что упрощает разработку и развёртывание. Бэкенд общается с Hysteria2 по REST, фронтенд — с бэкендом, а Hysteria2 занимается только VPN-соединениями.
Для управления всем стеком используется docker-compose.yaml, который поднимает все сервисы одной командой. Hysteria2 загружается как готовый образ с Docker Hub, а бэкенд и фронтенд собираются локально из Dockerfile. Стас уделил внимание оптимизации сборки: часто перестраиваемые слои размещены ниже в инструкции, чтобы ускорить процесс.
Для продакшна созданы отдельные конфигурации под каждую VPS-ноду: Германия, Нидерланды, Польша и Швеция. Hysteria2-ноды принимают соединения пользователей и проксируют трафик, а бэкенд размещён в Yandex Cloud. Такая архитектура обеспечивает гибкость и масштабируемость.
Верните доступ к нужным сервисам Без лимита устройств на любом тарифе. 3 дня за 10 ₽ — или сразу месяц от 200 ₽. Тарифы от 200 ₽/мес · оплата через банк · доступ сразу после оплатыБэкенд на FastAPI: структура и авторизация
Бэкенд написан на FastAPI и использует PostgreSQL для хранения данных. Структура проекта включает основные модули: main.py, auth_jwt.py, config.py, database.py, schemas.py, rate_limiter.py, maintenance.py, traffic_collector.py и роутеры для аутентификации, пользователей, Hysteria, серверов, трафика и WebSocket.
Авторизация построена на JWT с двумя токенами: access и refresh. Refresh-токен хранится в httpOnly cookie, что защищает его от XSS-атак. В базе данных хранится не сам токен, а его SHA-256 хеш. Это позволяет проверять и отзывать сессии. Logout удаляет конкретный refresh-токен, а блокировка пользователя — все его токены.
Роль пользователя не включена в JWT, а читается из базы при каждом защищённом запросе. Это добавляет один запрос к БД, но обеспечивает мгновенное применение изменений ролей или блокировок. WebSocket-инфраструктура написана, но пока закомменчена, так как поллинг через TanStack Query справляется с текущей нагрузкой.
База данных: от новичка до уверенного пользователя
До этого проекта Стас не имел опыта работы с базами данных. Он не знал, что такое foreign key или primary key. Чтобы разобраться, он скачал DataGrip, сделал дамп продакшн-базы и начал изучать её структуру. PostgreSQL уже работал на сервере, но внутреннее устройство оставалось загадкой.
Для обучения он создал локальную среду на PostgreSQL 17 в Docker-контейнере. Прошёл курс от Postgres Pro и прочитал их книгу по SQL. Это помогло понять, как проектировать таблицы, писать запросы и оптимизировать индексы. Стас отмечает, что практика с реальными данными дала больше, чем теория.
В проекте используются таблицы для пользователей, refresh-токенов, серверов и трафика. Связи между таблицами настроены через внешние ключи. Стас старался следовать принципам нормализации, но иногда допускал денормализацию для упрощения запросов. Он признаётся, что база данных — это та часть, где он до сих пор учится.
Фронтенд на React: интерфейс и взаимодействие
Фронтенд написан на React с использованием Vite. Он взаимодействует с бэкендом через REST API. Для управления состоянием и запросами используется TanStack Query, который обеспечивает кэширование и автоматическое обновление данных. Интерфейс включает страницы входа, регистрации, личного кабинета и админ-панели.
Стас не имел опыта во фронтенд-разработке, поэтому изучал React по ходу дела. Он использовал компонентный подход, разбивая интерфейс на переиспользуемые блоки. Для стилизации применял CSS-модули. Особое внимание уделил безопасности: все запросы к API защищены JWT, а refresh-токен хранится в httpOnly cookie.
WebSocket-инфраструктура на фронте написана, но пока не активна. Стас планирует включить её, когда нагрузка вырастет. Сейчас он использует поллинг для обновления данных о трафике и статусе серверов. Это простое решение, которое работает без лишней сложности.
AI-ассистенты: 15 Google-аккаунтов и ни рубля
Стас активно использовал AI-инструменты для написания кода и решения задач. У него не было бюджета на платные подписки, поэтому он зарегистрировал 15 Google-аккаунтов, чтобы получать бесплатные кредиты в различных сервисах. Это позволяло ему генерировать код, искать ошибки и изучать новые технологии.
Он отмечает, что AI-ассистенты помогли ему быстрее освоить FastAPI, React и Docker. Однако он всегда проверял сгенерированный код и адаптировал его под свои нужды. Некоторые решения приходилось переписывать, так как AI не всегда учитывал контекст проекта. Но в целом это сэкономило много времени.
Стас считает, что AI — это инструмент, который не заменяет разработчика, но ускоряет рутинные задачи. Он советует новичкам использовать AI для обучения, но не полагаться на него слепо. Важно понимать, что делает код, и уметь его отлаживать.
Деплой и инфраструктура: VPS и Docker
Для развёртывания Стас использует несколько VPS в разных странах: Германия, Нидерланды, Польша и Швеция. Каждая нода запускает Hysteria2 в Docker-контейнере. Бэкенд размещён в Yandex Cloud, что обеспечивает стабильность и масштабируемость. Всё управляется через docker-compose, что упрощает обновление и мониторинг.
Стас настроил CI/CD через GitHub Actions. При пуше в репозиторий автоматически собираются образы и деплоятся на серверы. Секреты хранятся в GitHub Secrets, хотя изначально он ленился это делать. Позже он осознал важность безопасности и переделал все креды.
Мониторинг осуществляется через встроенные инструменты Docker и логи. Стас также написал скрипты для сбора трафика и проверки доступности серверов. Он признаётся, что инфраструктура — это та часть, где ещё есть что улучшать, но текущая конфигурация справляется с нагрузкой.
Верните доступ к нужным сервисам Без лимита устройств на любом тарифе. 3 дня за 10 ₽ — или сразу месяц от 200 ₽. Тарифы от 200 ₽/мес · оплата через банк · доступ сразу после оплатыБезопасность: JWT, httpOnly cookie и хеширование
Безопасность — один из ключевых аспектов проекта. Стас использует JWT с двумя токенами: access и refresh. Refresh-токен хранится в httpOnly cookie, что делает его недоступным для JavaScript и защищает от XSS. В базе данных хранится SHA-256 хеш токена, а не сам токен. Это позволяет отзывать сессии без компрометации данных.
При логине сервер генерирует refresh-токен, хеширует его и сохраняет в базе. Клиент получает токен в cookie. При обновлении access-токена сервер проверяет хеш refresh-токена в базе. Если токен отозван или истёк, пользователь должен войти заново. Logout удаляет refresh-токен из базы.
Роль пользователя не хранится в JWT, а читается из базы при каждом запросе. Это позволяет мгновенно менять права или блокировать пользователя. Стас считает, что такой подход оправдан, несмотря на дополнительный запрос к БД. Безопасность для него важнее микрооптимизации.
Уроки и планы на будущее
Стас делится уроками, извлечёнными из проекта. Во-первых, не бояться начинать с нуля: он не имел опыта в коммерческой разработке, но смог создать работающий SaaS. Во-вторых, AI-инструменты — это помощники, но не замена пониманию. В-третьих, безопасность должна быть приоритетом с самого начала.
В планах — включить WebSocket для实时 обновлений, улучшить мониторинг и добавить поддержку новых протоколов. Стас также хочет оптимизировать базу данных и внедрить кэширование. Он продолжает учиться и экспериментировать, используя проект как песочницу для новых технологий.
Он советует другим разработчикам не бояться сложных проектов и использовать доступные ресурсы. GitHub-репозиторий проекта открыт, и Стас приветствует обратную связь. Его история показывает, что настойчивость и любопытство могут привести к впечатляющим результатам.
Частые вопросы
Какие технологии использовались для создания VPN-сервиса?
Бэкенд написан на FastAPI с PostgreSQL, фронтенд — на React с Vite. Для VPN-соединений используется Hysteria2. Всё развёрнуто в Docker-контейнерах и управляется через docker-compose.
Как обеспечивается безопасность пользовательских данных?
Используются JWT с access и refresh токенами. Refresh-токен хранится в httpOnly cookie и в базе в виде SHA-256 хеша. Роль пользователя проверяется при каждом запросе, что позволяет мгновенно блокировать доступ.
Сколько Google-аккаунтов понадобилось для бесплатного использования AI?
Стас зарегистрировал 15 Google-аккаунтов, чтобы получать бесплатные кредиты в различных AI-сервисах. Это позволило ему генерировать код и учиться без финансовых затрат.
Какие сложности возникли при работе с базами данных?
Изначально Стас не имел опыта работы с базами данных. Он изучал PostgreSQL через курс Postgres Pro и практику на локальной среде. Основные сложности были с проектированием связей и написанием запросов.
Планируется ли добавление WebSocket для реального времени?
Да, инфраструктура WebSocket уже написана, но пока закомменчена. Стас планирует включить её, когда нагрузка вырастет. Сейчас используется поллинг через TanStack Query.