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

279 lines
8.7 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.

from django.contrib import admin
from django.utils.html import format_html
from .models import (
NotificationCategory,
NotificationType,
Notification,
UserNotificationSettings,
NotificationTemplate,
)
@admin.register(NotificationCategory)
class NotificationCategoryAdmin(admin.ModelAdmin):
list_display = ['name', 'code', 'icon_preview', 'color', 'order', 'is_active']
list_editable = ['order', 'is_active']
list_filter = ['is_active']
search_fields = ['name', 'code', 'description']
ordering = ['order', 'name']
def icon_preview(self, obj):
return format_html('<i class="{}" style="font-size: 1.2em;"></i>', obj.icon)
icon_preview.short_description = 'Иконка'
@admin.register(NotificationType)
class NotificationTypeAdmin(admin.ModelAdmin):
list_display = [
'name',
'code',
'category',
'priority',
'default_enabled',
'is_groupable',
'is_required',
'is_active',
]
list_filter = [
'category',
'priority',
'default_enabled',
'is_groupable',
'is_required',
'is_active',
]
search_fields = ['name', 'code', 'description']
list_editable = ['is_active']
ordering = ['category__order', 'name']
fieldsets = (
('Основная информация', {
'fields': ('category', 'code', 'name', 'description', 'is_active')
}),
('Настройки по умолчанию', {
'fields': ('default_enabled', 'default_channels', 'priority', 'is_required')
}),
('Группировка', {
'fields': ('is_groupable', 'grouping_window_minutes'),
'classes': ('collapse',)
}),
)
@admin.register(Notification)
class NotificationAdmin(admin.ModelAdmin):
list_display = [
'id',
'recipient_name',
'title_short',
'notification_type',
'is_read',
'created_at',
'channels_sent',
]
list_filter = [
'is_read',
'is_archived',
'notification_type__category',
'notification_type',
'created_at',
]
search_fields = [
'title',
'message',
'recipient__first_name',
'recipient__last_name',
'recipient__email',
]
readonly_fields = [
'created_at',
'updated_at',
'read_at',
'sent_at',
'archived_at',
]
date_hierarchy = 'created_at'
ordering = ['-created_at']
fieldsets = (
('Основная информация', {
'fields': ('recipient', 'notification_type', 'title', 'message', 'short_message')
}),
('Связанный объект', {
'fields': ('content_type', 'object_id'),
'classes': ('collapse',)
}),
('Действие', {
'fields': ('action_url', 'action_text')
}),
('Статус', {
'fields': ('is_read', 'read_at', 'is_archived', 'archived_at')
}),
('Доставка', {
'fields': (
'sent_web',
'sent_email',
'sent_telegram',
'sent_whatsapp',
'sent_wechat',
'sent_at',
),
'classes': ('collapse',)
}),
('Группировка', {
'fields': ('group_key', 'is_grouped', 'grouped_count'),
'classes': ('collapse',)
}),
('Метаданные', {
'fields': ('metadata', 'created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def recipient_name(self, obj):
return obj.recipient.get_full_name() or obj.recipient.username
recipient_name.short_description = 'Получатель'
recipient_name.admin_order_field = 'recipient__last_name'
def title_short(self, obj):
return obj.title[:50] + '...' if len(obj.title) > 50 else obj.title
title_short.short_description = 'Заголовок'
def channels_sent(self, obj):
channels = []
if obj.sent_web:
channels.append('🌐 Веб')
if obj.sent_email:
channels.append('📧 Email')
if obj.sent_telegram:
channels.append('✈️ Telegram')
if obj.sent_whatsapp:
channels.append('💬 WhatsApp')
if obj.sent_wechat:
channels.append('💚 WeChat')
return ', '.join(channels) if channels else '—'
channels_sent.short_description = 'Отправлено'
actions = ['mark_as_read', 'mark_as_unread', 'archive_notifications']
def mark_as_read(self, request, queryset):
count = 0
for notification in queryset.filter(is_read=False):
notification.mark_as_read()
count += 1
self.message_user(request, f'Отмечено как прочитанное: {count}')
mark_as_read.short_description = 'Отметить как прочитанное'
def mark_as_unread(self, request, queryset):
count = queryset.filter(is_read=True).update(is_read=False, read_at=None)
self.message_user(request, f'Отмечено как непрочитанное: {count}')
mark_as_unread.short_description = 'Отметить как непрочитанное'
def archive_notifications(self, request, queryset):
count = 0
for notification in queryset.filter(is_archived=False):
notification.archive()
count += 1
self.message_user(request, f'Заархивировано: {count}')
archive_notifications.short_description = 'Архивировать'
@admin.register(UserNotificationSettings)
class UserNotificationSettingsAdmin(admin.ModelAdmin):
list_display = [
'user_name',
'notification_type',
'is_enabled',
'channels_enabled',
'quiet_hours_status',
]
list_filter = [
'is_enabled',
'notification_type__category',
'send_web',
'send_email',
'send_telegram',
'quiet_hours_enabled',
]
search_fields = [
'user__first_name',
'user__last_name',
'user__email',
'notification_type__name',
]
ordering = ['user__last_name', 'notification_type__category__order']
fieldsets = (
('Основная информация', {
'fields': ('user', 'notification_type', 'is_enabled')
}),
('Каналы доставки', {
'fields': ('send_web', 'send_email', 'send_telegram', 'send_whatsapp', 'send_wechat')
}),
('Тихий режим', {
'fields': ('quiet_hours_enabled', 'quiet_start_time', 'quiet_end_time'),
'classes': ('collapse',)
}),
)
def user_name(self, obj):
return obj.user.get_full_name() or obj.user.username
user_name.short_description = 'Пользователь'
user_name.admin_order_field = 'user__last_name'
def channels_enabled(self, obj):
channels = []
if obj.send_web:
channels.append('🌐')
if obj.send_email:
channels.append('📧')
if obj.send_telegram:
channels.append('✈️')
if obj.send_whatsapp:
channels.append('💬')
if obj.send_wechat:
channels.append('💚')
return ' '.join(channels) if channels else '—'
channels_enabled.short_description = 'Каналы'
def quiet_hours_status(self, obj):
if obj.quiet_hours_enabled and obj.quiet_start_time and obj.quiet_end_time:
return f'🌙 {obj.quiet_start_time.strftime("%H:%M")} - {obj.quiet_end_time.strftime("%H:%M")}'
return '—'
quiet_hours_status.short_description = 'Тихий режим'
@admin.register(NotificationTemplate)
class NotificationTemplateAdmin(admin.ModelAdmin):
list_display = [
'notification_type',
'channel',
'is_active',
'updated_at',
]
list_filter = [
'channel',
'is_active',
'notification_type__category',
]
search_fields = [
'notification_type__name',
'title_template',
'message_template',
]
ordering = ['notification_type__category__order', 'channel']
fieldsets = (
('Основная информация', {
'fields': ('notification_type', 'channel', 'is_active')
}),
('Шаблоны', {
'fields': ('title_template', 'message_template', 'action_button_template')
}),
('Email (дополнительно)', {
'fields': ('html_template',),
'classes': ('collapse',)
}),
)