Commit Graph
37 Commits
Author SHA1 Message Date
nunuchek 88b6e1395e refactor: simplify — мёртвые поля контента, общие хелперы api и тестов, липкая CTA без site в бандле
landing:
- site.ts: убраны неиспользуемые roast, brew, priceDisplay, hero.eyebrow, footer.phone (Footer берёт CONTACT_PHONE)
- form.ts: убраны FORM_TEXT.title/lead, CTAS, CtaPreset
- StickyCta получает тексты пропсами от layout: клиентский чанк больше не тащит все тексты страницы
- LeadForm/useLeadForm: без неиспользуемой подмены sendLead и className; leadPrefill без счётчика seq
- Stamp без мёртвого tone; убраны data-interest, data-hot, класс lead-form (ничем не читались)
- одно LEAD_FIELDS на sendLead и leadFormModel; регэксп typo вынесен из вызова; дубль фона body

api:
- changedFields и Version общие для leads и outbox; countLeads и leadStamps без копий SQL
- errorMessage в system.ts вместо четырёх копий; take() для ожидающих подтверждений
- backup: один Intl.DateTimeFormat на модуль, без невызываемых параметров timeZone/hour
- без мёртвых LOOPBACK '::ffff:127.0.0.1', client.call, Pick<BackupCommands> со всеми ключами
- testing.ts: validLead, addLead, count, leadIds вместо копий в тестах

deploy.sh: deployed_field и цикл по REPOS вместо повторов имён
2026-10-17 17:31:11 +03:00
nunuchek be8db997a5 fix(landing): impeccable audit — фокус на первое неверное поле, POST без JS, отступ под липкую CTA, 320 без скролла, AVIF и sizes
- форма: после «Отправить» с ошибками фокус уходит на первое поле с aria-invalid, скринридер читает подсказку; method=post — без JS имя и телефон не попадают в URL
- scroll-padding-bottom < 768: фокус и якоря не прячутся под липкой CTA (WCAG 2.4.11)
- штамп на этикетке сжимается на 320 вместо горизонтального скролла (WCAG 1.4.10); на 390 и шире без изменений
- цена читается «32 BYN», «250 г» (sr-only пробелы)
- sizes фото hero и «Кто обжаривает» по реальной ширине колонки на 1440; AVIF перед WebP
2026-10-17 17:21:12 +03:00
nunuchek b662e89058 fix(landing): sharp в образе через NODE_PATH; итерация критика 3 — переносы
Next 16 грузит sharp обычным require и NEXT_SHARP_PATH не читает: без sharp
/_next/image отдавал исходный JPEG. Критик 3 (9/10): точка-разделитель
нот и «·» в скобке недели не висят в начале/конце строки, имя и фамилия
латиницей не разрываются.
2026-10-17 17:13:36 +03:00
nunuchek 397cf62a37 content: подписи полей доставки без повтора «От 50 BYN» 2026-10-17 17:08:20 +03:00
nunuchek 834c4e53a5 design: итерация критика 2
Заголовки через typo(): «и» не висит. Подписка: у цены нет своей
линейки, цена и CTA по низу полей. Этикетки сортов делят строки
через subgrid, ступеньки под названием нет; значения полей с
интерлиньяжем 1.3, «•» держится за следующую ноту, вес «250 г» —
число и единица отдельными span. Под ассортиментом строка про
бесплатную доставку от 50 BYN. В тёмной секции строки доставки —
поля с подписями, акцент только на «бесплатно». В подвале слеш
не отрывается.
2026-10-17 17:07:56 +03:00
nunuchek 333870d287 docs: шаблоны ссылок для Директа и Instagram, инструкция по деплою 2026-10-17 17:02:53 +03:00
nunuchek 38356c2a7d docs: журнал ошибок ИИ — история коммитов, итерация критика 1 2026-10-17 17:02:24 +03:00
nunuchek e092ac47c5 design: итерация критика 1
Штамп первого экрана на непрозрачной подложке, цифры под мягким
фильтром, зазор кнопка→фото 40px. Ленты-наклейки сняты, у штампов
на этикетках разные повороты и сдвиги. Ряд цифр в «Кто обжаривает»
заменён одной tag-строкой. Дескрипторы сортов — поля этикетки,
«в месяц» после цены набрано Golos. Дрип-пакеты на десктопе: три зоны
по центру с вертикальными линейками. Доставка в колонках 7–12, на
мобиле без фото мешка. Интерес в форме — радиокнопки. Хелпер typo:
неразрывные пробелы и дефисы. Скрипт скриншотов: кадр 390 со скроллом.
2026-10-17 17:02:02 +03:00
nunuchek a5c9dd1ee1 chore: скрипт скриншотов 390/1440 для критика (Playwright), журнал ошибок ИИ 2026-10-17 16:56:52 +03:00
nunuchek 7f4f4a199e feat(landing): OG-картинка и метаданные
opengraph-image и twitter-image 1200×630 через next/og: знак, заголовок,
штамп «ЧТ · обжарка», лента недели с ЧТ/ПТ красным, без фото и дат.
Статичные TTF Golos Text и Martian Mono для Satori. Метаданные из
site.meta: title, description, openGraph, twitter, metadataBase
kvarta.unuchak.com, canonical. Иконка — SVG-знак из семи штрихов.
2026-10-17 16:56:52 +03:00
nunuchek d8d67c036f feat(landing): штамп, лента недели, состояния кнопки, липкая CTA
Оттиск штампа при загрузке и при успехе формы, заполнение ленты
недели по IntersectionObserver, мигающие точки при отправке.
Только CSS и transform/opacity; анимации включает скрипт в head,
поэтому без JS и при prefers-reduced-motion всё сразу в финале.
Липкая CTA на мобиле: «Заказать кофе», прячется у формы, при фокусе
в ней и при открытой клавиатуре. Ритм секций плотнее.
2026-10-17 16:56:52 +03:00
nunuchek dc70ebccd8 feat(landing): секции по DESIGN.md
Шрифты Golos Text и Martian Mono через next/font, токены @theme,
все блоки по плану: первый экран со штампом даты ближайшей обжарки,
лента недели, этикетки сортов, подписка, кто мы, доставка, форма,
подвал. Три фото Unsplash локально, priority только у первого экрана.
LeadForm перекрашена под токены, логика и data-атрибуты те же.
В site.ts и form.ts добавлены служебные подписи для вёрстки.
2026-10-17 16:56:52 +03:00
nunuchek 66d69c1245 feat: ретранслятор Bot API на домашнем сервере и TELEGRAM_API_BASE
С VPS api.telegram.org недоступен. deploy/tg-relay: nginx на адресе
Tailscale домашнего сервера, пропускает только запросы нашего бота из
tailnet, без логов доступа (токен в URL). api: базовый URL Bot API из
TELEGRAM_API_BASE; сетевые сбои polling пишутся в лог раз в 10 минут.
2026-10-17 16:56:52 +03:00
nunuchek 55ecf7c9d8 docs: DESIGN.md и PRODUCT.md — недельный календарь и штамп даты
Референсы (календарь Rationale, штамп FreshColor, этикетки Altura)
утверждены автором. Токены с контрастом WCAG, посекционные раскладки
390/1440, компоненты, моушн с reduced-motion, OG.
2026-10-17 16:33:05 +03:00
nunuchek 15a9333e6a content: тексты лендинга — черновик ИИ, вычитка автора, slopotron
site.ts: все блоки страницы только из фактов ТЗ. form.ts: правки,
принятые автором (без «помола», понятнее успех, уточнение и 429).
slopotron: убраны повторы «обжариваем по четвергам» и «пока обжарка
свежая» между первым экраном и блоком свежести.
2026-10-17 16:30:48 +03:00
nunuchek 8c27a5723d chore: инструменты дизайна и текстов — impeccable 4.6.0 и slopotron
impeccable ставится npx impeccable install --providers=claude --scope=project
(бинарник детектора не коммитим), slopotron — копия скила из
github.com/beaverbeard/slopotron@122d385.
2026-10-17 16:25:19 +03:00
nunuchek 25246ad0c4 fix(api): доверять X-Real-IP со шлюза docker-сети в IPv4-mapped виде
Node отдаёт адрес сокета как ::ffff:172.31.120.1, а TRUSTED_PROXIES
записан без префикса — все посетители попадали в один rate-limit.
2026-10-17 16:17:24 +03:00
nunuchek 5f31b03203 fix(deploy): кросс-сборка — JS на машине сборки, нативные модули под целевую платформу
Под QEMU next build падает с segfault (SWC). Сборка идёт на $BUILDPLATFORM,
sharp ставится отдельной стадией под целевую платформу (NEXT_SHARP_PATH),
у better-sqlite3 остаётся prebuilt целевой архитектуры.
2026-10-17 16:14:43 +03:00
nunuchek 45481d2105 docs: журнал ошибок ИИ — Telegram и бэкапы 2026-10-17 16:09:39 +03:00
nunuchek 249b4bb627 feat(api): ежедневный бэкап, копия в личку, восстановление через бота
backup.ts: db.backup() → gzip в backups/ рядом с базой, имя по времени
Минска (Intl), 14 ежедневных и pre-restore на 30 дней; планировщик
в 03:00 по Минску и при старте, если сегодня копии нет; файл админу,
итог в «Систему». Восстановление: gunzip → integrity_check → версия
схемы → пауза приёма (503) → pre-restore → атомарная подмена файла →
миграции; при сбое возвращаем прежнюю базу. createApp и очередь берут
базу через геттер, чтобы увидеть подменённую.
Бот в личке: /backups с кнопками, /backup, присланный .sqlite.gz,
подтверждение со списком пропадающих заявок, живёт 10 минут.
2026-10-17 16:07:26 +03:00
nunuchek 667c6a6e6d feat(api): Telegram — темы, очередь повторов, «Система», /setup
telegram/client: Bot API на fetch с таймаутом 5 с; ошибки делятся на
повторяемые (сеть, таймаут, 429, 5xx) и конфигурационные (400/403…),
токен не попадает ни в тексты ошибок, ни в tg_last_error.
format: заявка и уточнение по шаблону плана, HTML с экранированием,
источник из utm / referrer, первый визит.
outbox: Notifier с отправкой после ответа и тиком раз в минуту, пауза
1, 2, 4… мин, уточнение только после своей заявки, после 10 попыток —
в «Систему». system: тема «Система» с троттлингом ×N за период.
bot: long polling, только TELEGRAM_ADMIN_ID, /setup создаёт темы и
присылает id в личку. config: всё окружение в одном месте.
2026-10-17 16:07:26 +03:00
nunuchek 5d970be228 docs: журнал ошибок ИИ — deploy 2026-10-17 15:48:52 +03:00
nunuchek 1accf9341b chore(deploy): фиксированная подсеть и TRUSTED_PROXIES для X-Real-IP 2026-10-17 15:48:45 +03:00
nunuchek 79ab146b1d chore(deploy): Docker-образы, compose, deploy.sh, setup-nginx.sh 2026-10-17 15:48:19 +03:00
nunuchek e2d85a28ef chore: type module в корне, журнал ошибок ИИ — landing 2026-10-17 15:47:36 +03:00
nunuchek e6610f0408 feat(landing): состояния формы и ошибки отправки 2026-10-17 15:47:24 +03:00
nunuchek a5ee348df0 feat(landing): первое и последнее касание 2026-10-17 15:47:24 +03:00
nunuchek 02da483747 feat(landing): заглушка страницы с рабочей формой 2026-10-17 15:47:24 +03:00
nunuchek c2786f5894 docs: журнал ошибок ИИ — каркас, телефон, api 2026-10-17 15:42:32 +03:00
nunuchek 84463da3eb chore: игнорировать worktree'ы агентов в .claude/worktrees 2026-10-17 15:41:45 +03:00
nunuchek ea0535001c feat(api): rate-limit, honeypot, коды ошибок
rateLimit.ts: 5 запросов / 10 минут на IP в памяти, фиксированное окно,
чистка устаревших записей по ходу запросов, 429 с Retry-After. IP из
X-Real-IP только от loopback-сокета (плюс TRUSTED_PROXIES для шлюза
docker), иначе адрес сокета; в базу IP не пишем. Honeypot → 201 без
записи и уведомления. isPaused() → 503 на время восстановления базы.
Тесты на каждый код ответа из таблицы плана.
2026-10-17 15:41:04 +03:00
nunuchek c20e40c333 feat(api): уточнения в окне 10 минут
saveLead в одной IMMEDIATE-транзакции: заявка с тем же телефоном
моложе 10 минут → уточнение, если имя, интерес или комментарий
отличаются от последней версии, иначе повтор без записи. Окно от
created_at заявки, уточнения его не продлевают. Ответ 200 duplicate,
notifier получает id уточнения и список изменившихся полей.
2026-10-17 15:39:31 +03:00
nunuchek fa0964b24d feat(api): /api/lead с SQLite и миграциями
db/: better-sqlite3 по DB_PATH (WAL, foreign_keys), миграции через
PRAGMA user_version, схема leads и lead_clarifications по плану.
createApp(deps) с инъекцией базы, часов и notifier (пока no-op, зовётся
после ответа). POST /api/lead: только JSON (415), тело до 16 КБ (413),
validateLead → 400, запись → 201. index.ts: env и graceful shutdown.
2026-10-17 15:38:42 +03:00
nunuchek a2885d180c feat(shared): нормализация белорусского телефона и контракт заявки
normalizeBelarusPhone/phoneError/formatBelarusPhone: любые форматы ввода
→ +375XXXXXXXXX, коды 25/29/33/44/17, подсказки по причине ошибки.
validateLead: типы POST /api/lead, ошибки по полям, honeypot, очистка
касаний. Тесты Vitest.
2026-10-17 15:34:23 +03:00
nunuchek 04b150f3c3 chore: pnpm workspace — landing, api, shared
apps/landing: Next.js 16 (App Router, standalone), Tailwind 4, ESLint
с запретом импорта из api. apps/api: Hono + better-sqlite3, сборка tsup,
/api/health. packages/shared: исходники TS без сборки. Vitest projects.
2026-10-17 15:33:07 +03:00
nunuchek 4b880e7d9a docs: план реализации, глоссарий, ADR, журнал ошибок ИИ
План по итогам интервью (docs/plan.md), GLOSSARY.md, ADR о двух
сервисах и о боте как канале администрирования, docs/ai-log.md,
.gitignore для секретов, базы и артефактов.
2026-10-17 15:30:30 +03:00
nunuchek cc7feb97a5 Initial commit: agent skills setup
Add project skills, CLAUDE.md with Agent skills section, and
docs/agents config (local markdown issue tracker, single-context domain docs).
2026-10-17 15:29:08 +03:00