Публичная версия исходного кода для портфолио. В репозитории только исходники,
обезличенные примеры конфигурации и синтетические демо-данные. Рабочие базы,
ключи, токены, пользовательские данные и внутренние заметки сюда не входят.
Числовые идентификаторы в seed_demo_data.py и тестах являются вымышленными
фикстурами и не связаны с реальными аккаунтами Telegram.
Операционный кабинет для владельцев пунктов выдачи: точки, смены, управляющие, сотрудники и управленческий финансовый учёт.
- двуязычная публичная главная RU/EN с формой подключения;
- защищённый inbox заявок для администратора с этапами продаж;
- регистрация владельца только по одноразовому коду после ручного подтверждения оплаты;
- логины при регистрации ограничены 3–64 символами, пароль — 12–256 символами;
- отдельный owner workspace с организацией, подпиской и аудитом;
- добавление ПВЗ и привязка Telegram-чата;
- подключение из кабинета синхронизирует чат с переходной bot DB, чтобы команды смены сразу работали;
- часы работы точки задаются при подключении чата и синхронизируются с окном смены;
- одноразовые приглашения управляющих и сотрудников;
- переключатель регистрации сотрудников по приглашениям в кабинете владельца;
- ручной ввод доходов/расходов по точке;
- CSV-экспорт финансовых записей с учётом области доступа управляющего;
- сводка зарплаты текущего расчётного периода по подключённым чатам ПВЗ;
- выбор последних расчётных периодов и просмотр их зарплатной истории;
- CSV-экспорт выбранного payroll-периода для владельца и управляющего;
- детализация смен внутри выбранного периода с часами и начислением;
- read-only журнал последних действий владельца и управляющего;
- общие, персональные и скрытые ставки с проверкой видимости для участника;
- кастомные команды смены, интервалы тревог и готовые пресеты;
- настраиваемые дни и время зарплатных напоминаний;
- автоматический импорт начислений Ozon/WB не заявлен как готовая функция и остаётся отдельным будущим модулем;
- жизненный цикл подписки
active/grace/expiredи напоминания о продлении; - пересчёт периодной статистики по общей ставке чата в legacy-боте;
- проверки логики смен, RBAC и полный HTTP-сценарий активации кабинета.
Старый Flask/VPN-контур пока оставлен рядом для миграции и не считается production-кабинетом. VPN не входит в презентацию продукта владельцу.
cp .env.example .env
python3 -m venv .venv
.venv/bin/pip install -r requirements-dev.txt
.venv/bin/python -m pytest -q
.venv/bin/python web_app.py
# В отдельном процессе для production-уведомлений:
.venv/bin/python worker.py
# Локальная демо-сеть: владелец, управляющий и сотрудник с моковыми данными.
# Пароль не хранится в репозитории и задаётся только при создании данных.
.venv/bin/python seed_demo_data.py --password 'your-local-demo-password'
# Безопасный deploy-архив: .env, базы, логи и ключи в него не попадают.
./pack_for_deploy.sh /tmp/takt-deploy.zipВ production обязательно задать APP_ENV=production, FLASK_SECRET_KEY,
ADMIN_FLASK_SECRET_KEY и
новый BOT_TOKEN через secret store. .env, SQLite-базы, ключи и данные
находятся в .gitignore.
Операторская/админская поверхность (/admin*, /owner*, /backups*) — это
отдельное WSGI-приложение admin_app.py/admin_routes.py, не часть
web_app.py. Публичное приложение не импортирует эти модули и не
регистрирует эти маршруты вообще: запрос на /admin, /owner, /backups на
публичном хосте — обычный 404, неотличимый от любого другого несуществующего
пути, без редиректа на логин. Внутри admin-приложения дополнительно
работает ADMIN_HOST (пусто по умолчанию — локальная разработка и тесты
работают без host-изоляции). При заданном ADMIN_HOST запрос на любой
другой хост к admin-приложению не рендерит admin-HTML: HTTPS-запрос уходит
редиректом на https://ADMIN_HOST<путь>, запрос без HTTPS получает 404. Это
defense-in-depth поверх уже существующих ролей/CSRF и поверх самого
разделения процессов, а не замена реальной сетевой изоляции. Подробности,
модель угроз и текущая граница (общая БД/секрет между процессами) — в
docs/security-baseline.md.
# Локально, без Docker — два процесса на разных портах:
.venv/bin/python web_app.py # публичный сайт, WEB_PORT (по умолчанию 5000)
.venv/bin/python admin_app.py # админка, ADMIN_WEB_PORT (по умолчанию 5001)salary_bot.py (бот + вспомогательные потоки) запускает оба веб-процесса в
отдельных потоках сам — публичный без аргументов, админский с менеджером
бэкапов.
Для production — отдельные контейнеры: Dockerfile (публичный образ,
собирается из явного allow-list файлов и физически не содержит
admin_app.py/admin_routes.py/templates/admin/) и Dockerfile.admin
(админский образ). Пример топологии с reverse proxy, раздельными хостами и
общим (пока) volume для БД — docker-compose.example.yml и
deploy/nginx.example.conf; скопируйте и адаптируйте под свои домены/сертификаты,
это не готовый к продакшену конфиг as-is.
Cookie-сессии в legacy SQLite хранятся только как SHA-256 digest; при старте старые plaintext-записи автоматически нормализуются.
salary_bot.py отвечает за команды и смены, а worker.py — за зарплатные и
подписочные уведомления. Их можно перезапускать независимо; журнал идемпотентной
доставки не допускает дублей.
После ручной проверки оплаты администратор выпускает одноразовый код:
.venv/bin/python create_owner_code.py
# при необходимости изменить срок, в течение которого код можно активировать
.venv/bin/python create_owner_code.py --expires-days 60
# код для сети до 5 ПВЗ с отдельным тарифным идентификатором
.venv/bin/python create_owner_code.py --plan network --point-limit 5Код действует 30 дней, используется один раз и создаёт рабочее пространство владельца с 30-дневным периодом подписки. Онлайн-эквайринг и автоматическое продление будут добавлены после стабилизации доменной схемы.
.venv/bin/python -m pytest -q
.venv/bin/python -m compileall -q .
.venv/bin/ruff check platform_store.py create_owner_code.py config.py tests/ public_i18n.pyАрхитектурные ограничения и план миграции описаны в docs/architecture.md,
а security baseline — в docs/security-baseline.md.
Целевая PostgreSQL-схема находится в migrations/postgres/001_platform_schema.sql;
runtime переключается на неё через PLATFORM_DATABASE_URL. Если переменная
пуста, локально используется переходный SQLite-файл из PLATFORM_DATABASE_PATH.
Для переноса существующей копии сначала используйте безопасный dry-run:
python migrate_platform_sqlite_to_postgres.py --source platform.db --dry-run,
затем повторите команду с --target на пустой staging PostgreSQL.