Initial commit: InviteMe production redesign
This commit is contained in:
+195
@@ -0,0 +1,195 @@
|
||||
# Контекст проекта InviteMe
|
||||
|
||||
## Назначение
|
||||
|
||||
InviteMe — веб-приложение для организации регистрации гостей на корпоративные мероприятия. Является упрощённым аналогом Timepad: загрузка списка участников, генерация персональных QR-кодов, рассылка приглашений, контроль входа на мероприятии и административная аналитика.
|
||||
|
||||
## Бизнес-процесс
|
||||
|
||||
### 1. Подготовка до мероприятия
|
||||
|
||||
1. Администратор создаёт учётные записи организаторов и сотрудников на входе.
|
||||
2. Организатор создаёт новое мероприятие в админ-панели.
|
||||
3. Организатор формирует Excel-файл со столбцами:
|
||||
- `Отдел`
|
||||
- `ФИО`
|
||||
- `Должность`
|
||||
- `e-mail`
|
||||
- `название фото`
|
||||
4. Собирает фотографии гостей. Название каждого файла должно совпадать со значением в столбце `название фото`.
|
||||
5. В админ-панели выбирает созданное мероприятие и загружает Excel. Система создаёт записи гостей в рамках этого мероприятия и генерирует для каждого уникальный QR-код.
|
||||
6. Загружает фото (по одному или ZIP-архивом) — они сохраняются в закрытой папке мероприятия `instance/uploads/photos/event_<id>/`.
|
||||
7. При необходимости вручную добавляет, редактирует или блокирует гостей.
|
||||
8. Запускает массовую рассылку приглашений. Каждый гость получает письмо с QR-кодом.
|
||||
|
||||
### 2. Вход на мероприятие
|
||||
|
||||
1. Сотрудники на входе открывают страницу `/check-in` на телефоне/планшете.
|
||||
2. Авторизуются под учётной записью `staff`.
|
||||
3. Проверяют гостей одним из способов:
|
||||
- Сканирование QR-кода с экрана телефона или распечатки.
|
||||
- Поиск по ФИО (вводят фамилию, выбирают из выпадающего списка).
|
||||
4. На экране отображается крупное фото гостя, отдел, должность, email и кнопка «Отметить вход».
|
||||
5. Сотрудник сверяет фото с гостем и нажимает кнопку.
|
||||
6. Если гость уже был отмечен, система показывает предупреждение и время входа.
|
||||
|
||||
### 3. Аналитика во время и после мероприятия
|
||||
|
||||
1. Организатор в админ-панели видит:
|
||||
- Общее количество гостей.
|
||||
- Количество пришедших.
|
||||
- Процент посещаемости.
|
||||
- Список последних входов.
|
||||
2. Может быстро проверить по ФИО, пришёл ли конкретный человек.
|
||||
3. Может выгрузить отчёт в Excel с отметкой о посещении и временем входа для каждого участника.
|
||||
|
||||
## Роли пользователей
|
||||
|
||||
| Роль | Возможности |
|
||||
|---|---|
|
||||
| Администратор | Полный доступ: управление пользователями, всеми мероприятиями, гостями, рассылка, аналитика, отчёты |
|
||||
| Организатор | Создание и управление своими мероприятиями, гостями, рассылка, аналитика, отчёты |
|
||||
| Сотрудник на входе | Страница `/check-in`: доступ только к назначенным мероприятиям, сканирование QR, поиск, отметка входа |
|
||||
|
||||
Пользователи создаются и управляются через веб-интерфейс администратора. При первом запуске открывается страница создания первого администратора.
|
||||
|
||||
## Архитектура
|
||||
|
||||
```text
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────────┐
|
||||
│ Браузер │──────│ Flask │──────│ PostgreSQL │
|
||||
│ (Admin/ │ │ (Python) │ │ (inviteme.db) │
|
||||
│ Organizer/ │ │ │ └─────────────────┘
|
||||
│ Staff) │ └──────┬──────┘
|
||||
└─────────────┘ │
|
||||
┌────────────────┼────────────────┐
|
||||
│ │ │
|
||||
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
|
||||
│ QR-сервис │ │ Email │ │ Excel │
|
||||
│ (qrcode) │ │ (SMTP) │ │ (openpyxl)│
|
||||
└────────────┘ └───────────┘ └───────────┘
|
||||
```
|
||||
|
||||
- **Flask** обрабатывает HTTP-запросы и рендерит HTML-шаблоны.
|
||||
- **PostgreSQL** хранит пользователей, мероприятия, гостей, RSVP и историю входов. SQLite используется только локально.
|
||||
- **RQ worker + Redis** выполняют массовую рассылку в фоне и сохраняют прогресс независимо от браузера.
|
||||
- **Локальная файловая система** хранит фото и QR-коды.
|
||||
- **SMTP** используется для отправки приглашений.
|
||||
|
||||
## Поток данных
|
||||
|
||||
### Импорт гостей
|
||||
|
||||
```text
|
||||
Создание мероприятия в админ-панели
|
||||
↓
|
||||
Excel-файл
|
||||
↓
|
||||
excel_service.read_guests_from_excel()
|
||||
↓
|
||||
Создание/обновление записей Guest в базе данных (с привязкой к мероприятию)
|
||||
↓
|
||||
qr_service.generate_qr_for_guest() для каждого нового гостя
|
||||
↓
|
||||
Готовность к рассылке
|
||||
```
|
||||
|
||||
### Рассылка приглашений
|
||||
|
||||
```text
|
||||
Гость с invitation_sent = false
|
||||
↓
|
||||
email_service.send_invitation()
|
||||
↓
|
||||
HTML-шаблон + QR-код как вложение
|
||||
↓
|
||||
Отправка через SMTP
|
||||
↓
|
||||
invitation_sent = true
|
||||
```
|
||||
|
||||
### Проверка входа
|
||||
|
||||
```text
|
||||
QR-код → URL /check-in?token=<uuid>
|
||||
↓
|
||||
Загрузка карточки гостя
|
||||
↓
|
||||
POST /api/check-in/<token>
|
||||
↓
|
||||
Проверка: уже отмечен?
|
||||
↓
|
||||
Создание записи CheckIn или возврат ошибки 409
|
||||
```
|
||||
|
||||
## Формат Excel
|
||||
|
||||
Обязательные столбцы (имена должны совпадать точно):
|
||||
|
||||
| Столбец | Описание | Пример |
|
||||
|---|---|---|
|
||||
| `Отдел` | Название отдела | IT |
|
||||
| `ФИО` | Полное имя | Иванов Иван Иванович |
|
||||
| `Должность` | Должность гостя | Разработчик |
|
||||
| `e-mail` | Email для рассылки | ivanov@company.com |
|
||||
| `название фото` | Имя файла фото | ivanov.jpg |
|
||||
|
||||
> Важно: значение в столбце `название фото` должно точно совпадать с именем загруженного файла, включая расширение.
|
||||
|
||||
## Хранение файлов
|
||||
|
||||
```text
|
||||
instance/uploads/
|
||||
├── photos/ # Фото гостей
|
||||
├── qrcodes/ # Сгенерированные QR-коды
|
||||
├── reports/ # Выгруженные отчёты
|
||||
└── temp/ # Временные файлы при загрузке
|
||||
```
|
||||
|
||||
Эти папки создаются автоматически при старте приложения.
|
||||
|
||||
## Ограничения и предположения
|
||||
|
||||
- Production-конфигурация использует PostgreSQL и рассчитана на мероприятия с сотнями или тысячами гостей при одном экземпляре приложения.
|
||||
- Все фото и QR-коды хранятся локально на диске. Перед переносом на другой сервер их нужно копировать отдельно (они не хранятся в git).
|
||||
- Массовая рассылка писем выполняется фоновым worker-процессом; результат и ошибка сохраняются для каждого гостя.
|
||||
- QR-код содержит прямую ссылку на страницу входа. При переносе на другой хост необходимо указать корректный `BASE_URL` в `.env` или при загрузке Excel.
|
||||
|
||||
## Чеклист перед мероприятием
|
||||
|
||||
- [ ] Созданы учётные записи организаторов и сотрудников на входе.
|
||||
- [ ] Создано мероприятие в админ-панели.
|
||||
- [ ] Подготовлен Excel со списком гостей.
|
||||
- [ ] Собраны и загружены фото всех гостей.
|
||||
- [ ] Загружен Excel в админ-панель в рамках нужного мероприятия.
|
||||
- [ ] Проверено, что QR-коды сгенерированы.
|
||||
- [ ] Настроен SMTP и отправлено тестовое письмо.
|
||||
- [ ] Разосланы приглашения.
|
||||
- [ ] Протестирована страница `/check-in` на телефоне/планшете.
|
||||
- [ ] Проверена работа камеры и сканирования QR.
|
||||
- [ ] Проверен поиск по ФИО.
|
||||
- [ ] Настроено резервное копирование базы и загруженных файлов.
|
||||
|
||||
## Распространённые проблемы
|
||||
|
||||
### Гость не находится по QR
|
||||
- Проверьте, что гость не заблокирован (`is_active = true`).
|
||||
- Проверьте, что QR-код не был перегенерирован, а старый распечатан.
|
||||
|
||||
### Фото не отображается на странице входа
|
||||
- Проверьте точное совпадение имени файла в Excel и в папке `uploads/photos/`.
|
||||
- Проверьте, что файл действительно загружен.
|
||||
|
||||
### Письма не отправляются
|
||||
- Проверьте настройки SMTP в `.env`.
|
||||
- Для Yandex/Google используйте пароль приложения, а не основной пароль.
|
||||
- Проверьте логи консоли Flask.
|
||||
|
||||
### QR-код ведёт на localhost
|
||||
- Укажите `BASE_URL` в `.env`.
|
||||
- Или введите базовый URL при загрузке Excel в админ-панели.
|
||||
|
||||
## История изменений
|
||||
|
||||
- **2026-08-28** — Первая рабочая версия (MVP). Реализованы импорт Excel, QR-коды, рассылка, check-in, аналитика.
|
||||
- **2026-08-28** — Добавлены пользователи, роли и поддержка нескольких мероприятий.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Развёртывание InviteMe
|
||||
|
||||
Для боевого сервера используйте инструкцию [PRODUCTION.md](PRODUCTION.md). Она включает PostgreSQL, Redis, автоматический HTTPS, миграции, закрытое хранение файлов и резервное копирование.
|
||||
|
||||
## Локальная разработка
|
||||
|
||||
```bash
|
||||
python -m venv venv
|
||||
venv/bin/pip install -r requirements-dev.txt
|
||||
venv/bin/python run.py
|
||||
```
|
||||
|
||||
На Windows замените `venv/bin/` на `venv\Scripts\`.
|
||||
|
||||
Локальный режим:
|
||||
|
||||
- слушает только `127.0.0.1`;
|
||||
- использует SQLite в `instance/inviteme.db`;
|
||||
- хранит пользовательские файлы в закрытом каталоге `instance/uploads`;
|
||||
- разрешает создание первого администратора через `/setup`;
|
||||
- не предназначен для доступа из локальной сети или интернета.
|
||||
@@ -0,0 +1,90 @@
|
||||
# Развёртывание InviteMe в production
|
||||
|
||||
## 1. Сервер и DNS
|
||||
|
||||
Используйте отдельный Linux-сервер с актуальными Docker Engine и Docker Compose. Создайте `A`/`AAAA`-запись домена InviteMe и направьте её на сервер. Разрешите входящие TCP 80/443 и UDP 443. Не публикуйте наружу PostgreSQL, Redis и порт 8888.
|
||||
|
||||
## 2. Конфигурация
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
openssl rand -hex 32
|
||||
openssl rand -hex 32
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
Запишите полученные значения в `SECRET_KEY`, `CREDENTIAL_ENCRYPTION_KEY` и `POSTGRES_PASSWORD`. В `DOMAIN` укажите публичное имя сервера без схемы и пути.
|
||||
|
||||
Ограничьте доступ к файлу конфигурации:
|
||||
|
||||
```bash
|
||||
chmod 600 .env
|
||||
```
|
||||
|
||||
`CREDENTIAL_ENCRYPTION_KEY` защищает сохранённые SMTP/EWS/IMAP-пароли. При его потере или изменении расшифровать эти пароли будет невозможно, поэтому сохраните ключ в менеджере паролей.
|
||||
|
||||
## 3. Первый запуск
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
docker compose ps
|
||||
docker compose exec inviteme flask --app run.py create-admin
|
||||
```
|
||||
|
||||
В `docker compose ps` должны быть запущены приложение, отдельный `worker`, PostgreSQL, Redis и Caddy. Массовая рассылка выполняется worker-процессом и не зависит от открытой страницы браузера.
|
||||
|
||||
Веб-страница первичной настройки в production отключена. Команда создания администратора проверяет сложность пароля и не принимает пароль аргументом командной строки.
|
||||
|
||||
Проверьте состояние приложения:
|
||||
|
||||
```bash
|
||||
curl -fsS https://events.example.com/healthz
|
||||
```
|
||||
|
||||
Замените адрес на значение `DOMAIN`.
|
||||
|
||||
После создания пользователей откройте каждое мероприятие и назначьте сотрудников на входе. Неназначенный сотрудник не видит мероприятие и его гостей.
|
||||
|
||||
## 4. Резервное копирование
|
||||
|
||||
Копируйте и PostgreSQL, и закрытый том приложения. Дамп базы не содержит фотографии гостей, QR-коды и сгенерированные билеты.
|
||||
|
||||
```bash
|
||||
docker compose exec -T database pg_dump -U inviteme -d inviteme -Fc > inviteme.dump
|
||||
docker run --rm --volumes-from "$(docker compose ps -q inviteme)" \
|
||||
-v "$PWD":/backup alpine \
|
||||
tar -czf /backup/inviteme-uploads.tar.gz -C /app/instance .
|
||||
```
|
||||
|
||||
Храните резервные копии в зашифрованном виде и регулярно проверяйте восстановление. Никогда не добавляйте `.env` и бэкапы в Git.
|
||||
|
||||
## 5. Обновление
|
||||
|
||||
Сначала создайте свежую резервную копию, затем выполните:
|
||||
|
||||
```bash
|
||||
git pull --ff-only
|
||||
docker compose build --pull
|
||||
docker compose up -d
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
Перед запуском Waitress сервис автоматически выполняет `flask db upgrade`. При ошибке миграции приложение не стартует — восстановите резервную копию или исправьте миграцию до повторного запуска.
|
||||
|
||||
## 6. Переход с существующего SQLite
|
||||
|
||||
Не подключайте старый SQLite-файл к production-стеку. Сначала сохраните полную копию `instance/`. Новая версия при локальном запуске переносит старые файлы из `app/static/uploads` в закрытый каталог, но записи из SQLite в PostgreSQL нужно переносить как отдельную контролируемую операцию и проверять до переключения DNS.
|
||||
|
||||
После переноса данных назначьте сотрудников нужным мероприятиям: новая production-модель не даёт роли `staff` автоматический доступ ко всем событиям.
|
||||
|
||||
## 7. Эксплуатация
|
||||
|
||||
```bash
|
||||
docker compose logs -f --tail=200 inviteme
|
||||
docker compose logs -f --tail=200 worker
|
||||
docker compose restart inviteme
|
||||
docker compose restart worker
|
||||
docker compose exec inviteme flask --app run.py routes
|
||||
```
|
||||
|
||||
Контролируйте свободное место, ответы HTTP 5xx, состояние worker, неудачные входы, ошибки отправки почты и возраст последней резервной копии PostgreSQL. Ротацию секретов выполняйте только как плановое обслуживание.
|
||||
Reference in New Issue
Block a user