Образовательная платформа — FastAPI + React + PostgreSQL
Нужен только Docker Desktop — ни Python, ни Node ставить не надо.
./start.shСкрипт сам создаст .env, соберёт образы, поднимет всё, дождётся ответа API и
откроет сайт в браузере. Запускать можно сколько угодно раз.
На Windows — из Git Bash той же командой. Если запускать скрипт нельзя, работает и обычное:
cp .env.example .env
docker compose up --build| Что | Адрес |
|---|---|
| Сайт | http://localhost:8080 |
| Админка | http://localhost:8080/admin |
| Документация API | http://localhost:8080/api/docs |
Вход в админку — из FIRST_ADMIN_EMAIL / FIRST_ADMIN_PASSWORD
(по умолчанию admin@itstek.com / admin123). Администратор создаётся
только пока таблица пользователей пуста — менять эти переменные позже
бесполезно. Порт сайта задаётся через APP_PORT в .env.
Остановить: docker compose down. Логи: docker compose logs -f backend.
- 📚 Каталог курсов
- 📝 Заявки — ФИО, телефон с проверкой формата, Telegram по желанию
- 🧭 Тест профориентации на
/test, подбирает курс по ответам - 🏆 Сертификаты с публичной проверкой на
/verifyи генерацией PDF - 🏷️ Бегущая строка со скидками, расписание задаётся в админке
- 🎯 Воронка заявок, аналитика на дашборде
- 👩🏫 Управление курсами и преподавателями
- 👥 Две роли — администратор (вся панель) и менеджер (только заявки)
- 📱 Уведомления о заявках в Telegram
- 🌐 Три языка интерфейса — узбекский, русский, английский
| Слой | Технологии |
|---|---|
| Фронтенд | React (Vite) + TailwindCSS |
| Бэкенд | FastAPI + Python 3.13 + SQLAlchemy 2.0 |
| База | PostgreSQL + Alembic |
| Инфраструктура | Docker Compose + Nginx |
| Авторизация | JWT |
Все параметры — в корневом .env, полный список с комментариями лежит в
.env.example. Что важно знать:
- Telegram не обязателен. Пустые
BOT_TOKEN/CHAT_ID— уведомления просто пропускаются, заявки всё равно сохраняются. Проверить связку:GET /api/telegram/test. - Переменные
VITE_*запекаются в бандл при сборке, а не читаются в рантайме. Поменяли номер телефона или ссылки на соцсети — нуженdocker compose up --build, простого перезапуска мало.
База в Docker, бэкенд и фронтенд — на хосте. Дополнительно нужны Python 3.13+, uv и Node 20+.
Базе нужно открыть порт наружу — этим занимается отдельный файл
docker-compose.dev.yml, в обычном запуске он не участвует.
# Возьмите свободный порт: 5432 обычно занят локально установленным PostgreSQL.
echo "POSTGRES_PORT=55432" >> .env
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d postgresПроверка, что база доступна с хоста:
set -a; . ./.env; set +a
PGPASSWORD="$POSTGRES_PASSWORD" psql -h 127.0.0.1 -p "$POSTGRES_PORT" \
-U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "select current_database()"cd backend
uv sync
set -a; . ../.env; set +a
export DATABASE_URL="postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@127.0.0.1:${POSTGRES_PORT}/${POSTGRES_DB}"
uv run alembic upgrade head
uv run uvicorn app.main:app --port 8000 --reloadDATABASE_URL нужно задавать явно: значение по умолчанию указывает на хост
postgres, который существует только внутри сети Compose.
cd frontend
npm install
npm run devОткроется http://localhost:5173. Запросы /api/* Vite проксирует на
localhost:8000 — это уже настроено в vite.config.js.
# внутри Docker
docker compose exec backend uv run alembic upgrade head
docker compose exec backend uv run alembic current
# с хоста (нужен экспортированный DATABASE_URL, см. выше)
cd backend
uv run alembic upgrade head
uv run alembic revision -m "описание"--autogenerate используйте осторожно и всегда читайте получившийся файл
перед применением.
docker compose logs -f backend # логи бэкенда
docker compose logs -f frontend nginx # логи фронта и прокси
docker compose restart backend # перезапустить один сервис
docker compose up -d --build backend # пересобрать после правок бэкенда
docker compose down # остановить всё
docker compose down -v # остановить и СТЕРЕТЬ базуСброс базы с нуля (данные пропадут, админ создастся заново):
docker compose down -v && ./start.shsh: vite: Permission denied, сборка падает с кодом 126. Возникало, когда
в папке проекта лежал node_modules, установленный на хосте: COPY . .
затирал им то, что установил npm install внутри образа, и на системе без
права на исполнение (Windows, распакованный архив, флешка) vite переставал
запускаться. Лечится файлами .dockerignore в frontend/ и backend/ —
они уже в репозитории, отдельных действий не требуется. Если ошибка всё же
всплыла, соберите начисто:
docker compose build --no-cache frontend && ./start.shFATAL: database "itstek" does not exist, бэкенд перезапускается по кругу.
PostgreSQL создаёт базу из POSTGRES_DB только когда том с данными пуст. Если
том остался от прерванного первого запуска или от другого значения
POSTGRES_DB, initdb пропускается и база не появляется. Бэкенд теперь
создаёт её сам при старте, так что достаточно пересобрать образ:
docker compose up -d --build backendЛибо начисто: docker compose down -v && ./start.sh (
docker compose exec postgres psql -U postgres -c 'create database itstek'
docker compose restart backendaddress already in use, контейнер не стартует. Порт на хосте занят другой
программой. Запуск через Docker публикует наружу только порт сайта
(APP_PORT, по умолчанию 8080) — база наружу не выставляется специально, чтобы
проект поднимался и на машинах с локальным PostgreSQL. Если ошибка про 8080 —
впишите в .env другой порт:
echo "APP_PORT=8081" >> .env
./start.shПорт 5432 может быть занят — это касается только гибридного режима. Если на
машине уже стоит PostgreSQL (Homebrew, Postgres.app), он держит 5432, иногда и
5433. Docker при этом может спокойно написать Started, но подключение с хоста
уйдёт в чужую базу, и вы получите FATAL: password authentication failed с
правильным паролем. Симптом обманчивый: пароль ни при чём. Проверить, кто
отвечает:
docker compose stop postgres
psql -h 127.0.0.1 -p 5432 -U postgres -d postgres # отвечает — значит порт чужой
docker compose start postgresЛечится свободным портом в POSTGRES_PORT (например, 55432).
Миграции и create_all конфликтуют. При старте приложение вызывает
Base.metadata.create_all (backend/app/main.py), поэтому на чистой базе
таблицы создаёт оно, а не Alembic: alembic_version остаётся пустым, и
следующая alembic upgrade head падает с «таблица уже существует». Новые
колонки при этом не добавляются вовсе — create_all не делает ALTER. Пока
лечится вручную (alembic stamp + upgrade); правильное решение — убрать
create_all и прогонять миграции при старте.
npm run lint не работает. ESLint 9 ждёт eslint.config.js, а его в
проекте нет. Сборка (npm run build) при этом проходит нормально.