# WhatIDo Учётная система для образовательного центра: журнал посещений, проектные работы воспитанников, галерея групп, откреплённые файлы и публичные страницы-витрины (share-ссылки). ## Возможности - **Журнал записей** — отметки о занятиях с фото и прикреплёнными файлами (проектные работы), мягкое удаление и корзина - **Файлы** — централизованный раздел со всеми загруженными файлами, фильтры (имя воспитанника, группа, даты, поиск) и вкладка «Откреплённые» - **Группы** — учебные группы, расписание (день недели, время), фотохроника группы - **Воспитанники** — справочник с привязкой к группам - **Share-ссылки** — публичные страницы-витрины с выбором группы / воспитанника / диапазона дат - **Дашборд** — статистика, активные группы, активность за 14 дней, последние записи, топ воспитанников - **Резервное копирование** — экспорт/импорт полного дампа (БД + файлы) в `tar.gz` - **Настройки** — научные тексты футера, анти-спам интервал - **HTTPS** — самоподписанный TLS-сертификат по умолчанию + готовая заготовка reverse-proxy (Caddy / Let's Encrypt) для публичного запуска ## Технологии - Node.js + Express - PostgreSQL (pg) - Multer (загрузка файлов), Tar (бэкапы) - Docker / Docker Compose ## Быстрый старт ### Требования - Docker + Docker Compose ### Запуск ```bash # создайте .env с паролем администратора и БД (см. раздел «Конфигурация») docker compose up -d --build ``` После старта: - **HTTP** `http://localhost:3000` — редирект на HTTPS - **HTTPS** `https://localhost:3443` — приложение (самоподписанный сертификат, принимайте предупреждение браузера) - **PostgreSQL** — доступен только внутри docker-сети (порт 5432 наружу не публикуется) Управление: ```bash docker compose ps # статус docker compose logs -f app docker compose down # остановка (данные сохраняются) ``` > Приложение **не запустится** без `ADMIN_PASSWORD` (защита от пароля по умолчанию). > `DB_PASSWORD` задаёт пароль пользователя `app` в PostgreSQL. ## Конфигурация Переменные окружения (`.env`): | Переменная | По умолчанию | Назначение | |------------------|--------------|-------------------------------------| | `ADMIN_PASSWORD` | — (обязательно) | Пароль администратора (X-Admin-Token). Без него сервер не стартует | | `DB_PASSWORD` | — (обязательно) | Пароль пользователя `app` в PostgreSQL | Пример `.env`: ``` ADMIN_PASSWORD=сложный-пароль DB_PASSWORD=случайная-строка ``` `DB_PASSWORD` подставляется в `docker-compose.yml` в `POSTGRES_PASSWORD` и `DATABASE_URL`. Если БД уже была инициализирована ранее, значение `DB_PASSWORD` должно совпадать с фактическим паролем пользователя `app` в БД (иначе приложение не подключится). ## Структура данных - `groups` — группы, расписание (`day_of_week`, `time_start`, `time_end`) - `students` — воспитанники - `entries` — записи журнала (фото `photo_path`, описание, мягкое удаление `deleted_at`) - `project_files` — файлы записей (`token`, `path`, привязка `entry_id`, отметка `detached_at`) - `group_photos` — фотохроника групп - `share_links` — публичные ссылки-витрины - `settings` — пары ключ/значение (анти-спам интервал, футер) Схема инициализируется при первом запуске из `db/init.sql`; миграции существующей БД — в `db/migration.sql`. ## Хранилище файлов Загруженные фото и файлы хранятся в каталоге `uploads/` на хосте и монтируются в контейнер (`./uploads:/app/uploads`). Это даёт прямой доступ к данным из-под хост-системы. Данные БД хранятся в именованном томе `pgdata`. ## Бэкапы В админке (Настройки → Бэкап) можно: - Скачать полный бэкап — `tar.gz`, содержащий `data.json` (все таблицы) и `uploads/` - Восстановить из файла бэкапа Также доступны скрипты на хосте: ```bash ./scripts/backup.sh # дамп БД + фото в backups/whatido-backup-<дата>.tar.gz ./scripts/restore.sh # восстановление из архива ``` ## Безопасность - **Пароль администратора** обязателен (`ADMIN_PASSWORD`); фолбэка на `admin` нет. - **CORS отключён** — кросс-доменные запросы к API запрещены. - **Rate limiting** по IP на публичные роуты: `POST /api/entries` — 10 запросов / 15 мин, загрузка файлов и share-ссылки — 300 / 15 мин. - **Загрузки** ограничены: 30 МБ суммарно на запись, 10 МБ на файл; заблокированы опасные расширения (`.html`, `.js`, `.svg`, `.xml`, `.exe` и др.); SVG иным способом не отдаётся inline. - **Restore** проходит полную валидацию данных бэкапа; удаление файлов ограничено каталогом `uploads/`. - **Заголовки**: `helmet` — `X-Frame-Options`, `nosniff`, HSTS, `Referrer-Policy`. - **Порт БД** 5432 наружу не публикуется (доступ только внутри docker-сети). - **TLS**: по умолчанию самоподписанный сертификат. Для публикации включите reverse-proxy Caddy с Let's Encrypt — заготовка закомментирована в `docker-compose.yml`, конфиг в `Caddyfile.example`. ### Публичный запуск (TLS) ```bash cp Caddyfile.example Caddyfile # подставить реальный домен # расскомментировать сервис caddy и перевести ports сервиса app в expose (следуйте комментариям в docker-compose.yml) docker compose up -d --build ``` Caddy автоматически получит Let's Encrypt сертификат на 80/443. ## Основные API | Метод | Путь | Назначение | |-------|------|------------| | `GET` | `/api/entries` | Записи журнала (с фильтрами) | | `POST` | `/api/entries` | Создать запись (photo + files) | | `PUT/DELETE` | `/api/entries/:id` | Обновить / мягко удалить | | `GET` | `/api/files` | Все файлы (фильтры) | | `GET` | `/api/files/detached` | Откреплённые файлы | | `POST` | `/api/files/:id/detach` | Открепить файл от записи | | `GET` | `/api/files/:token` | Скачать/показать файл по токену (публично) | | `GET` | `/api/groups` | Список групп | | `POST/PUT/DELETE` | `/api/groups/:id(?)` | CRUD групп | | `GET/POST` | `/api/groups/:id/photos` | Фотохроника группы | | `GET/POST/PUT/DELETE` | `/api/share/...`, `/api/links` | Публичные ссылки | | `GET` | `/api/backup` | Скачать бэкап | | `POST` | `/api/restore` | Восстановить из бэкапа | | `GET` | `/api/dashboard`, `/api/stats` | Статистика | Защищённые админ-маршруты требуют заголовок `X-Admin-Token` с `ADMIN_PASSWORD`. ## Структура проекта ``` ├── docker-compose.yml # сервисы app + db (+ закомментированный caddy) ├── Dockerfile # сборка образа (Node 20, генерация TLS-сертификата) ├── Caddyfile.example # шаблон reverse-proxy с Let's Encrypt (домен заменить) ├── server.js # Express-приложение ├── db/ │ ├── init.sql # схема при первом запуске │ └── migration.sql # миграции существующей БД ├── public/ # статика (HTML/CSS/JS админки и витрин) ├── scripts/ # вспомогательные скрипты ├── uploads/ # загруженные файлы (bind-монт, вне git) └── backups/ # локальные бэкапы ```