Commit Graph

5 Commits

Author SHA1 Message Date
Aleksei Pavlov 1ced06fa2d fix(autogen): pg_advisory_lock + catch-up + double-check after lock
Race condition (дубли статей) устранён окончательно:

1. pg_advisory_lock по ключу категории в начале runAutogenForCategory —
   если два процесса запускаются одновременно, второй ждёт первого.
   pg_advisory_unlock в finally — освобождается всегда, даже при ошибке.

2. После получения lock — повторная проверка 'уже генерировали сегодня'
   (SELECT articles WHERE category AND created_at >= CURRENT_DATE).
   Если да — skip без генерации. Это защита от случая когда первый процесс
   завершился пока второй ждал lock.

3. draftAutoApprove catch-up при старте: если engine стартовал после 07:00
   и есть непрогнанные вчерашние черновики — одобряет их сразу.
   Раньше deploy в 07:27 приводил к тому что черновики зависали навсегда.
2026-06-21 21:30:06 +03:00
Aleksei Pavlov 214bf307c7 fix(draftAutoApprove): строго вчерашние черновики, LIMIT 4, не больше слотов
Проблема: брали черновики за 36ч → при нескольких деплоях за день или при
накоплении старых черновиков draftAutoApprove раскладывал их все по слотам
подряд → очередь на несколько дней вместо 1.

Исправления:
  - WHERE created_at AT TIME ZONE 'Europe/Moscow' вчерашний день (00:00-23:59)
  - LIMIT 4 — не больше количества слотов в день
  - раскладываем только min(drafts, freeSlots) черновиков, лишние остаются draft
  - если черновиков > слотов — пишем в лог предупреждение
2026-06-21 16:42:37 +03:00
Aleksei Pavlov 799816f66a fix(drafts): авто-одобрение только вчерашних черновиков → слоты сегодняшнего дня
Было: runDraftAutoApprove брал ВСЕ черновики подряд и вызывал nextDripSlot()
для каждого — слоты набивались на несколько дней вперёд.

Теперь:
- берём только черновики созданные за последние 36 часов (вчерашние)
- слоты — только сегодняшний день (getTodaySlots из SITE_PUBLISH_SLOTS)
- занятые слоты пропускаем, не затираем уже запланированное
- если слотов меньше черновиков — ставим оставшихся в последний слот

Механика как у заметок Зеро:
  17:00 → генерация 4 черновиков (лежат в /admin/drafts, можно редактировать)
  07:00 след.дня → авто-одобрение, слоты сегодняшнего дня (08:11/12:11/16:11/20:11)
  Ручное одобрение → nextDripSlot (ближайший свободный)
2026-06-21 16:36:47 +03:00
Aleksei Pavlov bdff84e579 feat(articles): drip scheduling — distribute published_at across day slots
Зачем: автоген генерит несколько статей подряд (4 категории за 15 минут).
Раньше у всех published_at=NOW(), и на сайте они появлялись скопом, выглядя
как спам. Теперь распределяем по слотам сайта (по умолчанию 09/13/17/21 МСК).

Архитектура:
  src/services/dripScheduler.js — nextDripSlot() читает app_settings.SITE_PUBLISH_SLOTS
    (CSV 'HH:MM,HH:MM,...'), default '09:00,13:00,17:00,21:00'. Перебирает дни
    вперёд (14 дней горизонт), для каждого слота проверяет ±60 мин окно —
    если уже есть опубликованная статья, считает занятым. Первый свободный
    слот возвращается. Slots в MSK, конвертируются в UTC для сравнения с NOW().

При approve draft → published:
  routes/drafts.js — PATCH /:id/approve по умолчанию ставит slot, ?immediate=true
    публикует немедленно. GET /next-slot возвращает ближайший свободный для
    UI-предпросмотра.
  draftAutoApprove.js — авто-approve в 07:00 МСК тоже использует slot.

Публичные SQL дополнены 'AND published_at <= NOW()' чтобы будущие статьи
не светились на сайте раньше времени:
  - services/articles.js: 7 мест (list/get/count/hero/grid/popular/recent)
  - routes/categories.js: GET /:slug/articles
  - routes/scheduledPosts.js: TG-publish source query

Опционально: SITE_PUBLISH_SLOTS можно редактировать в /admin/settings.
2026-06-19 13:01:36 +03:00
Nik (Claude) 5852b9f439 feat: draft review flow — autogen→draft, auto-approve 07:00 MSK, /api/drafts routes 2026-06-16 09:17:10 +03:00