36 Commits
Author SHA1 Message Date
Andrey 91fab586fa ✨ feat(deploy): сохранить override и постоянные dev-окружения
Сохранить порядок дополнительных Compose-файлов при обновлении и подключать их помощнику на чтение. Добавить проверки override, постоянных каталогов dev-данных и документацию проекта. Явно задать русский язык в тестах русских подписей.
2026-10-01 18:18:58 +03:00
Andrey 29e2232fc3 ✨ feat(webchat): передавать поля сайта через лоадер и виджет 2026-10-01 12:42:41 +03:00
AndreyandClaude Opus 5 31985fc35c ✨ feat(vk): канал ВКонтакте — сообщество как точка входа
Сообщения сообщества ВКонтакте принимаются через Bots Long Poll: тот же
способ, что у Telegram и MAX, поэтому подключению не нужен ни публичный
адрес, ни доступ извне — установка за NAT работает наравне с остальными.

Позиция потока живёт в poll_marker, адрес сервера и ключ — в памяти
процесса: их выдают на несколько часов, и колонка под них означала бы
запись в базу на каждом цикле опроса. Ответы failed 1-3 восстанавливаются
в том же цикле, иначе подключение висело бы с протухшим ключом до
перезапуска воркера.

Идентификатор сообщества владелец не вводит: его называет сам ключ
доступа, и проверка подключения кладёт его в конфигурацию — как имя бота
у Telegram и MAX. Проверка заодно смотрит настройки сообщества: без Long
Poll и события о входящем сообщении приём невозможен, и об этом честнее
сказать сразу, а не молчать зелёным статусом. Настройки чужого сообщества
при этом не меняются. Нехватка прав у ключа объясняется словами: ВКонтакте
отвечает на неё английским «no access», из которого не видно, что включить.

Имя, логин и фото отправителя ВКонтакте в апдейте не присылает — их
забирает один users.get на пачку сообщений, а не на каждое: на оживлённом
сообществе запрос на реплику упёрся бы в частоту обращений.

Телефона и кнопки «поделиться контактом» у ВКонтакте нет, поэтому просьба
уходит обычным сообщением, как и почтой. Голосовые принимаются, но не
отправляются: провайдер ждёт ogg/opus, а композер пишет webm.

Токен уходит строкой запроса — заголовка авторизации у ВКонтакте нет.
Поэтому адрес больше нигде не печатается как есть: и журнал, и поле
последней ошибки подключения, которое видно в интерфейсе, проходят через
маскирование секретных параметров.

Проверено на живом сообществе: проверка подключения, опрос Long Poll,
приём текста, голосового и фото с подстановкой имени, логина и аватара.
Тесты транспорта и проверки подключения, каталоги переводов, ruff из
корня, tsc и vitest по internal-ui.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 11:02:20 +03:00
Andrey Ermolaev 1ea0faf1c6 Update README.md 2026-09-17 09:38:32 +03:00
AndreyandClaude Opus 5 a41b23a5e2 docs: обложка и ссылки в README, design/ вне гита
Дизайн-хендоффы (design/) — рабочие материалы, а не часть продукта:
сняты с отслеживания и добавлены в .gitignore.

В обоих README перед оглавлением — обложка рабочего места (1024px,
сжата из исходных 3840px) и строка ссылок: телеграм-канал, сайт,
центр помощи.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 12:03:27 +03:00
AndreyandClaude Opus 5 d9bc540bc3 ✨ feat(calls): relay работает сразу после установки, без настройки
Звонок через симметричный NAT или VPN без relay не соединяется — значит relay должен стоять из коробки, а не быть отдельным профилем с выделенным IP и сертификатом. Coturn поднимается вместе со стеком на том же адресе: порт 3478 не спорит с 80 и 443, TURN-over-TLS выключен, потому что он и требовал второго адреса.

Адреса relay и STUN считаются от адреса установки и появляются в настройках сами; вписанное владельцем по-прежнему побеждает. Deploy и doctor больше не требуют второго публичного адреса, README обоих языков переписан: вместо «опционально, выделенный IP» — какие порты открыть.

Проверено: тесты TURN (включая четыре новых на автонастройку), тесты API звонков и адреса установки.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 06:38:47 +03:00
Andrey eeed0ed51a 📝 docs(readme): убрать упоминания бюджета и учёта расхода
Хвост рефакторинга 09fb0177: возможности больше нет, а описания её обещали.
2026-09-14 01:49:33 +03:00
AndreyandClaude Fable 5.1 5396dda8ad ✨ feat(updates): обновление установки из интерфейса
Установка раз в шесть часов и по кнопке проверяет страницу релизов
GitHub. Администратор установки видит баннер о новой версии и ставит
её одной кнопкой; карточка «Обновления» — в «Настройки → Платформа».

Установку выполняет отдельный сервис updater с Docker-сокетом:
backend-app общается с ним только файлами в томе chatballs-updates
(heartbeat, request.json, status.json). Updater принимает лишь релизы
своего репозитория с образами по digest и применяет compose.yaml в
одноразовом контейнере-помощнике, после чего кладёт файл в каталог
установки. Запрос установки попадает в аудит.

Релизный workflow собирает образ updater и пришпиливает его в
compose.yaml; версия бэкенда зашивается в образ (CHATBALLS_VERSION).
Миграции updates.0001 и tenancy.0034 (гранты на таблицу состояния).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-13 00:43:11 +03:00
AndreyandClaude Fable 5.1 ac824e192c 🔒 feat(deploy): три тома секретов и разрешение upstream в nginx на каждом запросе
Том chatballs-secrets целиком монтировался во все сервисы: публичный
backend-app читал пароли платформенной роли и роли миграций, которая обходит
RLS. Теперь томов три — общий, платформенной роли и роли миграций вместе с
паролем владельца кластера — и каждый процесс монтирует только свои.
Генератор переносит файлы существующих установок: копия, побайтная сверка,
только потом удаление из общего тома; повторный запуск ничего не трогает,
пароли не меняются. doctor на работающем стеке проверяет, что backend-app
паролей не видит. Воркер получает алиас platform флагом
CHATBALLS_DB_PLATFORM_ALIAS. README перечисляет три тома в бэкапе.

nginx (dev-шлюз и production-фронтенд) резолвил имя сервиса один раз при
старте: после пересоздания контейнеров старый адрес backend-app достался
backend-platform, и туда уходил WebSocket приложения. Upstream задан
переменной с resolver на DNS Docker. ADR-CHATBALLS-0048 §3, §5.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 14:37:38 +03:00
AndreyandClaude Fable 5.1 f91b3da27a 📄 docs(license): AGPL-3.0
Текст лицензии в LICENSE, раздел и бейдж в обоих README.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 19:52:11 +03:00
AndreyandClaude Fable 5.1 a41da36cc4 📝 docs(readme): продуктовый README на английском и русском
Позиционирование, описание, установка одной командой, функции и
решение проблем. Основная версия — английская, русская рядом.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 19:09:57 +03:00
AndreyandClaude Opus 5 482fa73d95 🚨 fix(lint): привести код к своей же конфигурации ruff и включить её в CI
Линтер никогда не проверял то, что задумано. Конфигурация лежит в корневом
pyproject.toml (`select = ["E","F","I","UP","B","DJ"]`), а запускали ruff из
apps/backend — там конфигурации нет, и он молча брал правила по умолчанию.
Из корня та же команда показывает 1102 нарушения вместо четырнадцати.

Что с ними сделано:

- E501 (968 из 1102) вынесен в ignore с объяснением: длину строки правит
  форматтер, а `ruff format` в этом репозитории не принят — он переписал бы
  374 файла. Порога, до которого можно поднять line-length, нет: 398 строк
  длиннее 120 символов, 222 длиннее 140. Правило, которое всегда красное,
  просто перестают читать.
- Миграции исключены из проверки: их пишет Django.
- Остальные 104 исправлены. Содержательное: assertRaises(Exception) в тесте
  доставки приглашения заменён на конкретный CallInviteDeliveryError; убрана
  мёртвая переменная identity в демо-сиде (у Conversation нет такого поля —
  единственный источник identity это контакт); порядок членов у пяти моделей
  по Django Style Guide; __str__ двум синглтонам инстанса, которые в админке
  показывались бы как «object (1)»; isinstance с кортежем → оператор |.

CI: ruff добавлен в .github/workflows/checks.yml (push и pull request — до
этого на GitHub не проверялось ничего до первого тега), в стадию validate
GitLab и в scripts/check.ps1. Везде запуск из корня репозитория, иначе
повторится та же подмена правил. Версия берётся из requirements бэкенда,
чтобы CI и образ не разъезжались.

Из pre-commit убран ruff-format: висел хук, который при первой же установке
переписал бы 374 файла — это отдельное решение, а не побочный эффект.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 07:39:18 +03:00
AndreyandClaude Opus 5 f74d242988 ✅ fix(tests): признак «мастер пройден» не кэшируется в тестах
Кэш живёт на весь процесс — в проде это ровно то, что нужно: после установки
признак больше не меняется, а до неё его спрашивает каждый запрос. Но в
тестах каждый тест начинается с пустой базы, и первый же из них, который
создал организацию и постучался с нелокальным Host, закрывал мастер всем
остальным в том же процессе: тесты мастера падали с «Invalid host» в
зависимости от порядка сбора.

Заодно README описывает переход на TLS так, как он теперь работает: домен
задаётся в «Настройках», шлюз выписывает на него сертификат сам, прежний
адрес остаётся принятым.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 06:12:01 +03:00
AndreyandClaude Opus 5 8fe66385d6 ✨ feat(release): установка на чистый хост одной командой
Раньше compose монтировал с хоста Caddyfile, init-скрипты базы и генератор
секретов. Из-за этого установка требовала рядом распакованный репозиторий, а
файл, забытый при сборке релиза, Docker молча подменял пустым каталогом — и
стек падал на первом старте у человека. Плюс production-образ backend вообще
не собирался: COPY content ссылался на каталог, которого в репозитории нет,
так что релиза не существовало ни на GitHub, ни на GitLab.

Теперь весь дистрибутив — один compose.yaml со страницы релиза:

  curl -fsSL .../compose.yaml -o compose.yaml
  docker compose up -d --wait

- Caddyfile переехал в свой образ шлюза (caddy validate — в сборке),
  init-скрипты базы — в свой образ postgres, генератор секретов — в
  backend-образ. Bind-mount'ов в production-манифесте не осталось.
- Состояние установки — именованные тома вместо каталогов рабочего каталога.
  Заодно чинит загрузку файлов на Linux: том наследует владельца из образа
  (hub), тогда как bind-mount доставался контейнеру как root:root.
- scripts/pin-release-compose.py закрепляет ссылки на образы по digest и
  падает, если хоть один ключ остался подстановкой.
- GitHub-workflow собирает четыре образа и прикладывает к релизу compose.yaml
  (основной путь) и release.env (для `chatballs deploy`).
- tests/cli/test_release_compose.py держит свойство: манифест без bind-mount'ов
  и полностью закрепляем по digest.
- Из окружения шлюза убраны CHATBALLS_APP_DOMAIN и CHATBALLS_ACME_EMAIL —
  Caddyfile их не читает.

Dev-контур не меняется по смыслу: compose.dev.yaml по-прежнему собирает всё
из исходников и держит состояние в ./data.

AUDIT-TODO.md — временный список остального из аудита; удаляется целиком,
когда закрыт последний пункт.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 04:55:17 +03:00
AndreyandClaude Opus 5 b601a72608 🚚 chore(repo): вернуть выпавшие при переносе корня файлы
При переносе корня в code/chatballs под версионным контролем остались
только CI-файлы старого корня. AGENTS.md, pack.bat, resources/ и
.claude/launch.json физически уцелели, но перестали версионироваться.

- AGENTS.md, pack.bat, resources/ и .claude/launch.json перенесены в корень;
- launch.json: cwd теперь «.», корень репозитория и есть корень приложения;
- pack.bat снова работает — он требует запуска из корня git-репозитория;
- README: после клона нужен «cd chatballs», вложенного пути больше нет;
- .gitignore: Chatballs.zip, архив pack.bat, снова игнорируется.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 21:51:41 +03:00
AndreyandClaude Opus 5 b09fcb6f10 🔥 chore: снесён скрипт переименования CustoCRM → Chatballs
Последний след .env в продукте: скрипт переезда переписывал .env старой
установки и оставлял рядом .env.bak-custocrm. Переименование состоялось,
установок со старыми именами не осталось, а инструмент тянул за собой файл,
которого у продукта нет.

Раздел README про этот переезд удалён вместе со скриптом — он и сам уже читался
бессмысленно: заголовок «Chatballs / hub → Chatballs» и команда
«mv /opt/chatballs /opt/chatballs» получились оттого, что скрипт переименования
однажды прошёлся по собственной документации.

Проверено: pytest tests/cli — 9 passed; bash -n чист по всем скриптам.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 17:25:38 +03:00
AndreyandClaude Opus 5 a2e6672424 🔥 chore: .env убран отовсюду — установка не читает файл с переменными
Переход на запуск без .env (41a4b0d) не был доведён: в дереве остались файл
`env.example`, чтение instance .env во всём deploy/cli и CI, который этот .env
сам же и создавал из `env.example`. Из-за остатков ломались две вещи.

`chatballs deploy` не работал на установке без .env: _normalize_schema_ownership
брал POSTGRES_USER и POSTGRES_DB из файла и падал с «POSTGRES_USER not set»,
хотя установка исправна. Теперь берёт те же значения по умолчанию, что compose.

`chatballs doctor` выдавал пять ложных ошибок подряд: искал в .env домены,
ACME-почту, POSTGRES_PASSWORD и CHATBALLS_SECRET_KEY. Первое задаёт владелец
в «Настройках», второе генерирует в том с секретами первый старт стека —
снаружи, с хоста, этого не видно, и проверки убраны.

Убрано: env.example (из репозитория, release bundle и README); instance_env_file()
и все его чтения в common/compose/deploy/doctor/status; --env-file instance .env
из вызова compose — остаётся только release.env с digest-пинами образов от CI;
создание .env в gateway:validate. Профиль calls и его адреса читаются из
переменных окружения — оттуда же, откуда их берёт сам compose.

Тесты CLI переведены с фиктивного .env на переменные окружения; три проверки
удалённого поведения doctor убраны. Девять оставшихся проходят вообще без .env
— это и есть проверка, что установка теперь обслуживается. Заодно у
test_custocrm_cli.py выправлены окончания строк: в файле были одиночные CR
внутри кода, отчего diff по нему больше содержательной правки.

Проверено: pytest tests/cli — 9 passed; docker compose config валиден с пустым
каталогом инстанса; bash -n чист по всем скриптам CLI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 17:22:29 +03:00
Andrey 41a4b0dd84 ✨ feat: запуск без .env — всё настраивается в UI, и порталы по дизайну
Первичное требование: поднял докер — прошёл мастер — дальше всё в интерфейсе.
Ни одной переменной окружения задавать не нужно и негде.

Запуск и секреты:
- .env.example удалён, мёртвые переменные вычищены; секреты инстанса
  генерирует одноразовый сервис secrets в именованный том, пароли БД —
  через POSTGRES_*_PASSWORD_FILE;
- Caddy: catch-all :80 и on_demand TLS вместо хостов в конфиге — свежая
  коробка отвечает по IP и по любому домену, до мастера дойти можно;
- адрес установки, SMTP и TURN переехали в настройки (InstanceSettings,
  миграции 0027–0030), внешние ссылки строятся от него;
- deploy/cli больше не читает .env; сборка образов в ghcr через GitHub
  Actions, release.env с digest-пинами;
- куки Secure/__Host- выставляются по факту TLS запроса, а не настройкой.

Порталы (дизайн-базлайн v2, кадры PT1–PT8):
- список, карточка портала, библиотека материалов, редактор статьи и
  настройки — ширины и ритм как в остальных разделах;
- файлы статьи: изображение вставляется в текст своим механизмом, файл
  прикрепляется вложением и выводится на портале списком с иконкой формата;
- ссылки на файлы приводятся к относительным: абсолютный хост резал CSP
  портала и картинка не появлялась;
- «Опубликовать» сверяется с сервером и публикует то, что на экране, а не
  ранее выбранную редакцию;
- колонка «Оценки» в списке статей и блок оценок в редакторе.

Первый запуск: полоса «демо-данные устанавливаются» — установка идёт в
worker, и без неё человек видел пустые разделы без объяснения.

Починен фон: вторичное хранилище стало best-effort — при живых остаточных
S3-ключах повторная загрузка файла падала в 500.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 20:37:48 +03:00
AndreyandClaude Fable 5.1 0a8fde42cd 🧹 docs: без вендора — README и ссылки на документы; удалён контент агентов вендора (content/)
Идентификаторы документов PLAN/INVENTORY/RUNBOOK-CUSTOCRM-* → *-CHATBALLS-* (файлы в
локальном docs/ переименованы). Каталог content/ с промптами агентов для продуктов
вендора удалён — в открытом продукте ему не место.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 00:45:49 +03:00
AndreyandClaude Fable 5.1 1a61780dec 🚚 chore!: переименование инфраструктуры CustoCRM/hub → Chatballs с миграцией
- каталог code/custocrm → code/chatballs; CLI custocrm → chatballs; пакеты
  hub_platform → chatballs, hub_backend → chatballs_backend (app labels прежние)
- переменные CUS_* и CUSTOCRM_* → CHATBALLS_*; образы chatballs-*; compose-проект
  и база chatballs (были edevs_hub); роли Postgres chatballs_* (были custocrm_*);
  схема RLS chatballs и GUC chatballs.organization_id; cookie chatballs_*
- CI: APP_DIR code/chatballs, DEPLOY_ROOT /opt/chatballs
- deploy/migrate/rename-to-chatballs.sh — миграция существующей установки без
  потери данных: остановка старого проекта, .env (с резервной копией),
  переименование суперпользователя initdb через временную роль, остальных ролей,
  базы, схемы и функции RLS; проверено на локальном стеке
- dev-стек хранит Postgres в bind-mount data/postgres, как prod (именованный том
  compose.dev был устаревшим снимком и вводил в заблуждение)
- снятие демо удаляет объекты, созданные поверх демо-данных тестировавшим
  (звонки по демо-диалогу), вместо падения на PROTECT
- реальные домены *.custocrm.ru и идентификаторы документов не тронуты

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 22:21:12 +03:00
AndreyandClaude Fable 5.1 dbd7ae3150 🚚 chore: переименование Chatbolls → Chatballs во всём проекте
Бренд, API-схема, TOTP issuer, письма, тексты бота уведомлений, загрузчик виджета
(window.ChatballsChat, типы postMessage), ключи localStorage, id/параметры
виджета помощи, health-service, README, скрипты, демо (пароль
Chatballs-Demo-2026). Макеты в design/baseline не тронуты — это файлы владельца.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 21:43:33 +03:00
AndreyandClaude Fable 5.1 29d061096d 📝 docs(readme): демо повторяет кадры дизайн-базлайна, голосовые из Common Voice (CC0)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 20:56:29 +03:00
AndreyandClaude Fable 5.1 0ca9eb865f ✨ feat(setup)!: мастер первого запуска и демо-набор «Ателье Норд» с точным удалением
Open source: «развернуть за минуту». Никаких параметров в .env и CLI —
при пустом инстансе браузер показывает мастер (/api/v1/setup/): название
организации, имя, e-mail, пароль владельца и флаг «Установить демо-данные».
Запись идёт на соединении platform (роль app не создаёт организации) через
tenancy/routing.use_database; после первого владельца мастер закрыт (409).
bootstrap_owner и захардкоженные Edevs/Котова из установки удалены
(bootstrap_edevs_owner остаётся тестовым helper'ом).

Демо ставится в организацию установщика; реестр DemoRecord (post_save во
время сида) даёт точное удаление в обратном порядке с разрывом PROTECT-циклов
только между демо-объектами. Установка/удаление — outbox → worker; статус и
витринные учётки (админ + сотрудники разных групп, пароль Chatbolls-Demo-2026)
— карточка «Демо-данные» в «Настройках» (/company/demo/).

Сид переписан под дизайн-базлайн v2 («Ателье Норд»): 6 сотрудников (группы,
блокировка, TOTP, приглашения), 4 агента (активные/черновик/выключенный),
подключения TG/MAX/почта/два веб-виджета (анонимный и авторизованный) с
ошибкой у одного, знания с иерархией категорий и вложениями (md/txt/pdf),
30 дней истории LLM, 13 диалогов во всех состояниях с метками, приоритетами,
заметками, ответственными, историей контакта, спамом и архивом, веб-гость
через настоящую сессию виджета, шаблоны «/», портал поддержки со статьями
(опубликованные/черновик/архив, вторая ревизия, оценки), звонки с метриками и
приглашением, уведомления всех типов, привязки уведомителя. Аватары
контактов — портреты владельца, публичный /api/v1/demo-media/. Голосовые —
слоты под клипы владельца (media/voice/README.md).

Тест покрытия: каждая модель hub_platform получает демо-запись; удаление
возвращает счётчики к исходным. compose-профиль demo-seed и seed-demo.ps1
удалены; seed_demo --organization --apply/--remove для разработки; start.sh
для Linux/macOS; README — раздел «Быстрый старт».

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 16:55:31 +03:00
AndreyandClaude Fable 5 446a438776 🔥 feat(platform)!: контакт-центр Chatbolls — снос домена продаж и ребрендинг
Пивот по ADR-HUB-0041/0042: продукт становится open-source контакт-центром,
домен продаж удаляется целиком, прод-данные по решению владельца не сохраняются.

Backend:
- удалены приложения sales и orders (модели, API, ingest, команды, миграции);
- из products удалены Offer/Price/MarketplacePublication и ingest_token_hash
  (products/0011); таблицы, ingress-вьюхи и служебные записи Django снимаются
  миграцией tenancy/0017_drop_commerce;
- вычищены метрики выручки и заказов из conversations (stats/clients/command),
  типы уведомлений PAYMENT_* (notifications/0007), capabilities sales.*
  (identity/0019), коммерческие блокеры каналов, ingress-роуты продаж;
- демо-сид очищен от commerce; исторические миграции tenancy/0002-0004
  вычищены от таблиц orders_*/sales_* для чистой установки, tenancy/0003
  получила явные зависимости sessions/auth/contenttypes.

Frontend (internal-ui):
- удалены разделы «Продукты», «Обзор продаж», «Заказы», реестр и карточка
  продажи, вкладка «Продажи» и статус лид/клиент в контактах, коммерция
  в командном центре; роуты, навигация и доступы приведены в соответствие.

Ребрендинг CustoCRM → Chatbolls (видимый слой): UI, письма, TOTP issuer,
тексты ботов, заголовок API, README; виджет — window.ChatbollsChat
с legacy-алиасом CustoCRMChat. Технические идентификаторы (схема БД,
роли, cookie, CUS_-переменные, домены) — отдельным шагом (ADR-HUB-0042 §5).

Тесты: internal-ui 100/100, web-chat 5/5, backend RLS-сьют 10/10.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-03 21:36:22 +03:00
Andrey 4c622c7542 ✨ feat(seed): replace standalone importer with Django seed_demo command and Russian IIoT dataset
Старый сид dev/local-seed/ (standalone-скрипт под CUS_ENV) заменён полноценной
Django-командой seed_demo с редактируемыми JSON-манифестами в demo_seed/data/.
Полный выдуманный датасет организации «Северная Верфь» (IIoT, РФ-наполнение):
продукты Вектор/Репер, каналы, AI-агенты, база знаний со вложениями, диалоги,
заказы, продажи, поддержка, портал Help-центра, звонки, уведомления.
Идемпотентен (get_or_create/update_or_create), dry-run по умолчанию (--apply),
--force для облака. Удалён dev/local-seed/, скрипты seed-local/seed-support-portal,
compose-профиль переключен на demo-seed. Тесты: создание, идемпотентность, dry-run.
2026-07-30 19:59:46 +03:00
Andrey f8fb9162a6 ✨ feat(administration): add delivery-aware administration 2026-07-30 00:11:37 +03:00
Andrey 3f07d76173 ✨ feat(portals): complete custom domains and unified navigation 2026-07-29 16:49:50 +03:00
Andrey 7e9667ee88 ✨ feat(ui): align channel management with design baseline 2026-07-23 11:08:16 +03:00
Andrey dd849fa2d4 ✨ feat(platform): isolate runtime surfaces 2026-07-14 22:23:15 +03:00
AndreyandClaude Fable 5 192497ead0 💄 rebrand(ui): CustoCRM logo and product name instead of Edevs Hub
- New logo: favicon.svg and LogoIcon (replaces PulseIcon) in sidebar and auth screens
- Source logo asset saved to resources/icon.svg
- Replace "Edevs Hub" with "CustoCRM" across internal-ui, web-chat, backend strings (email sender/subject, TOTP issuer, notification bot texts, OpenAPI title), env examples and README
- Add .claude/launch.json for internal-ui dev server (bind 127.0.0.1)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 19:49:02 +03:00
AndreyandClaude Opus 4.8 f0111e40d5 fix(identity): disable TOTP for users; fix local credentials
Stop forcing OWNER totp_required on bootstrap (TOTP off for users), and set
the canonical local operator password. Document the fixed local OWNER/OPERATOR
credentials in the README. These local credentials are fixed and must not be
changed without explicit consent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 00:32:07 +03:00
AndreyandClaude Opus 4.8 e6d8923ae9 chore: remove Hub checkout app
Public checkout belongs to product backends, not Hub (ADR-HUB-0014,
ADR-HUB-0018). Delete apps/checkout and its wiring: compose service, nginx
pay.localhost route, npm workspace and dev script, playwright project,
HubApplication type, check.ps1 typecheck and README mentions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 15:10:36 +03:00
AndreyandClaude Opus 4.8 9679cb8cfc ✅ test(infra): make pytest discover the suite and unify the test command (#3)
- add apps/backend/pytest.ini (next to manage.py so it works inside the
  backend container); include tests.py in python_files so pytest collects the
  existing Django TestCase suites (was silently collecting nothing)
- drop the duplicate pytest config from the root pyproject.toml
- scripts/check.ps1 runs pytest + frontend unit tests + typechecks
- document the canonical test commands (backend/frontend/e2e) in README

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:02:51 +03:00
Andrey 7d4c4ba6ef Implement internal Hub baseline and auth flow 2026-06-17 20:47:58 +03:00
Andrey cce08385ff Expose platform foundation in Django admin 2026-06-17 18:33:06 +03:00
Andrey aacdd422b3 Initialize Edevs Hub repository 2026-06-17 18:14:16 +03:00