Привет, Хабр! Мне 14 лет, и пока мои ровесники ругаются в cs2, а нормальные люди спят по 8 часов, я решил, что мне катастрофически не хватает стресса в жизни. Поэтому за последний месяц я написал собственную платформу для решения алгоритмических задач - с изолированным выполнением кода, микросервисами и котиками.

Встречайте - ЛитКот. Проект уже крутится в проде на leetcot.ru, а в этой статье я расскажу, как организовать архитектуру такого сервиса, не задохнуться в монорепе и защитить сервер от мамкиных хакеров (вроде меня самого).

Зачем нужен ещё один тренажёр?

LeetCode - это классика, но давайте честно: решать сухие задачи про инверсию бинарного дерева бывает невероятно скучно. Особенно когда на реальном собеседовании тебя попросят просто отцентрировать div или переложить JSON из одной апишки в другую. Мне захотелось добавить геймификации, сюжета и немного (а точнее, нездорово много) безумия.

Например, в моём курсе «Щёлкаем алгоритмы как рыбку» нет унылой задачи «Поиск элемента в отсортированном массиве». Зато есть «Прятки с сосиской»! Пользователю нужно написать алгоритм бинарного поиска за O(log n), чтобы помочь коту найти сосиску в контейнерах. Согласитесь, ради виртуальной сосиски писать код как-то приятнее, чем ради мифического оффера в бигтех.

Выполнение кода: даём пользователю гранату (и надеемся на лучшее)

Самое весёлое в разработке аналога LeetCode - это запуск чужого кода. Разрешить случайным людям из интернета выполнять Python-скрипты на твоём сервере - это как добровольно пригласить в дом полтергейста. Один случайно (или специально) запущенный while True: fork() - и твой сервер со слезами отправляется в Вальгалу. Я это узнал не из книжки — во время разработки умудрился положить собственный тестовый сервер именно так. Сидел и смотрел как он перестаёт отвечать, пока до меня дошло что происходит.

Пришлось продумать надёжную изоляцию процессов. Код каждого пользователя выполняется в изолированной песочнице со строгими ограничениями по памяти и жёстким тайм-аутом. Не успел найти сосиску вовремя - лови Time Limit Exceeded и иди оптимизируй. У нас тут суровый кошачий киберспорт.

Инфраструктура, или «Я у мамы DevOps»

Чтобы проект не жил только на моём localhost, я настроил полноценный CI/CD-пайплайн в GitHub Actions. Проект пакуется в Docker-контейнеры с аккуратным Dockerfile и .dockerignore, чтобы случайно не утянуть в прод папку node_modules весом с небольшую чёрную дыру.

Для удобной разработки настроены DevContainers - настраивать локальное окружение руками каждый раз это для слабаков.

А чтобы после моих гениальных релизов не отваливались вёрстка и логика, прикручены E2E-тесты на Playwright. Да, я даже пишу тесты. Сам в шоке.

name: Deploy Production
on:
  release:
    types: [published]
jobs:
  deploy:
    runs-on: self-hosted
    steps:
      - name: Cleanup host
        run: |
          docker system prune -af --volumes
          sudo journalctl --vacuum-size=10M
          sudo apt-get clean
      - uses: actions/checkout@v4
      - name: Environment
        run: |
          [ -f /home/user1/actions-runner/secrets.env ] && cp /home/user1/actions-runner/secrets.env ./.env || exit 1
      - name: Update and Deploy
        run: |
          docker compose pull
          docker compose up -d --remove-orphans
      - name: Migrations
        run: |
          docker compose exec -T app pnpm --filter @repo/db exec prisma migrate deploy

Что дальше?

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

Но почивать на лаврах скучно. Прямо сейчас я готовлю к релизу следующий проект -и это будет кое-что большее чем просто тренажёр задач. Следите за leetcot.ru и arlist.ru.

Буду рад конструктивной критике (и неконструктивному хейту) в комментариях. Пишите, как вы реализовывали безопасное выполнение кода в своих пет-проектах, ставьте плюсики и буду рад увидеться с вами вновь!