from django.db import models from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError from .base import BaseModel from .fields import EncryptedCharField class ConnectorConfig(BaseModel): CONNECTOR_TYPES = [ ('mattermost', 'Mattermost'), ('ad', 'Active Directory'), ('carbonio', 'Carbonio CE'), ('mail', 'Почта (IMAP/SMTP)'), ('nextcloud', 'Nextcloud'), ('onlyoffice', 'OnlyOffice'), ('asterisk', 'Asterisk AMI'), ] connector_type = models.CharField( _('Тип коннектора'), max_length=20, choices=CONNECTOR_TYPES, unique=True ) is_enabled = models.BooleanField(_('Включён'), default=False) # Подключение host = models.CharField(_('Хост'), max_length=255, blank=True) port = models.PositiveIntegerField(_('Порт'), null=True, blank=True) use_ssl = models.BooleanField(_('Использовать SSL'), default=True) # Аутентификация username = models.CharField(_('Логин'), max_length=255, blank=True) # password/token хранятся в БД зашифрованными (Fernet). max_length задан с # запасом под шифртекст, который длиннее открытого текста. password = EncryptedCharField(_('Пароль'), max_length=512, blank=True) token = EncryptedCharField( _('Токен / API Key'), max_length=1024, blank=True, help_text=_('Bearer-токен или API-ключ, если используется вместо логина/пароля') ) # Дополнительные параметры коннектора extra_config = models.JSONField( _('Дополнительные параметры'), default=dict, blank=True, help_text=_( 'JSON. Nextcloud: verify_ssl, timeout. ' 'OnlyOffice: public_url, callback_base, verify_ssl. ' 'Asterisk AMI: originate_context (from-internal), ' 'sip_channel (PJSIP/{exten}), analog_channel (DAHDI/{exten}), timeout.' ), ) notes = models.TextField(_('Примечания'), blank=True) class Meta: verbose_name = _('Настройки коннектора') verbose_name_plural = _('Настройки коннекторов') ordering = ['connector_type'] def __str__(self): status = '✓' if self.is_enabled else '✗' return f'{status} {self.get_connector_type_display()}' def clean(self): super().clean() if self.is_enabled and not self.host: raise ValidationError({'host': _('Хост обязателен для включённого коннектора')}) if self.connector_type == 'asterisk' and self.is_enabled: errors = {} if not self.username: errors['username'] = _('Для AMI нужен логин менеджера') if not self.password: errors['password'] = _('Для AMI нужен секрет (password)') if errors: raise ValidationError(errors) elif self.is_enabled and not self.token and not self.username: raise ValidationError( _('Для включённого коннектора необходимо указать токен или логин') ) @classmethod def get_for(cls, connector_type: str) -> 'ConnectorConfig | None': """Получить конфиг коннектора. Возвращает None если не найден или отключён.""" try: cfg = cls.objects.get(connector_type=connector_type) return cfg if cfg.is_enabled else None except cls.DoesNotExist: return None