feat: Добавить систему мультитенантности с регистрацией магазинов

Реализована полноценная система мультитенантности на базе django-tenants.
Каждый магазин получает изолированную схему БД и поддомен.

Основные компоненты:

Django-tenants интеграция:
- Модели Client (тенант) и Domain в приложении tenants/
- Разделение на SHARED_APPS и TENANT_APPS
- Public schema для общей админки
- Tenant schemas для изолированных данных магазинов

Система регистрации магазинов:
- Публичная форма регистрации на /register/
- Модель TenantRegistration для заявок со статусами (pending/approved/rejected)
- Валидация schema_name (латиница, 3-63 символа, уникальность)
- Проверка на зарезервированные имена (admin, api, www и т.д.)
- Админ-панель для модерации заявок с кнопками активации/отклонения

Система подписок:
- Модель Subscription с планами (триал 90 дней, месяц, квартал, год)
- Автоматическое создание триальной подписки при активации
- Методы is_expired() и days_left() для проверки статуса
- Цветовая индикация в админке (зеленый/оранжевый/красный)

Приложения:
- tenants/ - управление тенантами, регистрация, подписки
- shops/ - точки магазинов/самовывоза (tenant app)
- Обновлены миграции для всех приложений

Утилиты:
- switch_to_tenant.py - переключение между схемами тенантов
- Обновлены image_processor и image_service

Конфигурация:
- urls_public.py - роуты для public schema (админка + регистрация)
- urls.py - роуты для tenant schemas (магазины)
- requirements.txt - добавлены django-tenants, django-environ, phonenumber-field

Документация:
- DJANGO_TENANTS_SETUP.md - настройка мультитенантности
- TENANT_REGISTRATION_GUIDE.md - руководство по регистрации
- QUICK_START.md - быстрый старт
- START_HERE.md - общая документация

Использование:
1. Пользователь: http://localhost:8000/register/ → заполняет форму
2. Админ: http://localhost:8000/admin/ → активирует заявку
3. Результат: http://{schema_name}.localhost:8000/ - готовый магазин

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-27 19:13:10 +03:00
parent 4b44624f86
commit 097d4ea304
43 changed files with 3186 additions and 553 deletions

View File

@@ -0,0 +1,324 @@
# Руководство по системе регистрации тенантов
## Что реализовано
Создана полноценная система регистрации новых магазинов (тенантов) с ручной модерацией администратором.
### 1. Модели данных ([tenants/models.py](myproject/tenants/models.py))
#### Client (обновлена)
- Добавлен `db_index` для поля `name` (ускорение поиска)
- Изменено поле `phone` на `PhoneNumberField` (поддержка РБ/РФ форматов)
- Обновлен `help_text` для `owner_email` (один email может быть у нескольких магазинов для супер-админа)
#### TenantRegistration (новая)
Модель заявки на регистрацию:
- `shop_name` - название магазина
- `schema_name` - желаемый поддомен (с валидацией regex)
- `owner_email`, `owner_name`, `phone` - контактные данные
- `status` - статус заявки: pending/approved/rejected
- `processed_at`, `processed_by` - данные обработки
- `tenant` - ссылка на созданный тенант после активации
#### Subscription (новая)
Модель подписки:
- `plan` - тип плана (триал 90 дней, месяц, квартал, год)
- `started_at`, `expires_at` - период действия
- `is_active`, `auto_renew` - статус и автопродление
- Методы: `is_expired()`, `days_left()`, `create_trial(client)`
#### RESERVED_SCHEMA_NAMES
Список зарезервированных поддоменов (admin, api, www, и т.д.)
---
### 2. Админ-панель ([tenants/admin.py](myproject/tenants/admin.py))
#### ClientAdmin (обновлена)
- Добавлена колонка `subscription_status` с цветовой индикацией
- Разрешено редактирование `schema_name` при создании нового тенанта
- Запрещено удаление тенантов через админку (для безопасности)
#### TenantRegistrationAdmin (новая)
Функции:
- Список заявок с фильтрами по статусу и дате
- Кнопки "Активировать" / "Отклонить" для каждой заявки
- Массовые действия для обработки нескольких заявок
- При активации:
- Создается тенант (Client)
- Создается домен (например: myshop.localhost)
- Создается триальная подписка на 90 дней
- Заявка помечается как "Одобрено"
#### SubscriptionAdmin (новая)
- Просмотр и управление подписками
- Цветовая индикация истекающих подписок (красный < 0 дней, оранжевый < 7 дней)
---
### 3. Публичная форма регистрации
#### [tenants/forms.py](myproject/tenants/forms.py) - TenantRegistrationForm
Валидация:
- `schema_name`: приведение к lowercase, проверка длины (3-63 символа), проверка на зарезервированные имена, проверка уникальности
- `owner_email`: проверка на дубликаты pending заявок
#### [tenants/views.py](myproject/tenants/views.py)
- `TenantRegistrationView` - форма регистрации
- `RegistrationSuccessView` - страница благодарности
#### HTML шаблоны
- [base.html](myproject/tenants/templates/tenants/base.html) - базовый шаблон с Bootstrap 5
- [registration_form.html](myproject/tenants/templates/tenants/registration_form.html) - красивая форма с валидацией
- [registration_success.html](myproject/tenants/templates/tenants/registration_success.html) - страница с инструкциями
---
## Как использовать
### Для пользователей (владельцев будущих магазинов)
1. Откройте публичную форму регистрации:
```
http://localhost:8000/register/
```
2. Заполните форму:
- Название магазина
- Желаемый поддомен (только латиница, цифры, дефис)
- Ваше имя
- Email
- Телефон
3. После отправки увидите страницу благодарности
4. Ожидайте активации администратором (в течение 24 часов)
---
### Для администратора
1. Войдите в админ-панель:
```
http://localhost:8000/admin/
```
2. Перейдите в раздел "Заявки на регистрацию"
3. Увидите список заявок со статусом "Ожидает проверки"
4. Для активации заявки:
- Кликните на кнопку "Активировать" справа от заявки
- ИЛИ выберите несколько заявок и используйте массовое действие
5. Что происходит при активации:
- Создается новый тенант (Client) с указанным schema_name
- Создается домен `{schema_name}.localhost`
- Создается триальная подписка на 90 дней
- Заявка помечается как "Одобрено"
- В поле "Созданный тенант" появляется ссылка на тенант
6. Для отклонения:
- Кликните "Отклонить"
- Заявка помечается как "Отклонено"
---
## Доступ к магазинам
После активации магазин доступен по адресу:
```
http://{schema_name}.localhost:8000/
```
Например, для магазина с `schema_name=myshop`:
```
http://myshop.localhost:8000/
```
---
## Управление подписками
### Просмотр подписок
1. Админ-панель → "Подписки"
2. Видны все подписки с информацией:
- Тип плана
- Дата начала/окончания
- Осталось дней
- Истекла или нет
### Продление подписки
1. Откройте подписку тенанта
2. Измените:
- `expires_at` - новую дату окончания
- `plan` - новый тип плана (если меняется)
3. Сохраните
### Типы планов
- **Триальный (90 дней)** - автоматически при создании
- **Месячный** - 30 дней
- **Квартальный** - 90 дней
- **Годовой** - 365 дней
---
## Технические детали
### Валидация schema_name
Regex: `^[a-z0-9](?:[a-z0-9\-]{0,61}[a-z0-9])?$`
Правила:
- Только латинские буквы в нижнем регистре
- Цифры и дефис разрешены
- Длина 3-63 символа
- Не может начинаться или заканчиваться дефисом
- Не совпадает с зарезервированными именами
### Зарезервированные имена
```python
RESERVED_SCHEMA_NAMES = [
'public', 'admin', 'api', 'www', 'mail', 'ftp', 'smtp',
'static', 'media', 'assets', 'cdn', 'app', 'web',
'billing', 'register', 'login', 'logout', 'dashboard',
'test', 'dev', 'staging', 'production', 'demo'
]
```
### Email для супер-админа
Один email может использоваться для нескольких магазинов (полезно для вас как супер-админа для входа в разные тенанты).
Для обычных пользователей форма проверяет наличие pending заявок с таким же email.
---
## Что дальше (рекомендации)
### 1. Email-уведомления
Добавить отправку писем:
- Пользователю при активации заявки
- Пользователю при истечении подписки (за 7 дней, за 1 день)
- Админу при новой заявке
### 2. Биллинг
Создать страницу `/billing/` где владелец магазина может:
- Посмотреть текущую подписку
- Продлить подписку
- Оплатить через платежную систему
### 3. Middleware для is_active
Если нужна жесткая блокировка доступа к деактивированным магазинам, создать middleware:
```python
# tenants/middleware.py
class TenantStatusMiddleware:
def __call__(self, request):
if hasattr(request, 'tenant'):
if not request.tenant.is_active:
# Показать страницу "Магазин заблокирован"
pass
sub = request.tenant.subscription
if sub.is_expired():
# Редирект на /billing/renew/
pass
return self.get_response(request)
```
### 4. Автоматическая очистка
Создать команду для удаления старых отклоненных заявок:
```bash
python manage.py cleanup_old_registrations --days=30
```
---
## Структура файлов
```
myproject/tenants/
├── models.py # Модели Client, TenantRegistration, Subscription
├── admin.py # Админ-панель с функционалом активации
├── forms.py # Форма регистрации с валидацией
├── views.py # Views для публичной регистрации
├── urls.py # Роуты /register/ и /register/success/
└── templates/tenants/
├── base.html # Базовый шаблон
├── registration_form.html # Форма регистрации
└── registration_success.html # Страница благодарности
```
---
## Тестирование
### 1. Регистрация магазина
```bash
# Запустите сервер
python manage.py runserver
# Откройте браузер
http://localhost:8000/register/
# Заполните форму:
Название: Тестовый магазин
Поддомен: testshop
Имя: Иван Иванов
Email: test@example.com
Телефон: +375291234567
# Отправьте заявку
```
### 2. Активация через админку
```bash
# Войдите в админку
http://localhost:8000/admin/
# Логин/пароль супер-админа
# Перейдите в "Заявки на регистрацию"
# Нажмите "Активировать" напротив заявки
```
### 3. Проверка созданного магазина
```bash
# Откройте браузер
http://testshop.localhost:8000/
# Должна открыться страница магазина
```
---
## Поддержка
При возникновении проблем проверьте:
1. Миграции применены: `python manage.py migrate_schemas --shared`
2. В `settings.py` приложение `tenants` в `SHARED_APPS`
3. В `urls_public.py` подключены роуты tenants
4. Виртуальное окружение активировано
5. `phonenumber_field` установлен
---
**Система готова к использованию!**
Теперь вы можете:
- Принимать заявки на регистрацию
- Модерировать их через админку
- Управлять подписками
- Контролировать доступ к магазинам