Files
lokses c42b1b533e docs: полный аудит проекта (AUDIT.md)
Статический аудит: архитектура, приложения/модели, сильные стороны,
покрытие тестами, безопасность, конфигурация, техдолг по приоритетам,
план действий.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 17:33:48 +03:00

18 KiB
Raw Permalink Blame History

Аудит проекта ACORE

Система управления аптечной сетью · корпоративный портал + внутренние интеграции Дата аудита: 2026-07-22 · Ветка: main · Django 6.0.3 / Python 3.14 / PostgreSQL


1. Резюме

ACORE — внутренний портал аптечной сети: управление сотрудниками, аптеками, справочниками, ИТ-инвентарём, задачами, лентой публикаций, плюс живые интеграции с Active Directory и Mattermost. Проект в хорошей инженерной форме: архитектура последовательная, паттерны выдержаны, безопасность продумана на уровне выше среднего для внутренних систем. Растёт быстро — 13 приложений, ~14,4 тыс. строк Python (без миграций и тестов).

Общая оценка: 7.5/10. Фундамент добротный. Главные риски не архитектурные, а процессные: неравномерное покрытие тестами, большой объём незакоммиченной работы и хранение секретов интеграций открытым текстом.

Показатель Значение
Приложений 13
Моделей 39
Python-код (без миграций/тестов) ~14 400 строк
Миграций 56
Тестов 127 функций (~2000 строк)
Шаблонов 39 HTML
JS-модулей (кастомных) 12
manage.py check чисто
check --deploy (production) 2 минорных предупреждения
Миграции консистентны да (No changes detected)

2. Архитектура

Технологический стек

  • Backend: Django 6.0.3, PostgreSQL (psycopg2), Django REST Framework (точечно, 5 endpoint-ов).
  • Async: Celery 5.5 + Redis + django-celery-beat/results.
  • Frontend: серверный рендеринг + HTMX (навигация без перезагрузки), Tailwind CSS v4 (сборка через CLI), ванильный JS для виджетов. SPA-фреймворков нет — сознательно.
  • Админка: django-unfold (кастомная тема) + django-import-export.
  • Интеграции: ldap3 (Active Directory), mattermostautodriver + httpx (Mattermost).
  • Контент: django-tinymce (WYSIWYG) + nh3 (санитизация HTML против XSS).
  • Справочники: django-fast-treenode (иерархические деревья).

Ключевые архитектурные решения

  1. Единый справочный слой (core.Directory / DirectoryType) — иерархические справочники (организации, отделы, должности, регионы, типы/бренды оборудования, статусы). На него опирается почти всё. Реализовано аккуратно, с автоперестройкой дерева после миграций и импорта.
  2. Интеграции по единому паттерну: сервис-клиент → sync-сервис → сигналы → Celery-задачи → management-команды. Благодаря этому AD лёг рядом с Mattermost без изобретения велосипеда.
  3. Единый шелл фронта (templates/index.html) + страницы-фрагменты HTMX. Общие утилиты вынесены (core/utils/htmx.py, permissions.py, templatetags/common_tags.py).
  4. Роли через auth.Group, а не поле-enum (миграция 27a917e).

3. Приложения и модели

Приложение Назначение Модели Тесты
core Справочники, конфиги коннекторов, портал-вью Directory, DirectoryType, ConnectorConfig 21 (смоук страниц, PII-доступ, дерево)
employees Сотрудники (кастомный User), права Employee, EmployeePermissionOverride, PermissionGroup 0
locations Аптеки, склады, офисы (MTI) Location, Pharmacy, PharmacyPhone, PharmacyHistory 0
it_manager ИТ-инвентарь, заявки, интернет-договоры Equipment, EquipmentModel (каталог), EquipmentAssignment, Consumable, Ticket, TicketComment, WorkLog, ServiceCall, InternetProvider, InternetContract 10
feed Лента и публикации (TinyMCE) Post, PostComment 40
tasks Задачи (роли Битрикс-стиля) Task, TaskComment 0 ⚠
ad_connector Active Directory (провижининг) ADSyncLog 30
mattermost_connector Mattermost (аккаунты, каналы) MattermostUser/Team/Channel + membership + sync-settings (7) 19
visit_manager Учёт посещений, графики, смены AttendanceLog, WorkShift, Shift, Holiday, Schedule* (9) 0
hiring Заявки на найм HiringRequest 7
utilities Показания счётчиков UtilityReading 0
mail_connector, mailparser, portal_connector Заглушки / служебное — 0

Заметные модели

  • Employee — кастомный AbstractBaseUser, богатый: контакты, PII, локация (аптека/офис/склад), руководитель, интеграционные поля AD/Mattermost, гибкие права через EmployeePermissionOverride (grant/revoke с датами действия). Валидация в clean() разделяет аптечных/офисных сотрудников.
  • EquipmentModel + Equipment (свежий рефакторинг) — каталог моделей отделён от экземпляров: тип+бренд+модель заводятся один раз, экземпляры несут только серийник/локацию. Массовый ввод и массовая выдача.
  • Task — три роли (ответственный/соисполнители/наблюдатели), видимость через for_user(), наблюдатели не меняют статус.

4. Сильные стороны

✅ Безопасность продумана. Отдельный коммит hardening (faa06b9): исправлен open redirect в логине, добавлена аутентификация на API, MIME-валидация загрузок (magic bytes + Pillow verify), HttpOnly/SameSite куки, лимиты загрузки. Есть полноценный production.py (DEBUG=False, SSL-redirect, HSTS, secure cookies) — check --deploy даёт лишь 2 минорных предупреждения.

✅ Разделение PII. Личные данные (телефоны, ИНН, дата рождения, экстренный контакт) отделены от публичных полей — и в DRF-сериализаторах (EmployeeSerializer / EmployeeDetailSerializer), и на детальной странице сотрудника (_can_view_employee_pii: staff / сам / непосредственный руководитель). Покрыто тестами.

✅ XSS-защита ленты. Тело поста (HTML из TinyMCE) всегда прогоняется через nh3 в Post.save() — чистка в модели, а не во view, поэтому защищён любой путь записи. Загрузка картинок отдельным endpoint-ом с проверкой содержимого; data:-URI вырезаются.

✅ Оптимизация запросов. Осознанная борьба с N+1: Prefetch с select_related, агрегаты вместо циклов .count(), distinct=True в Count при OR-джойнах по M2M.

✅ Устойчивость к сбоям. Сигналы обёрнуты в try/except — недоступный Celery-брокер или AD не роняют сохранение сотрудника. Reverse-миграции пишутся (где возможно).

✅ Единый UX-язык. HTMX + Tailwind + переиспользуемые виджеты (people-picker дерево, searchable-select, пагинация). Украинский интерфейс выдержан.


5. Тестирование

127 тестовых функций, ~2000 строк. Покрытие качественное там, где оно есть, но неравномерное:

Покрыто хорошо:

  • feed (40) — публикация, права, санитизация, картинки, комментарии.
  • ad_connector (30) — LDAP-клиент на мок-каталоге ldap3 (MOCK_SYNC), sync-сценарии, сигналы, страница сопоставления.
  • core (21) — смоук всех страниц портала, PII-доступ, дерево справочников.
  • mattermost_connector (19), it_manager (10), hiring (7).

Не покрыто вообще (0 тестов):

  • ⚠ tasks — модуль с нетривиальной логикой видимости и ролей, но без тестов (задача осталась в бэклоге).
  • ⚠ employees — кастомная модель User с валидацией clean(), правами-оверрайдами; критичный код без тестов.
  • ⚠ visit_manager — 9 моделей (графики, смены, посещения) без единого теста.
  • locations, utilities, mail/portal-заглушки.

Инфраструктурный нюанс: discovery manage.py test apps.X падает на структуре пакетов — тесты запускают явными метками модулей (apps.feed.tests). CI отсутствует — тесты гоняются вручную.


6. Безопасность — находки

Приоритет Находка Детали
🔴 Высокий Секреты интеграций открытым текстом ConnectorConfig.password / token — обычные CharField в БД. Пароль сервисной учётки AD и токен Mattermost лежат в базе как есть. Рекомендация: шифрование на уровне поля (django-fernet-fields или аналог) либо вынос в секрет-менеджер/переменные окружения.
🟡 Средний Нет тестов на критичный код доступа tasks.for_user(), employees права, PII-гейтинг частично — регрессии в контроле доступа не отлавливаются автоматически.
🟢 Низкий SECURE_HSTS_PRELOAD не выставлен check --deploy W021 — не даёт подать сайт в preload-список. Косметика.
🟢 Низкий Синхронные операции AD с паролем блокируют запрос Сохранение формы ждёт LDAP-вызов (до 15 с таймаут при недоступном контроллере). Ошибка не роняет сохранение, но UX-задержка возможна.

Хорошо: .env и db.sqlite3 не в git; .gitignore покрывает env/sqlite/pycache/node_modules/шаблон-референс; SECRET_KEY/ALLOWED_HOSTS/DATABASE_URL — из окружения.


7. Конфигурация и деплой

  • Настройки разделены на base / local / production. Production корректен.
  • БД: PostgreSQL и в dev, и в prod (через DATABASE_URL). Лежащий в корне db.sqlite3 — мёртвый артефакт, стоит удалить.
  • Celery: приложение сконфигурировано (acore/celery.py), брокер из env, локально CELERY_TASK_ALWAYS_EAGER=True (без брокера задачи синхронно). Beat-задачи есть в mattermost/ad коннекторах.
  • Локализация: LANGUAGE_CODE='ru', TIME_ZONE='Europe/Kiev', UI на украинском, verbose_name на русском, комментарии — смесь ru/uk. Работает, но единого языка нет.
  • Нет: README, docs/, CI/CD (.github/.gitlab-ci), Makefile, зафиксированного процесса деплоя.

8. Технический долг — сводка по приоритетам

🔴 Высокий

  1. Незакоммиченная работа. В рабочем дереве 116 изменённых/новых файлов — целые модули (feed, tasks, employee-detail, equipment-каталог, виджеты) не в истории git. Последний коммит — c3f56f8. Риск потери работы; история не отражает реальное состояние. Закоммитить логическими блоками.
  2. Секреты открытым текстом (см. §6).

🟡 Средний

  1. Тесты для tasks, employees, visit_manager — критичная бизнес-логика без покрытия.
  2. Нет CI — тесты не гоняются автоматически на push; регрессии всплывают вручную (в этой сессии дважды находились сломанные смоук-URL).
  3. Рост числа сигналов на Employee.post_save (9 ресиверов между AD и Mattermost, у каждого свой pre_save-кеш). При третьей интеграции (Carbonio в планах) станет хрупким — напрашивается единый диспетчер изменений.
  4. Reverse-миграции частично ломаются на восстановлении non-null полей (напр. 0018_equipment_model_catalog) — forward безупречен, но откат в dev упадёт. Приемлемо, но стоит знать.

🟢 Низкий

  1. Дубль зависимости: import-export==0.3.1 (устаревший неофициальный) соседствует с django-import-export==4.4.0. Проверить и убрать лишнее.
  2. Единый язык кода/комментариев/verbose_name.
  3. Мёртвые артефакты: db.sqlite3 в корне, .env2.
  4. Портал наполовину макет: email, chat, документы, календарь, часть HR — статичные страницы без бэкенда. Стоит явно решить, что реализуем, а что убрать из меню.

9. Рекомендации (порядок действий)

  1. Закоммитить накопленное логическими блоками — снять риск №1 немедленно.
  2. Зашифровать секреты ConnectorConfig перед подключением живого AD в прод — именно эта логика трогает внешнюю систему, где ошибка видна всей компании.
  3. Покрыть тестами tasks и employees — минимум контроль доступа и валидацию. Мок-сценарии для AD/Mattermost уже почти готовые тесты.
  4. Поднять минимальный CI (GitHub Actions / GitLab CI): manage.py check + прогон тестов + makemigrations --check. Дёшево, ловит регрессии автоматически.
  5. Написать README с процессом запуска (dev-сервер, Tailwind-сборка, Celery, тесты) и деплоя.
  6. Отрефакторить сигналы Employee в единый диспетчер до третьей интеграции.
  7. Причесать: удалить db.sqlite3/.env2, разобраться с дублем import-export, определиться с макет-страницами.

10. Вывод

Фундамент выбран правильно и выдержан — проект масштабируется добавлением модулей, а не переписыванием. Паттерны интеграций, справочный слой, безопасность, борьба с N+1 — всё на уровне выше среднего для внутренней системы. Основной долг — процессный, а не архитектурный: закоммитить работу, зашифровать секреты, дописать тесты на критичные модули и поднять CI. Закрыв эти пункты, проект будет готов к спокойной эксплуатации и дальнейшему росту.


Аудит подготовлен на основе статического анализа кода, конфигурации, миграций, тестов и git-истории. Данные собраны инструментально (manage.py check --deploy, makemigrations --check, подсчёт моделей/тестов/строк).