- require ADMIN_PASSWORD (no default), remove CORS - close public DB port, move DB credentials to .env (DB_PASSWORD) - fix HTML escaping, add helmet + sec headers (no CSP due to inline scripts) - rate limit public routes by IP (express-rate-limit) - validate restore data and confine file unlinking to uploads/ - block dangerous upload extensions, 30MB per-entry limit, SVG not served inline - return 400 on unknown group_id in POST /api/entries - add commented Caddy/Let's Encrypt reverse-proxy scaffold + Caddyfile.example - update README
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
Запуск
# создайте .env с паролем администратора и БД (см. раздел «Конфигурация»)
docker compose up -d --build
После старта:
- HTTP
http://localhost:3000— редирект на HTTPS - HTTPS
https://localhost:3443— приложение (самоподписанный сертификат, принимайте предупреждение браузера) - PostgreSQL — доступен только внутри docker-сети (порт 5432 наружу не публикуется)
Управление:
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/ - Восстановить из файла бэкапа
Также доступны скрипты на хосте:
./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)
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/ # локальные бэкапы