Игорь Надеин ddf22c47da fix: автоматическое обновление JWT токенов
- ApiClient теперь обрабатывает 401 и 403 ошибки для обновления токена
- Добавлен JWTRefreshMiddleware для проактивного обновления токенов за 5 минут до истечения
- Добавлена настройка JWT_REFRESH_THRESHOLD_MINUTES (по умолчанию 5)
- Исправлена проблема с периодическими 403 ошибками после истечения access token
2025-11-20 18:52:29 +03:00

147 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Generated by Django 5.2.4 on 2025-11-20 12:57
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='NotificationCategory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(choices=[('communications', 'Коммуникации'), ('documents', 'Документы'), ('requests', 'Заявления'), ('calendar', 'Календарь'), ('department', 'Отдел'), ('profile', 'Профиль'), ('feed', 'Новости'), ('system', 'Система')], max_length=50, unique=True, verbose_name='Код')),
('name', models.CharField(max_length=100, verbose_name='Название')),
('description', models.TextField(blank=True, verbose_name='Описание')),
('icon', models.CharField(default='bi-bell', help_text='Bootstrap icon класс', max_length=50, verbose_name='Иконка')),
('color', models.CharField(default='primary', help_text='Bootstrap цвет', max_length=20, verbose_name='Цвет')),
('is_active', models.BooleanField(default=True, verbose_name='Активна')),
('order', models.IntegerField(default=0, verbose_name='Порядок')),
],
options={
'verbose_name': 'Категория уведомлений',
'verbose_name_plural': 'Категории уведомлений',
'db_table': 'notifications_category',
'ordering': ['order', 'name'],
},
),
migrations.CreateModel(
name='NotificationType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('code', models.CharField(help_text='Уникальный код типа, например: chat_new_message', max_length=100, unique=True, verbose_name='Код')),
('name', models.CharField(max_length=200, verbose_name='Название')),
('description', models.TextField(blank=True, verbose_name='Описание')),
('default_enabled', models.BooleanField(default=True, verbose_name='Включено по умолчанию')),
('default_channels', models.JSONField(default=dict, help_text='{"web": true, "email": false, "telegram": false}', verbose_name='Каналы по умолчанию')),
('priority', models.CharField(choices=[('low', 'Низкий'), ('normal', 'Обычный'), ('high', 'Высокий'), ('urgent', 'Срочный')], default='normal', max_length=20, verbose_name='Приоритет')),
('is_required', models.BooleanField(default=False, help_text='Нельзя отключить (например, системные уведомления)', verbose_name='Обязательное')),
('is_groupable', models.BooleanField(default=True, help_text='Объединять несколько похожих уведомлений', verbose_name='Можно группировать')),
('grouping_window_minutes', models.IntegerField(default=5, help_text='Группировать уведомления за последние N минут', verbose_name='Окно группировки (минуты)')),
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создан')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлен')),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='types', to='notifications.notificationcategory', verbose_name='Категория')),
],
options={
'verbose_name': 'Тип уведомления',
'verbose_name_plural': 'Типы уведомлений',
'db_table': 'notifications_type',
'ordering': ['category__order', 'name'],
},
),
migrations.CreateModel(
name='NotificationTemplate',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('channel', models.CharField(choices=[('web', 'Веб'), ('email', 'Email'), ('telegram', 'Telegram'), ('whatsapp', 'WhatsApp'), ('wechat', 'WeChat')], max_length=20, verbose_name='Канал')),
('title_template', models.TextField(help_text='Django template синтаксис', verbose_name='Шаблон заголовка')),
('message_template', models.TextField(help_text='Django template синтаксис', verbose_name='Шаблон сообщения')),
('html_template', models.TextField(blank=True, help_text='Для email', verbose_name='HTML шаблон')),
('action_button_template', models.CharField(blank=True, max_length=200, verbose_name='Шаблон кнопки действия')),
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создан')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлен')),
('notification_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='templates', to='notifications.notificationtype', verbose_name='Тип уведомления')),
],
options={
'verbose_name': 'Шаблон уведомления',
'verbose_name_plural': 'Шаблоны уведомлений',
'db_table': 'notifications_template',
'unique_together': {('notification_type', 'channel')},
},
),
migrations.CreateModel(
name='Notification',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255, verbose_name='Заголовок')),
('message', models.TextField(verbose_name='Сообщение')),
('short_message', models.CharField(blank=True, help_text='Для превью', max_length=150, verbose_name='Краткое сообщение')),
('object_id', models.PositiveIntegerField(blank=True, null=True, verbose_name='ID объекта')),
('metadata', models.JSONField(blank=True, default=dict, help_text='Дополнительные данные для рендеринга', verbose_name='Метаданные')),
('action_url', models.CharField(blank=True, max_length=500, verbose_name='Ссылка действия')),
('action_text', models.CharField(default='Посмотреть', max_length=100, verbose_name='Текст кнопки')),
('is_read', models.BooleanField(db_index=True, default=False, verbose_name='Прочитано')),
('read_at', models.DateTimeField(blank=True, null=True, verbose_name='Прочитано когда')),
('is_archived', models.BooleanField(default=False, verbose_name='В архиве')),
('archived_at', models.DateTimeField(blank=True, null=True, verbose_name='Заархивировано когда')),
('sent_web', models.BooleanField(default=False, verbose_name='Отправлено на сайт')),
('sent_email', models.BooleanField(default=False, verbose_name='Отправлено на email')),
('sent_telegram', models.BooleanField(default=False, verbose_name='Отправлено в Telegram')),
('sent_whatsapp', models.BooleanField(default=False, verbose_name='Отправлено в WhatsApp')),
('sent_wechat', models.BooleanField(default=False, verbose_name='Отправлено в WeChat')),
('sent_at', models.DateTimeField(blank=True, null=True, verbose_name='Отправлено когда')),
('group_key', models.CharField(blank=True, db_index=True, help_text='Для объединения похожих уведомлений', max_length=255, verbose_name='Ключ группировки')),
('is_grouped', models.BooleanField(default=False, verbose_name='Сгруппировано')),
('grouped_count', models.IntegerField(default=1, help_text='Сколько уведомлений объединено', verbose_name='Количество в группе')),
('created_at', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Создано')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлено')),
('content_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype', verbose_name='Тип объекта')),
('recipient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL, verbose_name='Получатель')),
('notification_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='notifications.notificationtype', verbose_name='Тип')),
],
options={
'verbose_name': 'Уведомление',
'verbose_name_plural': 'Уведомления',
'db_table': 'notifications_notification',
'ordering': ['-created_at'],
'indexes': [models.Index(fields=['recipient', '-created_at'], name='notificatio_recipie_a972ce_idx'), models.Index(fields=['recipient', 'is_read'], name='notificatio_recipie_4e3567_idx'), models.Index(fields=['notification_type', '-created_at'], name='notificatio_notific_c4ec61_idx'), models.Index(fields=['group_key', '-created_at'], name='notificatio_group_k_c495c5_idx')],
},
),
migrations.CreateModel(
name='UserNotificationSettings',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_enabled', models.BooleanField(default=True, verbose_name='Включено')),
('send_web', models.BooleanField(default=True, verbose_name='Отправлять на сайт')),
('send_email', models.BooleanField(default=False, verbose_name='Отправлять на email')),
('send_telegram', models.BooleanField(default=False, verbose_name='Отправлять в Telegram')),
('send_whatsapp', models.BooleanField(default=False, verbose_name='Отправлять в WhatsApp')),
('send_wechat', models.BooleanField(default=False, verbose_name='Отправлять в WeChat')),
('quiet_hours_enabled', models.BooleanField(default=False, verbose_name='Тихий режим включен')),
('quiet_start_time', models.TimeField(blank=True, help_text='Например, 22:00', null=True, verbose_name='Начало тихого режима')),
('quiet_end_time', models.TimeField(blank=True, help_text='Например, 08:00', null=True, verbose_name='Конец тихого режима')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлено')),
('notification_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='notifications.notificationtype', verbose_name='Тип уведомления')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notification_settings', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь')),
],
options={
'verbose_name': 'Настройка уведомлений пользователя',
'verbose_name_plural': 'Настройки уведомлений пользователей',
'db_table': 'notifications_user_settings',
'unique_together': {('user', 'notification_type')},
},
),
]