Документация
STATE.md — статус проекта
Обновляется после каждой рабочей сессии. ТЗ —
ARCHITECTURE.md. История —CHANGES.md. Чек-лист прода —PRODUCTION.md. Название проекта — Znymkari (Студія / Медиа группа — на логотипе).
Формат сессий: только даты в CHANGES.md. В остальных докax — без номеров.
Если в один день несколько сессий — 2026-10-14, 2026-10-14-б, 2026-10-14-в.
Что делаем сейчас
Проект znymkari-data — цех импорта на ноуте Олега.
2026-10-15-б — реструктуризация завершена.
D:\content_shop\web\стал отдельным git-репо (43 коммита, история черезgit-filter-repo). КореньD:\content_shop\— без git, как вARCHITECTURE.md §7.1. Бэкап:G:\My Drive\backups\content_shop_2026-10-15\.
Два независимых проекта:
| Проект | Где живёт | Роль |
|---|---|---|
web/ |
VPS (znymkari.com) |
Сайт-витрина: клиент, корзина, оплата, выдача |
znymkari-data/ |
Ноут Олега (oleg-home) |
Цех: импорт, sanitize, превью, push |
Связь между ними: management-команды pack_event → push_event (ноут) → receive_event (VPS). Транспорт — JSON (django.core.serializers) + ORM-merge. Единая схема БД на обоих.
Текущий план работ:
- ✅ Создать каркас
znymkari-data— Django-проект, отдельный репо. - ✅
import_service.py,media_metadata.py,generate_video_posters.py,management/commands/— вznymkari-data. - ✅
sanitize.py— транслит и чистка имён. - ✅ Копирование оригинала из
zyn-festivals/<название>/вSTORAGEс sanitize. - ✅ Новые поля моделей:
Event.slug,Event.external_id. (storage_rootудалён 2026-10-16.) - ✅ Мини-админка для Олега:
/,/import/,/import/<id>/,/storage/,/settings/. - ✅ Транспорт:
pack_event/push_event(ноут) +receive_event(VPS). Проверено на живом цикле. - ✅ Первый реальный импорт фестиваля (Dance Vocal Class).
- ✅
gunicorn-oleg.service— Django на0.0.0.0:8000(развёрнут). - ✅ Интерфейс отправки (
pack_event/push_event) с логами и прогрессом. - ✅ Страница «Чистка» (удаление мусора и хвостов).
- ⏳ Обратный канал VPS → ноут:
pull_protected+ файл чистки. - ⏳ Кнопка «Удалить из storage» на
/storage/— послеpull_protected.
Детали — в ARCHITECTURE.md §5.1 (обновляется вместе с кодом).
Что в очереди
Ближайшее (znymkari-data)
- ✅ Каркас проекта + git init.
- ✅ Модели каталога — перенос из
web/. - ✅
sanitize.py— транслит + правила. - ✅
import_service.py— переписать безservices.media_metadata, добавить копирование. - ✅ Новые поля моделей + миграции.
- ✅ Мини-админка для Олега.
- ✅
pack_event/push_event/receive_event— проверено на живом цикле. - ⏳ systemd-юнит
gunicorn-oleg.service+ деплойznymkari-dataна ноут Олега. - ⏳ Первый реальный импорт.
- ⏳ Обратный канал VPS → ноут:
pull_protected+ файл чистки. - ⏳ Кнопка «Удалить из storage» на
/storage/— послеpull_protected.
Дальше (после стабильного импорта)
- WireGuard VPS ↔ ноут.
- nginx на ноуте +
auth_request. files.znymkari.com+ Let's Encrypt.- Модели
DownloadToken,Order,OrderItem— Этап 3. - Платёжный провайдер (LiqPay / Fondy / Monobank).
- Internal API
/internal/large-preview/— 60% оригинала с QR. - Кнопка «Проверить качество» в лайтбоксе.
home.html→ канонический app-shell.event.html→ общийlightbox.js.- Восстановление пароля через email.
- Реальный SMTP.
- SiteSettings — контакты, соцсети, лого.
- Мобильная версия.
Что не начато совсем
| Компонент | Статус |
|---|---|
znymkari-data — каркас |
✅ |
sanitize.py |
✅ |
Копирование оригинала из zyn-festivals в STORAGE |
✅ |
Event.slug, Event.external_id |
✅ |
Event.storage_root (удалено 2026-10-16) |
✅ |
Media.width/height |
❌ |
Мини-админка Олега (/, /import/, /storage/, /settings/) |
✅ |
pack_event / push_event / receive_event |
✅ |
gunicorn-oleg.service |
✅ |
Модель ImportJob |
✅ |
Кнопка «Проверить связь с VPS» в /settings/ |
✅ |
Команда assign_media_authors на VPS |
✅ |
Обратный канал VPS → ноут (pull_protected) |
❌ |
DownloadToken + auth_request |
❌ |
/internal/large-preview/ |
❌ |
| Заказы, оплата, выдача | ❌ |
| Восстановление пароля | ❌ |
| Реальный SMTP | ❌ |
| Мобильная версия | ❌ |
| SiteSettings — контакты, соцсети, лого | ❌ |
| Страница «Услуги» | ❌ |
| Кнопка чистки «Удалить без лайков и покупок» | ❌ |
| Публичная форма замечаний | ❌ |
| Вложения к Issue | ❌ |
| Экспорт ивентов (JSON / PDF / Excel) | 🚧 |
| Кэшбек, рефералка | ❌ |
| Удаление старого мира | ✅ (2026-10-16) |
| Grid-view для разметки | ⏳ |
.timeline-* → .markup-* |
⏳ |
Технический долг
Сайт (web/)
event.html— свой лайтбокс. Дублирует общий модуль.home.html— футер черезposition: fixed. Канон — app-shell.- Тесты не покрывают:
lightbox.js, favorites, cart. discount_cash_percent— есть в моделиEvent, вcart/smart/не применяется.favicon.ico— есть вstatic/, но на VPS не отдаётся.- Телефон и соцсети — заглушки → SiteSettings.
- Награждение — по названию выступления, эвристика.
Media.storage_path— относительный отSTORAGE_ROOT. Единый корень на HDD; HOT/COLD отменены 2026-10-16.media_metadata.pyперенесён вweb/apps/catalog/services/media_metadata.py(2026-10-16). Старый мир удалён.import_serviceне копирует оригиналы — только генерит превью. Копирование будет вznymkari-data.CategoryFK наBlock— вARCHITECTURE.md §3.4было написано наоборот. Исправить при следующей правке доки.- File picker для
source_folder/scan_path. block=nullв API = «без фильтра».- Удалённые аккаунты — повторная регистрация на тот же email не сработает.
- Ховер × на тач — превью нельзя удалить на телефоне.
.timeline-*иPAGE_TIMELINE="timeline"— оставлены после переименования вmarkup.- Пароль Postgres засветился на скрине → сменить перед публичным запуском.
- Токен бота засветился на скрине → сменить через
@BotFather /revoke. LOGGING— уже сRotatingFileHandler(10 МБ × 5), файл/var/log/znymkari/django.log. Техдолг закрыт./media/znymkari_logo.jpg— 404. nginx/media/на VPS не отдаёт.- Soft-delete email-аккаунтов — реактивация не реализована, делать вместе с SMTP.
MAILERS— Django 6.1, новая настройка вместоEMAIL_BACKEND. Пока console.db.sqlite3— используется только для dev, на VPS — Postgres.content_shop.db,docs_upload.tar.gz,web_upload.tar.gz,fix_docs.py,find_cyr.py— мусор, кандидаты на удаление.start_all.bat,start_shell.bat,test.bat— локальный запуск с ботом + туннель, shell, тесты. Оставить.- Баг
${html(pickhref)}— JS template literal попал вhref=в шаблоне. Искатьfindstr /s "html(pickhref)". - Remote (GitHub) — не настроен. Страховка — zip на
G:\My Drive\backups\. Делать, когдаweb/стабилизируется. D:\content_shop\.gitignore— теперь ни к какому репо не привязан. Кандидат на удаление или перенос вznymkari-data.D:\content_shop\desktop.ini— мусор.
Ноут Олега
ufwне включён. Правило для 445 добавлено, но firewall неактивен. Включить с проверкой SSH.- Резервирование Wi-Fi в роутере Xiaomi — сделано. IP
192.168.31.87. - NTFS-3G → NTFS3 — отложено до покупки новых винчестеров (Олег планирует).
- Broadcom DKMS — проверять пересборку
wlпосле обновления ядра. - Пароль
olegarh— Олег должен хранить в надёжном месте. - Симлинки в
/home/olegarh/—deployих не видит без sudo. Норма. /mnt/hdd/znymkari-storage— рабочая копия, NTFS/FUSE./mnt/hdd/zny-festivals— 10 фестивалей. Источник импорта./mnt/hdd/zny-heap— свалка, вне импорта.- SSD — ОС + PostgreSQL + venv + превью.
znymkari-data— готов целиком (import_service.py,media_metadata.py,sanitize.py,management/commands/, мини-админка, транспорт). Осталось: деплой на ноут +gunicorn-oleg.service.media/znymkari_logo.jpg— надо скопировать вznymkari-data/media/для_apply_qr_watermark.- Samba — юзер
olegarh, порт 445, LAN192.168.31.0/24. Windows-шары:Z:→zyn-festivals,Y:→zyn-heap. - NTFS «грязный» — Windows Fast Startup и «Безопасное извлечение». Выключить Fast Startup.
ufwна ноуте не включен — правило для 445 добавлено, но firewall неактивен.ThreadPoolExecutorна 3 потока — жестко зашито вimport_service.py(для USB HDD больше = хуже).
VPS
- Миграция
0012_drop_event_storage_rootприменена 2026-10-17. ALLOWED_HOSTS—znymkari.com,www.znymkari.com,128.140.81.213.SECURE_HSTS_PRELOAD— пока не включаем (необратимо).SECRET_KEY— бэкап вне сервера не сделан.settings_local.py— бэкап локально + облако не сделан.- Ротация пароля Postgres — не сделана.
- Ротация токена бота — не сделана.
- Бэкапы БД — не автоматизированы.
- Мониторинг — не настроен.
- Sentry — не подключён.
Открытые вопросы
- Оригиналы — храним вечно? Или удаляем через N лет?
- Срок жизни ссылки на оригинал — 72 часа? Или другое?
- Лимит скачиваний — 1 раз на файл, или больше?
- Что делать, если клиент потерял ссылку? Кнопка «Скачать снова»?
- Что делать, если дома упал свет во время скачивания?
- Кэшбек — нужен? Схема с
referrer_user. - Провайдер оплаты? LiqPay / Fondy / Monobank / WayForPay.
- Кто регистрирует ФОП/ТОВ?
- SMS — нужен? Telegram верифицирует телефон.
- Мобильная версия — насколько глубоко?
- Формат манифеста переноса HOT → COLD — что включить кроме
event_id,file_count,checksum? STORAGE_ROOTна VPS — не нужен, VPS без оригиналов. Подтвердить.import_service— оставляем вweb/или выносим в общий пакет? Сейчас он тянетservices.media_metadataиз старого мира.
VPS — параметры (для новой сессии)
| Параметр | Значение |
|---|---|
| IP | 128.140.81.213 |
| IPv6 | 2a01:4f8:c010:9558::/64 |
| Домен | znymkari.com (Hetzner, аккаунт Олега konsolch) |
| Имя сервера | znymkari |
| Провайдер | Hetzner |
| Location | Falkenstein (fsn1) |
| Type | CPX22 (2 vCPU / 4 ГБ / 80 ГБ SSD) |
| OS | Ubuntu 24.04.5 LTS |
| Kernel | 6.8.0-146-generic |
| Swap | 2 ГБ (/swapfile) |
| SSH root | root@128.140.81.213, ключ znymkari-my |
| SSH deploy | deploy@128.140.81.213, ключ znymkari-my, sudo NOPASSWD |
| Backups | включены |
| Цена | €23.89/мес |
| Owner | Олег (zhurbenko1985@gmail.com) |
| Команда | Олег — Owner, я — Member |
| WorkingDirectory | /home/deploy/content_shop/web |
| Структура | content_shop/ содержит web/ + venv/ + gunicorn.sock |
| settings_local | /home/deploy/content_shop/web/config/settings_local.py |
| settings_local_prod | /home/deploy/content_shop/web/config/settings_local_prod.py (шаблон) |
| IPv6 фикс | /etc/gai.conf → precedence ::ffff:0:0/96 100 |
| Cockpit | https://128.140.81.213:9090, юзер cockpit_admin |
| Telegram | Webhook (/telegram/webhook/<secret>/). Long polling отключён. |
| Бот | @Znymkari_dev_bot (id 8807489402). Токен и TELEGRAM_WEBHOOK_SECRET — в settings_local.py. |
| Логи | journalctl -u gunicorn-znymkari -f. Файл /var/log/znymkari/django.log. |
Стек:
- Пользователь deploy (uid 1000).
- Директория проекта: /home/deploy/content_shop.
- venv: /home/deploy/content_shop/venv.
- gunicorn: /etc/systemd/system/gunicorn-znymkari.service.
- nginx: /etc/nginx/sites-available/znymkari.
- Сокет: /home/deploy/content_shop/gunicorn.sock.
- www-data в группе deploy.
- Postgres: база znymkari_db, юзер znymkari_user, схема public (владелец юзер).
- settings_local.py — прод (НЕ в git).
- Static: /home/deploy/content_shop/staticfiles.
Ноут Олега — параметры
| Параметр | Значение |
|---|---|
| Пользователь | olegarh (uid 1000), deploy (uid 1001, sudo NOPASSWD) |
| Имя хоста | oleg-home |
| ОС | Ubuntu Server 24.04 LTS, без GUI, ядро 7.0.0-38 |
| Сеть | Wi-Fi wlp8s0, LAN 192.168.31.87 (резерв в роутере). Ethernet сломан. |
| Внешний SSH | 193.176.249.24:22022 → 192.168.31.87:22 |
| SSH-ключ | id_ed25519 (разработчик), в authorized_keys для olegarh и deploy |
| Крышка | HandleLidSwitch=ignore. Sleep/suspend/hibernate замаскированы. |
| Роль | Мини-UPS (2–4 ч), склад оригиналов, цех импорта |
| Python | 3.14 |
| PostgreSQL | 18, кластер 18/main, локаль uk_UA.UTF-8 |
| nginx | 1.28 |
| ffmpeg | 8.0.1 |
| HDD | WD My Passport 2 ТБ, NTFS, /mnt/hdd, SMART здоровый |
| STORAGE | /mnt/hdd/znymkari-storage/ — рабочая копия |
| FESTIVALS | /mnt/hdd/zny-festivals/ — оригиналы, источник импорта |
| HEAP | /mnt/hdd/zny-heap/ — свалка |
| SSD | ОС + PostgreSQL + venv + превью |
| Симлинки | /home/olegarh/HDD → /mnt/hdd, /home/olegarh/STORE → /mnt/hdd/znymkari-storage |
| Samba | Юзер olegarh, порт 445, LAN 192.168.31.0/24. Шары zny-festivals, zny-heap. |
| БД | znymkari_local, юзер znymkari_local_user, порт 5432 |
| Django | /home/deploy/znymkari-data/ (планируется), venv рядом |
| systemd | gunicorn-oleg.service, 0.0.0.0:8000 |
| Доступ к сайту | Браузер по LAN: http://192.168.31.87:8000/ |
| WinSCP | Локально 192.168.31.87:22 (не 22022, NAT loopback не работает) |
deploy не видит |
/home/olegarh/ — норма |
ufw |
Не включён. Правило для 445 добавлено. |
Что работает на сегодня
Сайт (web/)
| Компонент | Статус |
|---|---|
Django 6.1.1, Europe/Kyiv, USE_TZ |
✅ |
| Все модели каталога + прайс | ✅ |
| Админка Django, разметка, камеры | ✅ |
| Просмотр превью, генерация — на ноуте | ✅ |
Импорт медиа — только в znymkari-data (web-копия удалена) |
✅ |
| Постеры видео + 10-сек превью со звуком | ✅ |
| Главная, список ивентов, прайс | ✅ |
| Клиентская страница ивента — галерея, лайтбокс | ✅ |
| Избранное (LS + БД) + серверная корзина + merge-prompt | ✅ |
Общий lightbox.js / lightbox.css |
✅ |
header_actions.html, ?auth=, BuyModal |
✅ |
UserEventDiscount + cart/smart/ (правила 1–6) |
✅ |
Три типа офферов с applied |
✅ |
| Привязка награды к выступлению | ✅ |
| App-shell на 7 шаблонах | ✅ |
| Регистрация / логин / активация / профиль | ✅ |
| Telegram-флоу (вход, привязка, бот, 5 языков) | ✅ |
apps.issues — трекер + админка |
✅ |
| Читалка доков — 8 табов | ✅ |
| Django-тесты (77) | ✅ |
deploy.bat + стоп-краны |
✅ |
| VPS Hetzner — развёрнут, HTTPS работает | ✅ |
Telegram webhook + set_telegram_webhook |
✅ |
| Security hardening (HSTS, secure cookies) | ✅ |
LOGGING с RotatingFileHandler |
✅ |
Отдельный git-репо (web/.git, 43 коммита) |
✅ |
Три поля Event (external_id, storage_root, slug) |
✅ |
manage.py migrate в deploy.bat (шаг 5) |
✅ |
Ноут Олега
| Компонент | Статус |
|---|---|
| Ubuntu Server 24.04 | ✅ |
Пользователи olegarh / deploy |
✅ |
Внешний SSH 193.176.249.24:22022 |
✅ |
| Wi-Fi Broadcom DKMS | ✅ |
| Сон отключён | ✅ |
HDD NTFS /mnt/hdd |
✅ |
| Папки STORAGE / FESTIVALS / HEAP | ✅ |
Симлинки в /home/olegarh |
✅ |
| Стек Python 3.14 / PG 18 / nginx 1.28 / ffmpeg 8.0.1 | ✅ |
Samba с шарами zyn-festivals, zyn-heap |
✅ |
10 фестивалей в zyn-festivals |
✅ |
Свалка zyn-heap заполнена |
✅ |
Django + settings_local.py в znymkari-data |
✅ |
Модели каталога в znymkari-data |
✅ (10 моделей, миграции 0001–0010) |
sanitize.py в znymkari-data |
✅ |
import_service.py в znymkari-data |
✅ |
Мини-админка для Олега (/, /import/, /import/<id>/, /storage/, /settings/) |
✅ |
Модель ImportJob |
✅ |
Транспорт pack_event / push_event |
✅ |
| Кнопка «Проверить связь с VPS» | ✅ |
systemd gunicorn-oleg.service |
✅ |
Деплой znymkari-data на ноут |
✅ |
| Первый реальный импорт фестиваля | ✅ |
Транспорт pack_event / push_event в UI |
✅ |
| Страница «Чистка» | ✅ |
Обратный канал pull_protected |
🔜 |
| Кнопка «Удалить из storage» | 🔜 |
| WireGuard VPS ↔ ноут | 🔜 |
nginx на ноуте + auth_request |
🔜 |