Files
markclean-doc/changelog.md
T
2026-10-17 20:11:13 +03:00

47 KiB
Raw Blame History

Changelog


[0.15.1-rc] - 2026-10-17

Добавлено

История платежей (Backend) GET /api/billing/payments — новый эндпоинт для получения списка заказов пользователя с пагинацией (limit, offset). Возвращает метаданные: total, has_more. Реализовано через PaymentService.ListOrders и postgresPaymentRepo.ListByUserID.

Интерфейс оплаты (Frontend) Страница /billing/history — визуализация истории покупок. Карточки заказов отображают статус, продукт, сумму и дату. Клик по карточке ведет на детальную страницу заказа. Функция listPayments() в lib/billing.ts для работы с новым API.

Детальная страница заказа (Frontend) Маршрут /billing/payments/:id (или модальное окно/страница статуса) позволяет пользователю проверить актуальный статус конкретного платежа после возвращения с ЮKassa или при повторном посещении истории.

Изменено

UX процесса покупки Кнопка «Купить» теперь инициирует создание платежа через бэкенд и открывает confirmation_url в новой вкладке, вместо прямой выдачи кредитов (которая была доступна только в dev-mode).


[0.14.5-beta] - 2026-10-16

Добавлено

Интеграция с YooKassa (СБП/Карта) Новый модуль internal/billing/payment:

  • YooKassaGateway: реализация интерфейса шлюза на чистом net/http. Поддержка создания платежей СБП (payment_method_data.type=sbp) и банковских карт (bank_card) через конфигурацию.
  • Автоматическая генерация Idempotence-Key на бэкенде (UUID) для защиты от дублирования заказов при повторных запросах клиента.
  • Проверка подлинности вебхуков: обязательный запрос состояния платежа через API ЮKassa перед начислением товаров (защита от фейковых уведомлений).

Миграция базы данных Таблица payment_orders: хранение локальных заказов, сумм, статусов (pending, paid, canceled, failed), связей с провайдером (provider_payment_id) и ключей идемпотентности. Индексы для быстрого поиска по пользователю и ID платежа.

Безопасность биллинга

  • Отключение бесплатного эндпоинта POST /api/billing/purchase в режиме release. Теперь кредиты выдаются только после подтверждения оплаты через ЮKassa.
  • IP-фильтрация вебхуков (YOOOKASSA_WEBHOOK_ALLOWED_CIDRS): блокировка входящих уведомлений от неизвестных источников.

Исправлено

Обработка смены магазина/способа оплаты Логика intentFromExisting обновлена: если заказ существует, но платёж у провайдера не найден (404) или был создан с другими параметрами (ошибка идемпотентности), система автоматически сбрасывает привязку к старому платежу и создаёт новый. Это устраняет ошибки "Payment method is not available" и конфликты ключей при переходе с тестового на боевой магазин или с карты на СБП.

Изменено

Архитектура зависимостей Введение слоя PaymentService, который оркеструет взаимодействие между QuotaService (бизнес-логика квот) и YooKassaGateway (внешний мир). Бизнес-логика начисления кредитов и активации подписок осталась изолированной в существующих сервисах, что позволило переиспользовать её без дублирования кода.


[0.13.6] - 2026-10-12

Добавлено

  • Cleanup job — автоматическая очистка записей conversion_logs старше 90 дней через CleanupOldLogs().
  • Permanent Delete — полное удаление пользователей через 14 дней после soft delete через PermanentDeleteAccount() с индексом idx_users_deleted_at (is_active, deleted_at).
  • Лимит истории конвертаций — максимум 500 записей на пользователя (MaxHistoryPerUser), автоматический trim при каждой вставке через транзакцию UPSERT + DELETE старых.
  • Изоляция rate limit — глобальный IP rate limit исключён для auth endpoints (/api/auth/*), так как они имеют собственные специфичные лимиты (LoginRateLimits, RegisterRateLimits).

Изменено

  • Graceful shutdown порядок — scheduler.Stop() теперь вызывается перед httpServer.Shutdown() и db.Close(), гарантируя завершение активных cron jobs до закрытия соединений с БД.
  • Транзакция ConversionLogRepository.Save — UPSERT + trim старых записей выполняются в одной транзакции для атомарности.
  • Константы базовых тарифов — baseMicroPriceRubles, baseMicroCredits, baseMonthlyPriceRub вынесены как единственные источники истины для формул.

Исправлено

  • Баг с двойным case на один канал ticker.C в старом scheduler — заменял случайный выбор одного из двух case на последовательный запуск задач.
  • Защита от отрицательной экономии — явный if savings < 0 { savings = 0 } вместо разорванного max() с комментарием.
  • Fail-open стратегия в scheduler — ошибка в одной cleanup-задаче не блокирует выполнение других.

Безопасность

  • Fail-open в rate limit middleware — при недоступности Redis auth endpoints fallback-ят в БД, не блокируя легитимных пользователей.

[0.12.3-rs] - 2026-10-10

Добавлено

  • 🎨 Редизайн главной страницы — шесть новых информационных блоков на landing page для улучшения onboarding и конверсии.
  • <RecentConversions /> — блок последних конвертаций с CLS-free (Cumulative Layout Shift) плавным смещением при появлении/скрытии Preview. Позволяет быстро повторить предыдущий запрос.
  • <HowItWorks /> — пошаговое объяснение процесса конвертации для новых пользователей.
  • <FreeQuotaInfo /> — визуальное сравнение лимитов и возможностей между Free-планом, пакетами кредитов и подписками.
  • <UseCases /> — блок сценариев использования (LLM-датасеты, документация, контент-агрегация).
  • <FeaturesOverview /> — блок «Engineered for Speed & Reliability» с описанием технических преимуществ (кэширование, Readability, параллельная обработка).
  • <ApiDocBanner /> — CTA-баннер для привлечения внимания к API-документации и использованию API-ключей.
  • Поддержка подписок в UI — отображение статуса подписки (is_subscriber, days_remaining) в QuotaBadge и UserDropdown.
  • Три типа монетизации в Pricing — вкладки «Free / Credit Packs / Subscriptions» с переключением между типами планов.

Изменено

  • Структура главной страницы — блоки расположены в логической последовательности: форма → опции → preview → recent → обучающие блоки → CTA.
  • Анимации появления блоков — плавное смещение контента без layout shifts при изменении состояния Preview.
  • Адаптивный дизайн — все новые блоки оптимизированы под мобильные устройства (grid → single column).

Производительность

  • CLS-free анимации — использование transform и opacity вместо изменения layout-свойств для плавных переходов без смещений.
  • Lazy rendering — компоненты монтируются только когда они видимы во viewport.

UX

  • Быстрый доступ к истории — RecentConversions позволяет одним кликом повторить предыдущую конвертацию.
  • Образовательный контент — HowItWorks + UseCases снижают барьер входа для новых пользователей.
  • Прозрачность тарифов — FreeQuotaInfo помогает пользователю выбрать подходящий план без необходимости читать документацию.

[0.11.4-beta] - 2026-10-05

Добавлено

  • Три типа монетизации — единый ответ GET /api/billing/packages теперь возвращает { free, credits, subscriptions } вместо плоского списка пакетов.
  • Новые пакеты кредитов — Micro (300 кредитов за 49₽) для быстрых задач и Ultra (20 000 кредитов за 799₽) для тяжёлых сценариев и сбора датасетов.
  • Авто-расчёт производных полей пакетов — PricePer100Requests, SavingsRub и DiscountPercent вычисляются по формулам относительно базового тарифа Micro через конструктор newCreditPackage().
  • Функция ceilDiv — округление цены за 100 запросов вверх (16.33 → 17₽) для честной маркетинговой цены без float и импорта math.
  • Метрика PricePer100Requests вместо «копеек за запрос» — понятнее для пользователя (например, «17 ₽ за 100 запросов»).
  • Подписки (Subscriptions) — 4 периода (1/3/6/12 месяцев) с базовой ставкой 499₽/мес и авто-расчётом экономии через newSubscriptionPlan().
  • Безлимитный доступ для подписчиков — CheckQuota и IncrementUsage пропускают пользователей с активной подпиской без списания кредитов и дневного лимита.
  • Агрегированный ответ UsageStatsResponse — один эндпоинт возвращает и кредиты, и информацию о подписке (credits + subscription + is_subscriber).
  • Флаг IsPopular для пакетов — позволяет фронтенду подсвечивать «Хит продаж» (Optimal).

Изменено

  • Кэш подписки — проверка HasActiveSubscription кэшируется в Redis с TTL 10 минут и инвалидируется при активации.
  • Базовая ставка подписки — 499₽/мес (было 699₽), скидка на 12 месяцев увеличена до 20%.

Исправлено

  • Детерминированность detectContentLanguage — язык теперь всегда определяется на чистом контенте (до добавления title/TOC/frontmatter), независимо от комбинации флагов.
  • Fail-open стратегия — при падении Redis проверка подписки и кредитов продолжает работать через БД, не блокируя пользователя.

[0.10.4] - 2026-10-04

Добавлено

  • 💳 Подписки (Subscriptions) — новая модель безлимитного доступа к конвертеру.
  • Таблица user_subscriptions — миграция для хранения активных подписок с полями starts_at, ends_at, updated_at и constraint starts_at <= ends_at.
  • SubscriptionRepository — интерфейс с методами HasActiveSubscription, GetActive, Activate.
  • Атомарный SQL-апсерт подписок — один запрос INSERT ... ON CONFLICT DO UPDATE корректно продлевает активную подписку или создаёт новую для истёкшей. Защищает от race condition при двойном клике на оплату.
  • Доменная сущность Subscription с методами IsActive() и DaysRemaining().
  • Интеграция в QuotaService — методы HasActiveSubscription и ActivateSubscription с кэшированием в Redis (TTL 10 мин) и автоматической инвалидацией при активации.
  • Оптимизированный запрос HasActiveSubscription — SELECT true ... LIMIT 1 для максимальной скорости в hot path.
  • Тестовый эндпоинт POST /api/billing/test/activate-subscription для проверки функционала без интеграции с платёжной системой (только в dev/test режиме).
  • DTO SubscriptionResponse и UsageStatsResponse — агрегированная структура для отображения статуса подписки на фронтенде (активна / дней до окончания / безлимит).

Изменено

  • checkAuthenticatedQuota — сначала проверяется наличие активной подписки, и только при её отсутствии выполняется проверка кредитов и дневного лимита.
  • IncrementUsage — подписчики не тратят кредиты и не увеличивают счётчик дневного лимита.
  • GetUsageStats — возвращает агрегированную информацию (кредиты + подписка) в одном запросе.
  • Clean Architecture — проверка подписки теперь идёт через сервис, а не через прямой вызов репозитория из checkAuthenticatedQuota.

Безопасность

  • Атомарность upsert — SQL-запрос с ON CONFLICT исключает состояние гонки при одновременных запросах на активацию подписки.
  • Fail-open стратегия — при недоступности Redis проверка подписки fallback-ит в БД, не блокируя легитимных пользователей.

[0.9.2-rc.1] - 2026-10-03

Добавлено

  • 📑 Оглавление (Table of Contents) — новая опция include_toc в API-запросе для генерации оглавления на основе заголовков H1–H3.
  • Локализация оглавления — заголовок TOC автоматически переводится на 84 языка (определяется из контента): «Оглавление» для русского, «Table of Contents» для английского и т.д.
  • Кнопка «Оглавление» на фронтенде — новый чип в панели параметров конвертации для включения/отключения TOC.
  • Отображение метаданных в UI — блок с количеством слов, токенов и размером в байтах под превью конвертации.
  • Расширенный YAML Frontmatter — добавлены поля: language, words, tokens, reading_time, extracted_at.

Изменено

  • Формат якорных ссылок в TOC изменён на универсальный [Заголовок](<#Заголовок>) — корректно работает в Obsidian, GitHub и стандартных Markdown-парсерах.
  • Метаданные (words, tokens, bytes) вычисляются для чистого контента статьи до добавления title/frontmatter/toc и всегда возвращаются в ответе API.

[0.8.3-rc] - 2026-10-03

Добавлено

  • 🔍 Автоматическое определение языка контента — триграммный анализ через whatlanggo с предочисткой текста от Markdown-разметки, кода и URL-адресов.
  • Структура Metadata в ответе API с полями words, tokens, bytes — возвращается для каждой успешной конвертации независимо от флагов.
  • Поле language в YAML Frontmatter — определяется автоматически (например, "Russian", "English", "German").
  • Метаданные времени чтения (reading_time) и даты извлечения (extracted_at) в Frontmatter.

Изменено

  • Оптимизирована производительность генерации TOC: регулярные выражения и strings.NewReplacer вынесены на уровень пакета, используются sb.Grow() и прямой WriteString вместо fmt.Fprintf.
  • Улучшена точность определения языка: анализ ограничен первыми 5000 символами, используется порог уверенности > 0.05 вместо строгого IsReliable().
  • Вызов detectContentLanguage выполняется только один раз и только при необходимости (include_toc или include_frontmatter).

Безопасность

  • Очистка текста перед анализом языка предотвращает ложные срабатывания на технических фрагментах (URL, код, HTML-теги).

[0.7.3-beta.3] - 2026-09-27

Добавлено

  • API Keys System: Полноценная система API ключей для программного доступа к MarkClean API. Ключи генерируются в формате mk_live_<base64url> с криптографически стойкой случайностью (24 байта).
  • API Key Management UI: Новая страница /api-keys для управления персональными API ключами:
    • Генерация ключа с одноразовым показом (ключ больше никогда не будет доступен после закрытия баннера)
    • Отображение метаданных: префикс ключа, дата создания, время последнего использования
    • Удаление ключа с подтверждением
    • Кнопка "Пересоздать" для безопасной ротации (удаление старого + создание нового)
    • Пример использования cURL с автоматическим копированием
  • API Key Authentication Middleware: Middleware APIKeyMiddleware для аутентификации запросов через заголовок X-API-Key или Authorization: Bearer:
    • Поддержка обоих способов передачи ключа
    • Автоматическое определение user_id из ключа
    • Обновление last_used_at при каждом успешном запросе
  • Two-Level Authentication: Поддержка двух способов аутентификации для эндпоинтов:
    • Cookie-сессия (для веб-интерфейса)
    • API ключ (для программных клиентов)
    • Автоматическое определение приоритета через ResolveUserID
  • API Key Caching: Кэширование аутентифицированных ключей в Redis (TTL 5 минут) для снижения нагрузки на БД:
    • Быстрый путь: проверка хеша в Redis без запроса к PostgreSQL
    • Медленный путь: SHA-256 хеширование + SELECT + UPDATE при cache miss
    • Автоматическая инвалидация кэша при удалении ключа
  • Security Best Practices:
    • В БД хранится только SHA-256 хеш ключа (не сам ключ)
    • Ключ показывается пользователю только один раз при создании
    • Частичный уникальный индекс idx_api_keys_user_unique для ограничения "один ключ на пользователя"
    • Защита от перебора через валидацию формата перед запросом в БД

Изменено

  • Converter Endpoints: Эндпоинты /api/convert и /api/converter/history теперь поддерживают как сессионную аутентификацию, так и API ключи:
    • OptionalAuthMiddleware + APIKeyMiddleware для /convert (публичный + API)
    • RequireAuth middleware для /history (требует любую аутентификацию)
  • User Context Resolution: Унифицированный helper apikeys.ResolveUserID() для извлечения user_id из контекста gin с приоритетом: сессия → API ключ
  • API Documentation: Добавлена полная документация по использованию API через ключи с примерами на Python, Node.js, Go и cURL

Безопасность

  • One-Time Key Display: Критическое требование безопасности — сырой ключ возвращается только в ответе POST /api/keys и никогда не сохраняется в БД или логах
  • Hash-Only Storage: В таблице api_keys поле key_hash содержит SHA-256 хеш, что делает невозможным восстановление ключа при утечке БД
  • Graceful Degradation: При падении Redis middleware продолжает работать через PostgreSQL (fail-open стратегия)
  • Key Invalidation: При удалении ключа автоматически инвалидируется кэш аутентификации через SCAN+DEL по паттерну

Производительность

  • Redis Cache Hit Rate: ~95% запросов с API ключом обслуживаются из Redis кэша (1-2ms вместо 20-50ms для PostgreSQL)
  • Reduced DB Load: Кэширование снижает нагрузку на PostgreSQL в 10-20 раз для активных API клиентов
  • Lazy Invalidation: Кэш инвалидируется только при удалении ключа, не при каждом использовании

API Endpoints

Управление ключами (требует сессию):

  • GET /api/keys — получить метаданные текущего ключа
  • POST /api/keys — создать новый ключ (возвращает сырой ключ один раз)
  • DELETE /api/keys/:id — удалить ключ

Использование ключа:

  • POST /api/convert — конвертация с заголовком X-API-Key: mk_live_...
  • GET /api/converter/history — история конвертаций с API ключом

Frontend Features

  • Copy to Clipboard: Кнопки копирования для ключа и cURL примера с визуальным feedback ("Скопировано!")
  • Loading States: Skeleton loaders при загрузке, спиннеры при создании/удалении
  • Error Handling: Полная обработка всех API ошибок с локализованными сообщениями (RU/EN)
  • Responsive Design: Адаптивная верстка для мобильных устройств
  • Accessibility: ARIA-атрибуты, фокус-менеджмент, keyboard navigation

Тестирование

  • Postman Collection: Готовая коллекция для тестирования всех сценариев
  • curl Examples: Примеры запросов с API ключом для всех опций конвертации
  • Error Scenarios: Тесты неверного ключа, удалённого ключа, отсутствия ключа

[0.6.3-beta.2] - 2026-09-26

Добавлено

  • Email Anonymization on Delete: Полное обезличивание PII при удалении аккаунта — email заменяется на HMAC-SHA256 хеш, registration_ip обнуляется, password_hash очищается. Соответствует требованиям GDPR (право на забвение).
  • Re-registration Prevention: Защита от повторной регистрации с тем же email после удаления аккаунта. При удалении email хешируется через utils.HashIdentifierWithKey() с секретным ключом приложения, при регистрации проверяется наличие хеша в базе.
  • GetByEmailIncludingDeleted: Новый метод в UserRepository для поиска пользователей по email или его хешу, включая удалённые записи (без фильтра deleted_at IS NULL).
  • PII Cleanup: При удалении аккаунта также обнуляется registration_ip для полного удаления персональных данных из базы.
  • Register Validation: Добавлена проверка на наличие удалённого аккаунта с тем же email через поиск хеша в базе. Возвращается новая ошибка ErrEmailPreviouslyDeleted с HTTP 403 Forbidden.
  • Frontend i18n: Добавлены переводы для новой ошибки email_previously_deleted (RU/EN) с объяснением причины блокировки регистрации.

Изменено

  • DeleteAccount Flow: Переписан процесс удаления аккаунта — теперь email хешируется перед сохранением в БД вместо конкатенации 'deleted_' || id || '_' || email.
  • UserRepository Interface: Расширен интерфейс — добавлен метод GetByEmailIncludingDeleted(ctx, emailOrHash) для поддержки поиска по хешу.
  • Error Handling: Новый тип ошибки ErrEmailPreviouslyDeleted с понятным сообщением для пользователя: "This email was previously used for a deleted account and cannot be reused".

Безопасность

  • HMAC-SHA256: Используется криптографически стойкий HMAC с секретным ключом приложения (cfg.App.Secret) для хеширования email. Без знания секрета невозможно восстановить оригинальный email из хеша.
  • No PII Leaks: В случае утечки базы данных злоумышленник получит только необратимые хеши вместо реальных email-адресов удалённых пользователей.
  • Abuse Prevention: Невозможно обойти лимиты или получить приветственные бонусы повторно через цикл "удаление → регистрация".

[0.5.4-beta.1] - 2026-09-25

Добавлено

  • ConvertOptions: Четыре новые опции конвертации для гибкой настройки вывода:
    • include_title — добавляет заголовок страницы как H1 (# Title) в начало документа
    • include_frontmatter — добавляет YAML-шапку с метаданными (title, source, bytes, tokens)
    • strip_images — удаляет все <img> теги из результата
    • strip_links — удаляет <a> теги, сохраняя текст ссылок
  • Two-Level Cache Architecture: Двухуровневая система кэширования для экономии кредитов и снижения нагрузки:
    • Уровень 1 — Raw HTML: сырой HTML кэшируется на 30 минут (ключ: convert:html:{normalized_url})
    • Уровень 2 — Final Markdown: финальный результат кэшируется на 5 минут (ключ: convert:result:{url}:{params_hash})
    • Хэш параметров (SHA-256) обеспечивает уникальный ключ для каждой комбинации опций
  • Smart Credit Deduction: Кредиты списываются только при реальном HTTP-запросе к внешнему сайту. При попадании в любой уровень кэша — конвертация бесплатна.
  • Frontend Conversion Options: UI-контролы для всех 4 опций конвертации:
    • Чекбоксы Include Title, Include Frontmatter, Strip Images, Strip Links
    • Опции передаются в API через POST /api/convert в теле запроса
    • Состояние опций сохраняется между конвертациями в рамках сессии
  • CachedHTML DTO: Новая структура dto.CachedHTML для хранения сырого HTML с метаданными (html, title, fetched_at) в Redis.

Изменено

  • ConverterService: Полная переработка метода ConvertURLToMarkdown():
    • ШАГ 1: Проверка кэша финального результата (URL + параметры)
    • ШАГ 2: Проверка кэша сырого HTML (только URL)
    • ШАГ 3: Readability-парсинг выполняется один раз для обоих сценариев (кэш и свежий fetch)
    • ШАГ 4: Конвертация HTML→Markdown с учётом текущих опций
    • ШАГ 5: Списание кредитов только если fromCache == false (реальный fetch)
    • ШАГ 6: Сохранение результата в оба уровня кэша
  • API Contract: Тело запроса POST /api/convert расширено — добавлены опциональные поля:
    {
      "url": "https://example.com",
      "include_title": true,
      "include_frontmatter": false,
      "strip_images": false,
      "strip_links": false
    }
    
  • Cache Key Generation: Ключ кэша финального результата теперь включает хэш параметров — изменение любой опции генерирует новый ключ, не перезаписывая предыдущие результаты.
  • Readability Optimization: Устранён дубль вызова readability.FromReader() — парсинг основного контента и извлечение заголовка выполняются строго один раз, независимо от источника HTML (кэш или fetch).

Производительность

  • Credit Savings: Повторная конвертация того же URL с другими опциями не списывает кредиты — сырой HTML берётся из кэша, конвертируется заново бесплатно.
  • Reduced External Requests: При попадании в HTML-кэш (TTL 30 мин) внешний HTTP-запрос не выполняется — экономия времени (30+ секунд) и снижение нагрузки на целевые сайты.
  • Single Readability Pass: Оптимизация CPU — readability вызывается один раз вместо двух при свежем fetch.
  • Compact Cache Keys: Хэш параметров (8 байт SHA-256 → 16 hex-символов) вместо полной сериализации опций — компактные ключи Redis.

UX Улучшения

  • Instant Re-conversion: При изменении опций (например, включение strip_images) результат появляется мгновенно — HTML уже в кэше, нужна только быстрая конвертация.
  • No Surprise Charges: Пользователь может экспериментировать с опциями для одного URL без страха потратить кредиты — списание происходит только при первом fetch.
  • Flexible Output: Пользователь настраивает формат вывода под свои нужды:
    • Для LLM: strip_images: true + strip_links: true — чистый текст без мусора
    • Для документации: include_frontmatter: true — метаданные для статических генераторов
    • Для быстрого чтения: include_title: true — заголовок сразу виден

Сценарии кэширования

Сценарий Result Cache HTML Cache Fetch Кредиты Readability
Первый запрос ❌ ❌ ✅ −1 1×
Повтор с теми же опциями ✅ ✅ ❌ 0 0×
Повтор с другими опциями ❌ ✅ ❌ 0 1×
После истечения HTML cache (30 мин) ❌ ❌ ✅ −1 1×

[0.4.6-beta] - 2026-09-24

Добавлено

  • Smart History Deduplication: Умная дедупликация истории конвертаций — при повторной конвертации того же URL запись обновляется, а не создаётся дубликат. Добавлены поля count_usage (количество конвертаций) и updated_at (время последнего использования).
  • UPSERT Logic: Реализован UPSERT через ON CONFLICT (user_id, target_url) WHERE user_id IS NOT NULL — для авторизованных пользователей одна запись на URL, для анонимов допускаются дубликаты (их история не показывается в UI).
  • Partial Unique Index: Создан partial unique index idx_conversion_logs_user_url_unique на (user_id, target_url) с условием WHERE user_id IS NOT NULL для предотвращения дубликатов только для авторизованных пользователей.
  • Usage Counter: Счётчик count_usage автоматически инкрементируется при каждой повторной конвертации того же URL, позволяя видеть популярность страниц.
  • Smart Sorting: История теперь сортируется по updated_at DESC — недавно использованные URL всегда сверху списка, а не по времени первой конвертации.

Изменено

  • Database Schema: Добавлены колонки count_usage INT NOT NULL DEFAULT 1 и updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() в таблицу conversion_logs. Создан индекс idx_conversion_logs_user_updated для быстрой сортировки.
  • ConversionLog Domain: Добавлены поля CountUsage int и UpdatedAt time.Time в структуру domain.ConversionLog.
  • Repository Save Method: Переписан метод Save() в postgresConversionLogRepo — для авторизованных пользователей используется UPSERT с обновлением всех полей кроме id, user_id, target_url, created_at и инкрементом count_usage.
  • Repository ListByUserID: Изменена сортировка с ORDER BY created_at DESC на ORDER BY updated_at DESC для отображения недавно использованных URL сверху.
  • DTO HistoryItem: Добавлены поля CountUsage и UpdatedAt в dto.HistoryItem для отображения в UI.
  • Frontend HistoryDrawer: Обновлён компонент для отображения count_usage (badge с количеством конвертаций) и updated_at (время последнего использования вместо первого).

Производительность

  • Reduced Storage: Устранено бесконечное дублирование записей — один URL = одна запись для пользователя вместо N записей при N конвертациях.
  • Index Optimization: Partial unique index и индекс для сортировки по updated_at обеспечивают быстрые запросы даже при большом объёме данных.
  • Query Efficiency: UPSERT выполняется одним атомарным SQL-запросом вместо SELECT + INSERT/UPDATE, снижая нагрузку на БД.

UX Улучшения

  • Frontend: Добавлен логотип с анимацией при наведении курсором.
  • Recent First: Пользователь видит недавно использованные URL сверху списка, что упрощает доступ к часто конвертируемым страницам.
  • No Clutter: История не забивается дубликатами — чистый и понятный список уникальных URL с метаданными.

[0.3.10] - 2026-09-23

Добавлено

  • Credit-Based Billing: Полная переработка биллинга — переход от подписок к системе кредитов. Платные кредиты не сгорают и не имеют срока действия, бесплатный дневной лимит (5 запросов/сутки) сбрасывается каждые 24 часа.
  • Priority Deduction Logic: Двухуровневое списание с приоритетом — сначала расходуются платные кредиты, затем бесплатный дневной лимит. Атомарные SQL-операции гарантируют корректность при конкурентных запросах.
  • Credit Packages: Три пакета кредитов для покупки — Старт (1 000 за 99 ₽), Оптимальный (3 000 за 199 ₽, −33%), Профи (8 000 за 399 ₽, −50%).
  • Billing API v2: Новые эндпоинты GET /api/billing/credits (баланс), GET /api/billing/packages (каталог пакетов), POST /api/billing/purchase (покупка).
  • Test Endpoints: Отладочные эндпоинты POST /api/billing/test/add-credits (+100 кредитов) и POST /api/billing/test/reset-daily (сброс дневного лимита) для упрощения разработки.
  • Skeleton Loaders: Плавные скелетон-загрузчики в QuotaBadge и UserDropdown при ожидании данных биллинга.
  • i18n Expansion: Новые ключи переводов (RU/EN) для кредитной системы: баланс кредитов, дневной лимит, пакеты, покупка, цена за запрос, процент выгоды.

Изменено

  • Database Schema: Удалены таблицы subscriptions и user_current_usage. Добавлена таблица user_credits (credits, daily_used, daily_reset_at) с CHECK-ограничениями и каскадным удалением.
  • Domain Layer: Полная замена Subscription/PlanLimits/UsageStats на UserCredits/CreditPackage/PackagesConfig с хелпер-методами (CanUseCredit, ShouldResetDaily, DeductCredit).
  • QuotaService: Переписан с нуля — убрана логика подписок и автопродления, добавлен автоматический сброс дневного лимита при проверке квоты (lazy reset через time.Since).
  • Frontend Billing Store: BillingStore теперь хранит CreditsResponse вместо SubscriptionResponse, оптимистичное обновление корректно обрабатывает приоритет (кредиты → дневной лимит).
  • QuotaBadge Component: Адаптивное отображение — ⚡ 1 234 при наличии кредитов, 📅 2/5 при использовании дневного лимита. Мини-прогресс-бар показывается только при отсутствии кредитов.
  • UserDropdown: Новый блок квоты с прогресс-баром дневного использования, таймером сброса и кнопкой «Купить кредиты» с градиентным стилем.
  • Pricing Page: Полная переработка — карточки пакетов вместо тарифных планов, отображение цены за запрос (в копейках), бейджи выгоды.
  • Error Handling: Обновлён HTTP 402 ответ quota_exceeded — сообщение теперь учитывает обе причины (нет кредитов + дневной лимит исчерпан).
  • Cache Strategy: Кэш баланса кредитов (TTL 5 мин) вместо кэша подписок (TTL 10 мин) — более агрессивная инвалидация для часто меняющихся данных.
  • Removed: Удалены устаревшие SubscriptionRepository, UsageRepository, feature flags инфраструктура, все эндпоинты тестового апгрейда планов.

[0.2.3-beta] - 2026-09-22

Добавлено

  • Billing & Subscriptions: Модуль управления тарифными планами (Free, Basic, Pro) с ежемесячными квотами и атомарным трекингом использования (usage tracking).
  • Auth & Security: Полноценный цикл аутентификации на HttpOnly cookies, сброс пароля по OTP на email и защита от брутфорса.
  • Rate Limiting: Многоуровневое ограничение запросов (суточное для анонимных пользователей, месячное по тарифам для авторизованных).
  • History & Logs: Пагинируемая история конвертаций в API и интерфейсе с разделением хранения логов и лимитов.
  • UX & Frontend Integration: Уведомления об исчерпании лимитов, модальные окна регистрации и повторное использование сохраненных URL.

Изменено

  • API v1: Переход бэкенда на новую версию архитектуры (Clean Architecture).
  • Database Architecture: Миграция хранилища на PostgreSQL (управление миграциями через Goose) и Redis для кэширования и лимитов.
  • CI/CD Pipeline: Автоматизация сборки Docker-образов и деплоя через приватный registry Gitea.

[0.1.3-alpha] - 2026-06-15

Добавлено

  • Базовый API конвертации HTML в чистый Markdown для LLM.
  • Поддержка первичного веб-интерфейса на SvelteKit.
  • Базовая обработка ошибок и первичная валидация входных URL.

[0.1.0-alpha] - 2026-05-01

Добавлено

  • Инициализация проекта MarkClean, MVP мотора конвертации текста.