Локальная CRM-система для автосервиса. Хранит клиентов, авто, заказы, вложения.
Работает через HTML-интерфейс и Swagger API на localhost.
Поддерживает Alembic для миграций БД и легко переводится на PostgreSQL.
| Роль | Логин | Пароль | |
|---|---|---|---|
| superadmin | superadmin | admin | [email protected] |
| admin | maria.admin | admin123 | [email protected] |
| admin | sergio.admin | admin456 | [email protected] |
| master | lucas.master | lucaspass | [email protected] |
| master | natalia.master | natamaster | [email protected] |
| master | diego.master | diegomaster | [email protected] |
| master | marcela.master | marcela22 | [email protected] |
| Логин | Пароль | |
|---|---|---|
| juan.rivas | client1 | [email protected] |
| laura.mendez | client2 | [email protected] |
| pablo.flores | client3 | [email protected] |
| carla.sosa | client4 | [email protected] |
| matias.lopez | client5 | [email protected] |
| veronica.diaz | client6 | [email protected] |
| federico.gomez | client7 | [email protected] |
| sofia.rios | client8 | [email protected] |
| tomas.acosta | client9 | [email protected] |
| julieta.bianchi | client10 | [email protected] |
- Вход:
http://localhost:8000/admin/login - Клиенты:
http://localhost:8000/admin/clients - Добавить клиента:
http://localhost:8000/admin/clients/add - Добавить авто:
http://localhost:8000/admin/vehicles/add - Создать заказ:
http://localhost:8000/admin/orders/add - Выход:
http://localhost:8000/admin/logout
Для резервного копирования SQLite используется встроенный тул:
Создать бэкап (по умолчанию в backups/db/):
python -m tools.db_backupСоздать бэкап с тегом и архивом:
python -m tools.db_backup --compress --tag pre-seedХранить только 7 последних бэкапов:
python -m tools.db_backup --keep 7Каждый бэкап:
- Создаётся консистентно через
sqlite3.Connection.backup(); - Проверяется
PRAGMA integrity_check(результат пишется в.ok/.fail); - Хранится как
backups/db/auto_crm_YYYYMMDD-HHMMSS[_tag].sqlite(+.zipесли--compress).
.gitignore уже прописано исключение всех .sqlite, .zip, .ok, .fail внутри backups/.
- Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc
git clone https://github.com/your-user/auto-crm.git
cd auto-crmТеперь зависимости разделены на основные (requirements.txt) и dev (requirements-dev.txt)
В dev-версии закреплены стабильные версии ruff, djlint, eslint и prettier, чтобы авто-форматирование было воспроизводимым.
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
pip install -r requirements-dev.txt
pnpm installpython -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
pip install -r requirements-dev.txt
pnpm installСоздайте .env в корне:
PYTHONPATH=.
DATABASE_URL=sqlite:///./auto_crm.db
SECRET_KEY=your_super_secret_keyПримените миграции Alembic:
make migrateДобавьте демо-данные:
make initmake devДоступные варианты:
make run
make dev-css
make build-css| Команда | Описание |
|---|---|
make dev |
API + CSS-watcher параллельно |
make run |
Только API |
make dev-css |
Только CSS-watcher |
make build-css |
Сборка минифицированных стилей |
make lint |
Проверка кода |
make lint-fix |
Авто-фиксы |
make clean |
Очистка кешей |
make makemigration |
Генерация миграции |
make migrate |
Применение миграций |
make init |
Загрузка демо-данных |
make reset |
Полный сброс БД |
- ruff — Python
- eslint — JS
- prettier — JSON, CSS, Markdown
- djlint — Jinja
python -m app.scripts.audit_media_exists
python -m app.scripts.repair_missing_variants
python -m app.scripts.purge_broken_media --dry-runpython tools/i18n_scan.py scan --phrase app/templates/shared/ui/_confirm_modal.html
python tools/i18n_scan.py interactive app/templates/shared/ui/page_header.html
# 1) Генерация .pot:
make i18n-extract
# 2) Разовая инициализация языков:
make i18n-init
# 3) После добавления/обновления строк в шаблонах:
make i18n-all
# 4) Быстрый цикл после обёртки:
make i18n-extract # обновит messages.pot (если нужно)
make i18n-update # подтянет новые msgid в .po
make i18n-compile # соберёт .moВ проекте используется централизованная защита от CSRF (Cross-Site Request Forgery).
Файл: app/middleware/csrf.py
- На всех
GET/HEAD/OPTIONSзапросах:- гарантируем наличие CSRF-токена в cookie;
- кладём токен в
request.state.csrf_tokenдля шаблонов; - возвращаем тот же токен в cookie через
issue_csrf_with.
Файл: app/deps/csrf.py
require_csrfпроверяет:- скрытое поле формы
csrf_tokenИЛИ заголовокX-CSRF-Token; - сверяет его с токеном в cookie
csrf_token; - если несовпадение →
403 Forbidden.
- скрытое поле формы
Файл: app/templates/partials/form/_csrf.html
- Используется в формах для вставки:
<input type="hidden" name="csrf_token" value="{{ токен }}">
- Источники:
request.state.csrf_token→ cookie → контекстcsrf_token.
- FormKit-формы (clients, vehicles, electrical):
- GET:
ensure_csrfвыдаёт токен в hidden и cookie; - POST:
require_csrfпроверяет hidden+cookie.
- GET:
- Upload/replace/delete вложений (vehicles, electrical):
- POST: требуют
require_csrf, hidden-input добавлен вupload_panel.html.
- POST: требуют
- Confirm-модалка удаления: всегда передаёт
csrf_tokenв скрытом input.
- Никогда не использовать токен напрямую через JS (
meta[name=csrf-token]было удалено). - Всегда использовать
{% from "partials/form/_csrf.html" import csrf_input with context %}внутри форм. - Для API-запросов использовать заголовок
X-CSRF-Token.
- feat: добавление новой функциональности
- fix: исправление ошибки
- refactor: изменение логики без изменения функционала
- chore: служебные задачи (зависимости, настройки)
- style: стилистические изменения
- docs: изменение документации
- test: добавление/изменение тестов
Готово ✅ — открой http://localhost:8000/admin/login и начинай работать 🚗
{# Кнопки/формы/фильтры/сайдбар/страницы — всё домен ui, но контекст разный #}
<button class="btn">{{ pgettext('ui.buttons', 'Save') }}</button>
<label>{{ pgettext('ui.filters', 'Status') }}</label>
<nav>{{ pgettext('ui.sidebar', 'Vehicles') }}</nav>
<h1>{{ pgettext('ui.pages.reports', 'Print') }}</h1>
{# ENUM-лейбл (в БД хранится код): #}
<span class="chip">{{ pgettext('enums.report.status', report.status) }}</span>
{# Форматирование дат/валюты/чисел: #}
<time>{{ i18n.dt(report.created_at, 'medium') }}</time>
<div>{{ i18n.money(1234.5, 'USD') }}</div>
<div>{{ i18n.num(12345.678) }}</div>make i18n-scan — просканировать app/templates и вывести все куски «видимого» русского текста. Ничего не меняет.
make i18n-wrap [PATHS="..."] [I18N_CTX=ui.forms] [I18N_WORDS=1] [I18N_USE_KEYS=1] [I18N_NO_INFER=1] Обернуть найденные фразы в {{ pgettext('', "…") }} прямо в файлах, делая .bak-резервные копии.
PATHS — что оборачивать (по умолчанию app/templates).
I18N_CTX — жёсткий контекст (например, ui.forms).
I18N_WORDS=1 — оборачивать по словам (иначе фразами).
I18N_USE_KEYS=1 — вместо оригинальной фразы использовать ключ . как msgid.
I18N_NO_INFER=1 — отключить авто-инференс контекста по пути/атрибуту.
make i18n-interactive [PATHS="..."] — интерактивный режим: показ → выбор индексов → обёртка (всегда с .bak).
make i18n-extract — создать отдельный .pot по каждому домену в .i18n/pot/. Да, содержимое .pot совпадает — это нормально: разделение смысла у нас идёт через pgettext('ui.*', ...) и разные .po.
make i18n-init— разово создать .po для всех локалей и всех доменов (существующие не трогаем).
make i18n-update— обновить все .po из свежих .pot (нужно после изменений в коде/шаблонах).
make i18n-compile— собрать .mo из .po (нужно для рантайма).
make i18n-all— весь цикл (extract → update → compile).
make i18n-clean— удалить только .mo (удобно для чистой пересборки).
make i18n-status— сверка POT↔PO по (msgctxt,msgid). Возвращает exit 2, если что-то missing/obsolete.
Утилита для сканирования шаблонов на русский текст, автоматического перевода в английский, проверки ключей в app/locales/en/LC_MESSAGES/ui.po и оборачивания в pgettext с использованием существующих или новых msgid (EN). Основана на i18n_scan.py + i18n_autofill.py.
Процесс:
- Сканирует RU-текст в шаблонах.
- Переводит в EN (с кэшем RU → EN в JSON).
- Проверяет ключи по ctx/msgid в en/ui.po.
- Оборачивает в
pgettext('<ctx>', 'msgid'). - Опционально добавляет новые ключи в PO-файлы.
Зависимости: polib (установите: pip install polib). Python 3.9+.
Использование:
python tools/i18n_translate_scan.py [PATHS] [options]Основные опции (на основе логики скрипта):
--add-to-po: Добавляет новые ключи в соответствующие .po-файлы (en/ui.po и другие локали).--cache: Использует/обновляет JSON-кэш переводов RU → EN (по умолчанию в./.i18n_cache.json).--domain ui: Указывает домен (по умолчанию 'ui' для шаблонов).--paths "app/templates": Пути для сканирования (по умолчанию app/templates).--dry-run: Только сканирование и вывод, без изменений файлов.--verbose: Подробный вывод (логи переводов и найденных ключей).--help: Полный список опций.
Пример:
# Сканирование и оборачивание с переводом, добавлением в PO
python tools/i18n_translate_scan.py app/templates --add-to-po --verbose
# Только сканирование с кэшем
python tools/i18n_translate_scan.py --dry-run --cacheПосле запуска:
- Файлы шаблонов обновляются (.bak-копии создаются автоматически).
- Новые msgid добавляются в .po (если --add-to-po).
- Запустите
make i18n-compileдля сборки .mo.
make i18n-interactive.
Интеграция с Makefile: Добавьте make i18n-translate-scan для удобства (аналогично другим i18n-командам).