47 KiB
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и constraintstarts_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)RequireAuthmiddleware для/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) обеспечивает уникальный ключ для каждой комбинации опций
- Уровень 1 — Raw HTML: сырой HTML кэшируется на 30 минут (ключ:
- 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— заголовок сразу виден
- Для LLM:
Сценарии кэширования
| Сценарий | 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: Полноценный цикл аутентификации на
HttpOnlycookies, сброс пароля по 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 мотора конвертации текста.