Статический аудит: архитектура, приложения/модели, сильные стороны, покрытие тестами, безопасность, конфигурация, техдолг по приоритетам, план действий. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
18 KiB
Аудит проекта 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 (иерархические деревья).
Ключевые архитектурные решения
- Единый справочный слой (
core.Directory/DirectoryType) — иерархические справочники (организации, отделы, должности, регионы, типы/бренды оборудования, статусы). На него опирается почти всё. Реализовано аккуратно, с автоперестройкой дерева после миграций и импорта. - Интеграции по единому паттерну: сервис-клиент → sync-сервис → сигналы → Celery-задачи → management-команды. Благодаря этому AD лёг рядом с Mattermost без изобретения велосипеда.
- Единый шелл фронта (
templates/index.html) + страницы-фрагменты HTMX. Общие утилиты вынесены (core/utils/htmx.py,permissions.py,templatetags/common_tags.py). - Роли через
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. Технический долг — сводка по приоритетам
🔴 Высокий
- Незакоммиченная работа. В рабочем дереве 116 изменённых/новых файлов — целые модули (feed, tasks, employee-detail, equipment-каталог, виджеты) не в истории git. Последний коммит —
c3f56f8. Риск потери работы; история не отражает реальное состояние. Закоммитить логическими блоками. - Секреты открытым текстом (см. §6).
🟡 Средний
- Тесты для
tasks,employees,visit_manager— критичная бизнес-логика без покрытия. - Нет CI — тесты не гоняются автоматически на push; регрессии всплывают вручную (в этой сессии дважды находились сломанные смоук-URL).
- Рост числа сигналов на
Employee.post_save(9 ресиверов между AD и Mattermost, у каждого свой pre_save-кеш). При третьей интеграции (Carbonio в планах) станет хрупким — напрашивается единый диспетчер изменений. - Reverse-миграции частично ломаются на восстановлении non-null полей (напр.
0018_equipment_model_catalog) — forward безупречен, но откат в dev упадёт. Приемлемо, но стоит знать.
🟢 Низкий
- Дубль зависимости:
import-export==0.3.1(устаревший неофициальный) соседствует сdjango-import-export==4.4.0. Проверить и убрать лишнее. - Единый язык кода/комментариев/verbose_name.
- Мёртвые артефакты:
db.sqlite3в корне,.env2. - Портал наполовину макет: email, chat, документы, календарь, часть HR — статичные страницы без бэкенда. Стоит явно решить, что реализуем, а что убрать из меню.
9. Рекомендации (порядок действий)
- Закоммитить накопленное логическими блоками — снять риск №1 немедленно.
- Зашифровать секреты
ConnectorConfigперед подключением живого AD в прод — именно эта логика трогает внешнюю систему, где ошибка видна всей компании. - Покрыть тестами
tasksиemployees— минимум контроль доступа и валидацию. Мок-сценарии для AD/Mattermost уже почти готовые тесты. - Поднять минимальный CI (GitHub Actions / GitLab CI):
manage.py check+ прогон тестов +makemigrations --check. Дёшево, ловит регрессии автоматически. - Написать README с процессом запуска (dev-сервер, Tailwind-сборка, Celery, тесты) и деплоя.
- Отрефакторить сигналы
Employeeв единый диспетчер до третьей интеграции. - Причесать: удалить
db.sqlite3/.env2, разобраться с дублемimport-export, определиться с макет-страницами.
10. Вывод
Фундамент выбран правильно и выдержан — проект масштабируется добавлением модулей, а не переписыванием. Паттерны интеграций, справочный слой, безопасность, борьба с N+1 — всё на уровне выше среднего для внутренней системы. Основной долг — процессный, а не архитектурный: закоммитить работу, зашифровать секреты, дописать тесты на критичные модули и поднять CI. Закрыв эти пункты, проект будет готов к спокойной эксплуатации и дальнейшему росту.
Аудит подготовлен на основе статического анализа кода, конфигурации, миграций, тестов и git-истории. Данные собраны инструментально (manage.py check --deploy, makemigrations --check, подсчёт моделей/тестов/строк).