Training Planner
Рабочее место тренера на C# / .NET 10: персональные программы на месяц, упражнения, история клиента и выгрузка PDF.
Возможности
- Вход по логину и паролю без регистрации; первый аккаунт создаётся при первом запуске.
- Карточки клиентов: имя, цель, режим тренировок, состояние здоровья, отдельные заметки тренера и история программ. Корзина в карточке удаляет клиента вместе с его программами после подтверждения. У каждой программы есть отдельная корзина с подтверждением удаления.
- Библиотека упражнений с видеоссылками, независимыми группами и ручным добавлением в программу.
- «Импорт из таблицы»: вставка двух столбцов из Excel/Google Sheets без заголовков (название и ссылка) в модальном окне. Количество строк растёт автоматически; ячейки можно исправить, строки добавить или удалить. Ссылка необязательна. Можно выбрать существующую группу, создать новую или оставить без группы. Пустые строки пропускаются, одинаковые названия создают отдельные упражнения. При ошибке импорт целиком отклоняется, данные остаются для исправления.
- Создание, переименование и удаление групп вместе с их упражнениями. Корзина упражнения удаляет его из библиотеки; составленные тренировки не меняются.
- Перенос упражнения на группу или «Без группы» перетаскиванием. Добавление из другой группы создаёт независимую копию.
- Поиск в конфигураторе: адаптивный выпадающий список с фильтром названия и группы. Двойной клик или Enter сразу добавляет упражнение; одиночный клик выбирает его для кнопки «Добавить». Точное совпадение и начало названия выше остальных; регистр и «ё/е» не мешают поиску.
- Удаление из библиотеки через корзину с подтверждением; упражнения в ранее составленных программах сохраняются.
- Недели, тренировки и перемещаемые блоки. Перетаскивание за маркер или кнопки вверх/вниз.
- Общие подходы, текстовые повторения и вес; текстовые разминка и примечание; процент от максимума вручную.
- Отдых в минутах. Итог — количество подходов × отдых, включая последний подход. Время тренировки задаётся вручную.
- Копирование блоков, тренировок, недель и всей программы. Каждое упражнение — отдельный блок; сетов нет.
- Перетаскивание недель с автоматической перенумерацией и сохранением порядка.
- Перетаскивание тренировок внутри недели с указателем вставки, перенумерацией и сохранением выбранной тренировки.
- Удаление выбранной недели вместе с тренировками после подтверждения; в программе остаётся минимум одна неделя.
- Удаление тренировки через иконку корзины с подтверждением; после удаления последней тренировки неделю можно заполнить заново.
- Автосохранение, ручное сохранение, отмена последнего удаления блока в течение 10 секунд только в исходной тренировке (настройка
.env:Planner__UndoDeleteDurationSeconds) и защита от перезаписи из другой вкладки. - В начале PDF — таблица с именем, целью, режимом тренировок и состоянием здоровья клиента. Заметки тренера не экспортируются.
- Альбомный PDF: одна непрерывная таблица на неделю, цветные разделители тренировок с отдельными итогами, одна строка на упражнение, кликабельные ссылки и значки камеры. Ширина таблицы — 27,5 см; PNG камеры встроен в сборку, выровнен по центру ячейки. Размер иконки задаётся
Pdf__CameraSizePoints.
Интерфейс первой версии рассчитан на компьютер. Полная адаптация под iPhone 15 отложена. Клиентского кабинета, загрузки видео, регистрации и работы без интернета нет. Blazor Interactive Server требует соединения с сервером.
Запуск в Docker
Нужны Docker Engine/Desktop и Docker Compose.
cp .env.example .env
# Заполните POSTGRES_PASSWORD, BOOTSTRAP_TRAINER_USERNAME и BOOTSTRAP_TRAINER_PASSWORD.
docker compose up --build -d
По умолчанию приложение доступно на http://localhost:8080; порт меняется через APP_PORT. Пароль тренера должен соответствовать настройкам Identity: по умолчанию минимум 12 символов, цифра, строчная/прописная буква и спецсимвол.
Миграции применяются при запуске, затем создаётся первый тренер. Если аккаунт уже есть, переменные BootstrapTrainer его не изменяют и не сбрасывают пароль.
Два контейнера находятся в общей сети. PostgreSQL 18 не публикует порт на хост. Данные БД и ключи cookie сохраняются в именованных volumes. docker compose down сохраняет их; не используйте down -v, если данные нужны.
Конфигурация
appsettings.json задаёт значения по умолчанию. Docker Compose читает .env; env_file передаёт его содержимое контейнеру. Сам .NET автоматически .env не читает.
| Переменная в .env | Назначение |
|---|---|
APP_BIND_ADDRESS, APP_PORT |
Адрес и порт приложения на хосте |
POSTGRES_IMAGE, POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD |
База данных |
BOOTSTRAP_TRAINER_USERNAME, BOOTSTRAP_TRAINER_PASSWORD |
Первый тренер |
PLANNER_DEFAULT_WEEK_COUNT |
Число недель новой программы |
PLANNER_DEFAULT_SETS_COUNT |
Начальное число подходов |
PLANNER_DEFAULT_REPETITIONS |
Начальное значение повторений |
PLANNER_DEFAULT_REST_MINUTES |
Начальный отдых в минутах (например, 1.5) |
PLANNER_DEFAULT_WORKOUT_NAME |
Основа названия новой тренировки |
PLANNER_DEFAULT_WORKOUT_DURATION |
Время новой тренировки, по умолчанию 1 час |
PLANNER_DEFAULT_INTENSITY_PERCENT |
Процент нового упражнения, по умолчанию 80 |
PLANNER_AUTOSAVE_DELAY_MS |
Задержка автосохранения |
REQUIRE_HTTPS |
Защищённые cookie и перенаправление на HTTPS |
REVERSE_PROXY_IP |
Доверенный адрес обратного прокси |
COOKIE_HOURS, AUTH_REVALIDATION_MINUTES |
Срок cookie и повторная проверка авторизации |
DATABASE_INITIALIZE_ON_STARTUP |
Применение миграций и создание первого тренера |
Другие секции можно переопределять через стандартные имена .NET с __, например Pdf__FontSize=11, Pdf__ColumnWidthsCentimeters__0=7, Identity__Password__RequiredLength=16. Для настроек, явно перечисленных в Compose, используйте переменные из таблицы: значения секции environment имеют приоритет над env_file.
Лимиты импорта: ExerciseImport__MaxRows=1000, ExerciseImport__MaxClipboardCharacters=100000. Размер сообщения Blazor задаётся InteractiveServer__MaximumReceiveMessageBytes=1048576; при увеличении лимита текста учитывайте запас для UTF-8 и JSON. Все три параметра переопределяются через .env.
Шрифты PDF по умолчанию устанавливаются внутрь образа. Если задаёте другие пути, файлы должны быть доступны внутри контейнера. Не храните секреты в Git; .env исключён из Git и Docker build context.
Публичное развёртывание
Локальная .env.example использует HTTP и привязку к loopback. Для публичного сервера настройте TLS на обратном прокси, REQUIRE_HTTPS=true и доверенный REVERSE_PROXY_IP. Прокси должен передавать X-Forwarded-Proto, поддерживать WebSocket для Blazor и иметь доступ к выбранному адресу приложения. По умолчанию appsettings требует HTTPS.
Регистрации нет. Сервер проверяет принадлежность клиентов, упражнений и программ текущему тренеру. Повторные неудачные входы блокируются настройками Identity. Формы входа/выхода защищены antiforgery.
Резервная копия БД:
docker compose exec -T database sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > backup.sql
Храните резервные копии отдельно от сервера. Настройку расписания и хранения резервных копий нужно выполнить на целевом сервере.
Локальная разработка
Нужны .NET SDK 10 и доступный PostgreSQL. Передайте ConnectionStrings__TrainingDatabase, BootstrapTrainer__UserName, BootstrapTrainer__Password и пути Pdf__FontPath, Pdf__BoldFontPath к TTF-шрифтам с кириллицей. В Linux-контейнере пути настроены автоматически. Для HTTP-разработки задайте ASPNETCORE_ENVIRONMENT=Development.
dotnet restore TrainingPlanner.slnx
dotnet build TrainingPlanner.slnx
dotnet test tests/TrainingPlanner.Tests
dotnet run --project src/TrainingPlanner.Web
Для новой миграции:
dotnet tool restore
dotnet ef migrations add MigrationName --project src/TrainingPlanner.Web --output-dir Data/Migrations
Структура
- Domain: небольшие предметные классы со свойствами
get; set;. - Application: сценарии редактирования, валидация, копирование и контракты.
- Infrastructure: PostgreSQL, Identity, PDF.
- Web: Blazor-компоненты, HTTP-страницы авторизации и экспорта, конфигурация.
- Tests: проверки копирования параметров, порядка, валидации и расчёта отдыха.
Подробности — docs/architecture.md. Обязательные правила разработки — AGENTS.md.
Обновление старых программ
Перед применением миграции ExercisePrescription сделайте резервную копию БД. Общие вес и повторения берутся из первого подхода, старые сеты разделяются на одиночные упражнения, отдых переводится из секунд в минуты. Разные веса последующих подходов больше не используются. Для возврата к прежней модели нужны резервная копия и прежняя версия приложения. Переменные PLANNER_DEFAULT_REST_SECONDS и PLANNER_DEFAULT_ROUNDS заменены на PLANNER_DEFAULT_REST_MINUTES и PLANNER_DEFAULT_SETS_COUNT.