Технический анализ · 17 сентября 2026

Verse: подготовка к передаче

Отдельная ветка для стейджа и новой команды. Сохраняем нужное HTTP-поведение, переносим согласованный профиль пользователя, убираем старые данные и обслуживающий их код.

Ветка: handoff/verse-stageОснова: origin/main · 644fc9fСтатус: план, не реализованная очистка

1. Что делаем и чего не трогаем

Сейчас создана только новая рабочая ветка и этот план. Прикладной код не удалён, деплой не запускался, базы не менялись. Рабочая копия: /opt/github/hmkteam/verse-handoff. Основная копия /opt/github/hmkteam/verse не переключалась.

69явных HTTP-маршрутов
35 + 11коллекций приложения и авторизации
1профиль в текущем экспорте миграции

Базовый вариант: сначала сохранить работающие HTTP-контракты. Удаление самих функций продукта, способов входа, партнёрских API или страниц требует отдельного решения. Наличие ручки в коде не доказывает, что ею пользуются.

Не мигрировать данные ≠ удалить их тип. Старые сессии и платежи не входят в экспорт пользователя, но новой установке могут понадобиться новые сессии и платежные записи. Удаляем модель только после удаления или замены всех её потребителей.

Анализ выполнен по исходникам main на указанном коммите. Фронтенд стейджа ранее деплоился из ветки agreement: перед совместным релизом нужно проверить его отличия. Частоту вызовов по логам и поведение всех ручек на живом сервере в рамках этого анализа не проверяли.

2. Как перенос работает сейчас

Импорт разрешён при appName !== 'Verse_Prod'. На исходном проде это условие сохраняем. Сейчас адрес берётся из BITQUEST_VERSE_API_URL, путь из VERSE_MIGRATION_SOURCE_API_PATH. Название Bitquest в конфиге и функциях историческое и само по себе не определяет источник.

Для стейджа ранее был настроен источник https://api-verse.hamsterkombatgame.io и путь /api/migrate/user. В коде значение по умолчанию для пути всё ещё /api/verse/user: в новой установке это нужно исправить, не полагаясь на старые ENV.

После импорта сейчас выполняется старый код. В /sync остались обращения к interlude_user_mini и season2_tokens. Первая ветка снимает поля рассчитанного золота при выполнении условия пересчёта; вторая устанавливает tokenSeason2Claim = 0 и пересчитывает потенциальное золото. Перед удалением коллекций заменяем эти ветки нормализацией импортированного профиля и проверяем сохранность значений.

Целевой путь: вход → локальный профиль либо один импорт → проверка отдельного входного DTO → явное преобразование в компактную модель → сохранение → ответ. Никакого повторного обращения к старым сезонным таблицам. Параллельные первые запросы не должны перезаписывать уже созданный профиль или повторять награды.

Два разных изменения: убрать поля из хранения на стейдже можно преобразованием ответа на стороне получателя. Чтобы они вообще не пересекали границу передачи, нужно отдельно сузить экспорт на источнике. Это изменение прода не входит в текущие действия и требует согласования совместимости.

Основания: backend/src/hid/http.ts:123, backend/src/hid/logic.ts:176, backend/src/migration/http.ts:15, backend/src/Config.ts:98, backend/src/hid/interlude/migrate.ts:75.

3. Что делать с полями

Сохранить поведениеКандидат на удалениеСначала заменить / согласовать
Группа и поляСейчасПлан
id, createdAt, updatedAt, lastSyncAtВходят в экспорт. Ключ профиля используется при авторизации и sync.Сохранить. Отдельно определить исходные и локальные даты. Проверить соответствие ID для Telegram и объединённых аккаунтов; не переносить старые сессии.
totalDiamonds, balanceDiamonds, earnPassivePerHourВходят в экспорт; часть показателей отображается в старых сезонных экранах.Не удалять автоматически. Проверить нужность самих экранов. Профиль Season 1 подключает InterludeResult, поэтому это не просто забытые типы.
tokenBalancetokens_passive, tokens_keys, tokens_friends, tokens_achivements, tokens_tasks, total, claimed, unclaimed, nextUnlockedСохранить расчёты и нужные экрану значения. claimed / unclaimed / nextUnlocked участвуют в золоте; детализацию удалять только вместе с её UI-потребителями.
totalHamsterGold1..3,
totalSeason2HamsterGold1..4
Балансы по играм и сезонам. Возвращаются в HTTP-профиле и партнёрском API.Сохранить. Не суммировать и не обнулять. Нули и отсутствие значения тестировать отдельно.
potentialToken, potentialHamsterGold,
potentialSeason2Token, potentialHamsterGoldSeason2
Потенциальные суммы конвертации; часть вычисляется при sync.Сохранить смысл. Выбрать один источник расчёта и проверить результат до и после очистки.
rewardState, rewardGoldState, rewardGoldStateSeason2Флаги по boost, gamedev, fightclub, king, paws.Сохранить. Не терять историю получения наград и не создавать возможность повторного получения.
rewardGoldFrontStateВходит в схемы и экспорт; в выполненном поиске бизнес-потребитель не найден.Кандидат. Перед удалением проверить внешних клиентов: поле доступно через HTTP.
tokenSeason2, tokenSeason2Claimtoken1..4, token5?, tokenTotal и флаг: отсутствует / 0 / 1. Используются сезонной логикой.Сохранить право на конвертацию. Убрать зависимость от таблицы season2_tokens, но не обнулить суммы и состояние уже полученной награды.
withdraw.infoУже исключён из текущего экспорта. Внутренняя модель всё ещё содержит UID, адрес, memo, on-chain данные, подпись и хеш транзакции.Убрать из целевой модели и из общих типов, если других оставленных потребителей нет. Не включать в fixtures или логи.
withdraw.round, round2, unavailable, transactionRechecked, transactionRecheckedSyncСейчас передаются. В просмотренной основной логике sync / конвертации не используются для расчёта.Предложение: исключить из новой модели. Сузить экспорт на проде только отдельным согласованным изменением; проверить клиентские контракты.
withdraw.state, selected25 + SaveInGame участвуют в расчёте золота. Фронт показывает название биржи для состояний 18,19,20,22; 22 означает Bybit.Нельзя просто удалить. Предлагается нейтральное право на конвертацию и нейтральный UI без биржи. Эквивалентность расчёта подтвердить тестами. Это предложение, а не уже согласованный контракт.
tokenRechekedAt, tokenRechekedAt2, tokenRechekedAt3Все передаются. Третий ограничивает старый пересчёт в sync; первые два в просмотренной основной логике не читаются.Убирать вместе со старым пересчётом. Не оставлять путь, который без маркера повторно изменит баланс.
migrateBqAtНе экспортируется: получатель ставит локально.Заменить локальной меткой импорта с нейтральным названием. Не считать её исходной датой регистрации.

Формула, которую нельзя потерять: если unclaimed === 0, берём claimed только для state = 25 и selected = SaveInGame; иначе берём unclaimed. Прибавляем nextUnlocked и умножаем на 15. Для второго сезона используется tokenTotal × 15.

Основания: backend/src/migration/types.ts:57, protocol/src/hid/logic/types.ts:199, backend/src/hid/logic.ts:78, frontend/components/airdrop/Tokens.vue:1, frontend/components/profile/MenuBalance.vue:1, frontend/components/profile/Season1.vue:28.

4. HTTP-контур и новая база

КонтурНайдено в кодеРешение для первого этапа
Пользовательский Verse9 маршрутов: конфиг, sync, токен вендора, награды, две конвертации, партнёрские балансы.Сохранить. Отдельные DTO для импорта, хранения и HTTP-ответов: сейчас в нескольких местах возвращается широкий VerseUser.
Авторизация14 маршрутов, включая Telegram, TON, device ID, Google, объединение аккаунтов и административный вход.Сохранить необходимые способы входа. Удаление TON-типов вывода не означает, что можно удалить TonAuth. Проверить и ограничить служебные способы входа.
Партнёры и лидерборды2 vendor + 31 leaderboard/PVE.Сохранить до решения по партнёрам. Исторические рейтинги не мигрируются этой ручкой; схемы нужны для новых событий.
Админка8 маршрутов, в том числе чтение raw, изменение профиля, удаление, speed-test и просмотр источника.Состав пересмотреть отдельно. Диагностические и опасные методы не передавать автоматически только потому, что они HTTP.
TelegramWebhook и зарегистрированные обработчики платежа / возврата; бот также читает cbt_user.Не удалять бота и платежные типы без решения о поддержке этих сценариев. Ручка создания invoice в hid/http закомментирована: её не включали в 69 маршрутов.
Экспорт миграции1 API: /api/migrate/user.На исходном проде оставить. На получателе необходимость собственного экспорта согласовать отдельно; импортный клиент ему не равнозначен.
Эксплуатация/, /health, /metrics; отдельно Swagger и static-plugin.Оставить необходимые проверки здоровья и метрики. Swagger / playground ограничить или убрать из передаваемого внешнего контура.

Что не входит в текущий перенос

Ручка читает только профиль verse_user. Она не переносит auth-коллекции, историю инвойсов, рейтинги, newsletter, подписчиков и кэши. Это не обещание, что все эти сущности можно удалить из кода: часть заполняется заново, часть требует настройки или отдельного решения.

Удаление старых таблиц: interlude_user_mini и season2_tokens становятся кандидатами после замены sync. users1_cache / users2_cache / users3_cache помечены TODO, но всё ещё упоминаются в leaderboard-модулях. По комментарию удалять нельзя.

Инфраструктура: App.create требует Kafka и ClickHouse; HTTP-лидерборды и аналитика получают эти сервисы. На первом этапе сохраняем их подключение. Отказ от них является отдельным изменением архитектуры, а не удалением «лишних» файлов. Новый бот, ключи партнёров, внешние магазины и расписания требуют явной настройки на пустой базе.

Полный список 69 явных маршрутов с местом в коде

69 маршрутов

МетодПутьИсходник
GET/backend/src/App.ts:127
GET/healthbackend/src/App.ts:128
GET/metricsbackend/src/App.ts:129
POST/auth/auth-by-telegram-webappbackend/src/auth/AuthHttp.ts:42
POST/auth/auth-by-telegrambackend/src/auth/AuthHttp.ts:83
POST/auth/telegram-webapp-conflictbackend/src/auth/AuthHttp.ts:111
POST/auth/me-telegrambackend/src/auth/AuthHttp.ts:143
POST/auth/ton-proof/generate-payloadbackend/src/auth/AuthHttp.ts:161
POST/auth/ton-proof/check-proofbackend/src/auth/AuthHttp.ts:179
POST/auth/auth-by-deviceIdbackend/src/auth/AuthHttp.ts:217
POST/auth/auth-by-googleIdbackend/src/auth/AuthHttp.ts:252
POST/auth/auth-by-user-idbackend/src/auth/AuthHttp.ts:291
POST/auth/auth-by-user-id-adminbackend/src/auth/AuthHttp.ts:327
POST/auth/account-infobackend/src/auth/AuthHttp.ts:358
POST/auth/merge-accountsbackend/src/auth/AuthHttp.ts:401
POST/auth/get-avatarbackend/src/auth/AuthHttp.ts:419
POST/auth/select-avatarbackend/src/auth/AuthHttp.ts:469
POST/admin/verse/delete-mebackend/src/hid/adminHttp.ts:36
POST/admin/verse/show-userbackend/src/hid/adminHttp.ts:55
POST/admin/verse/update-userbackend/src/hid/adminHttp.ts:74
POST/admin/verse/update-user-balancebackend/src/hid/adminHttp.ts:97
POST/admin/verse/show-user-rawbackend/src/hid/adminHttp.ts:125
POST/admin/verse/leaderboard-speed-testbackend/src/hid/adminHttp.ts:145
GET/analytics/verse/tg-newsletterbackend/src/hid/adminHttp.ts:160
POST/admin/bitquest/verse-userbackend/src/hid/adminHttp.ts:173
GET/verse/config/:versionbackend/src/hid/http.ts:88
POST/verse/syncbackend/src/hid/http.ts:123
POST/verse/parse-tokenbackend/src/hid/http.ts:202
POST/verse/updateRewardStatebackend/src/hid/http.ts:237
POST/verse/convertToGoldbackend/src/hid/http.ts:260
POST/verse/convertToGoldSeason2backend/src/hid/http.ts:282
POST/verseVendor/getBalancesbackend/src/hid/http.ts:304
POST/verseVendor/updateGoldStatebackend/src/hid/http.ts:327
POST/verseVendor/updateGoldStateSeason2backend/src/hid/http.ts:353
POST/leaderboard/v1/create-eventbackend/src/leaderboard/LeaderboardHttp.ts:138
POST/leaderboard/v2/create-eventbackend/src/leaderboard/LeaderboardHttp.ts:172
POST/leaderboard/v1/add-me-to-eventbackend/src/leaderboard/LeaderboardHttp.ts:206
POST/leaderboard/v1/get-top-of-leaderboardbackend/src/leaderboard/LeaderboardHttp.ts:236
POST/leaderboard/v2/get-top-of-leaderboardbackend/src/leaderboard/LeaderboardHttp.ts:287
POST/leaderboard/v2/get-my-position-at-leaderboardbackend/src/leaderboard/LeaderboardHttp.ts:348
POST/leaderboard/v1/get-my-position-at-leaderboardbackend/src/leaderboard/LeaderboardHttp.ts:410
POST/leaderboard/v1/update-my-rating-parameterbackend/src/leaderboard/LeaderboardHttp.ts:463
POST/leaderboard/v1/add-userbackend/src/leaderboard/LeaderboardHttp.ts:537
POST/leaderboard/v1/my-leaderboardbackend/src/leaderboard/LeaderboardHttp.ts:569
POST/leaderboard/v1/get-event-by-idbackend/src/leaderboard/LeaderboardHttp.ts:598
POST/leaderboard/v1/get-userbackend/src/leaderboard/LeaderboardHttp.ts:619
POST/leaderboard/v1/delete-userbackend/src/leaderboard/LeaderboardHttp.ts:641
POST/leaderboard/v1/delete-user-with-ratingbackend/src/leaderboard/LeaderboardHttp.ts:660
POST/leaderboard/v1/delete-eventbackend/src/leaderboard/LeaderboardHttp.ts:680
POST/leaderboard/v1/get-leaderboardbackend/src/leaderboard/LeaderboardHttp.ts:700
POST/leaderboard/v1/get-list-usersbackend/src/leaderboard/LeaderboardHttp.ts:721
POST/leaderboard/v1/get-list-eventsbackend/src/leaderboard/LeaderboardHttp.ts:741
POST/leaderboard/v1/get-list-leaderboardsbackend/src/leaderboard/LeaderboardHttp.ts:761
POST/leaderboard/v1/migratebackend/src/leaderboard/LeaderboardHttp.ts:781
POST/leaderboard/v1/update-many-rating-parametersbackend/src/leaderboard/LeaderboardHttp.ts:810
POST/leaderboard/v1/get-cached-leaderboardbackend/src/leaderboard/LeaderboardHttp.ts:857
POST/leaderboard/v1/get-user-with-ratingbackend/src/leaderboard/LeaderboardHttp.ts:878
POST/leaderboard/v1/cleanbackend/src/leaderboard/LeaderboardHttp.ts:899
POST/leaderboard/v1/statbackend/src/leaderboard/LeaderboardHttp.ts:922
POST/leaderboard-pve/v1/create-eventbackend/src/leaderboard/LeaderboardHttp.ts:941
POST/leaderboard-pve/v1/update-my-rating-parameterbackend/src/leaderboard/LeaderboardHttp.ts:974
POST/leaderboard-pve/v1/get-top-of-leaderboardbackend/src/leaderboard/LeaderboardHttp.ts:1036
POST/leaderboard-pve/v1/get-my-position-at-leaderboardbackend/src/leaderboard/LeaderboardHttp.ts:1080
POST/leaderboard-pve/v1/get-event-by-idbackend/src/leaderboard/LeaderboardHttp.ts:1124
POST/leaderboard-pve/v1/get-stage-infobackend/src/leaderboard/LeaderboardHttp.ts:1148
POST/api/migrate/userbackend/src/migration/http.ts:16
POST/telegram-webhookbackend/src/tg/webhook.ts:33
POST/vendor/1/get-tokenbackend/src/vendor/VendorHttp.ts:58
POST/vendor/1/boost-schedulebackend/src/vendor/VendorHttp.ts:117

Количество получено из TypeScript AST в подключённых к App.ts модулях. Закомментированные ручки не учтены. Маршруты, автоматически добавляемые Swagger, static и CORS, в число 69 не входят.

Основания: backend/src/App.ts:30, backend/src/Db.ts:20, backend/src/auth/AuthDbSchema.ts:19, backend/src/tg/TelegramBot.ts:393, backend/src/auth/authUtils.ts:140.

5. Что чистим в репозитории

ОбластьДействиеУсловие
Старые сезонные миграцииУбрать raw-типы, нормализаторы, interlude/migrate и регистрацию исходных таблиц после перевода sync.Проверить все импорты: migrateGold вызывается из refreshGold; этот операторский скрипт тоже выводится из поставки.
Операторские скриптыНе включать в продукт getStarsTxs*, tgLogin, рассылки, txRefundRecovery, txUpRecovery, refreshGold и отладочные файлы.Это отдельные точки запуска, не runtime HTTP. Нужные текущему владельцу инструменты сохранить отдельно; не стирать историю отчётов.
Вне статического графа APIКандидаты: hid/applyCommand.ts, hid/interlude/types.ts, tg/tonWallet.ts, src/test.ts, src/ttt.ts.Проверить сборку, frontend, тесты и скрипты. Файл вне графа не равен доказанно неиспользуемому файлу.
Списки и старые настройкиУбрать персональные whitelist, старых администраторов, закомментированные ключи, старые домены и фиксированные настройки прежнего владельца из поставки.Не публиковать сами значения. Новые настройки задавать явно; при обнаружении действующих ключей заменить их и проверить историю Git.
Широкие типы и зависимостиПосле удаления потребителей сузить protocol, Db, HidDb, конфиги и package.json.Не удалять пакет по названию. Например, TON используется действующей авторизацией; TDLib-клиент может уйти вместе с операторскими скриптами.
ЛогиУбрать дампы профиля до/после, raw сезонных записей и тело ответа источника из сообщений ошибок; оставить коды и безопасные метаданные.Не потерять диагностику auth/sync. Секреты и initData не логировать.
Фронтенд и документацияПравить сезонные экраны, переводы, store и HTTP-клиент вместе с моделью. Переписать README под запуск на чистой базе.Не оставлять скрытые обращения к удалённым полям. Отдельно согласовать состав игр и старых сезонных экранов.

Статический ориентир: из backend/src/index.ts прослежены 77 локальных TypeScript-файлов, включая импорты типов. Это не полный анализ неиспользуемых символов: отдельные CLI, Nuxt auto-import, сборочные команды и внешних HTTP-клиентов нужно проверять дополнительно.

Ветка не очищает историю Git. Для передачи без прежних данных безопаснее подготовить отдельный проверенный снимок исходников / новый репозиторий без старой истории. Рабочую ветку от main используем для разработки и сравнения. Нельзя обещать «чистую передачу», удалив файлы только в последнем коммите.

6. Последовательность реализации

  1. Изолировать стейдж

    Запретить для handoff-ветки prod-цели во всех workflow: API, frontend, CDN, consumer, ClickHouse. Проверять branch + environment до получения prod-доступов. Зафиксировать stage bucket/scope/release, prod-настройки не менять. Сейчас этого запрета ещё нет.

  2. Зафиксировать контракт передачи

    Утвердить поля из раздела 3, HTTP-ручки и UI. По умолчанию сохранить действующее поведение. Выделить Migration DTO и тесты, запрещающие лишние поля на каждом уровне вложенности.

  3. Переписать импорт и sync

    Отделить адаптер ответа прода от внутренней модели, убрать обращения к старым таблицам. Настроить таймаут, обработку ошибки, проверку ID, безопасное создание при конкурирующих sync и локальную отметку импорта. Различать «пользователь не найден» и «источник недоступен».

  4. Сузить модель и обновить потребителей

    Убрать ненужную историю вывода после замены расчётов и UI. Сохранить балансы, начисления и флаги получения. Обновить ответы клиенту и партнёрам, не расширяя экспорт продового источника.

  5. Удалить недостижимый код и старые данные из поставки

    Убрать исходные сезонные модели, ненужные CLI, отладочные данные, whitelist, старые настройки. Затем удалить ненужные зависимости, типы, коллекции и команды сборки. Обновить тесты и документацию.

  6. Проверить на отдельной чистой stage-базе

    Не очищать даже общий стейдж автоматически. Использовать выделенные тестовые scopes, новый бот или согласованные credentials. Прогнать контракты, миграцию, UI и повторные запросы; затем согласованный stage-only релиз.

  7. Подготовить пакет новой команде

    Проверенный снимок без секретов, дампов и старой Git-истории, ENV-пример без значений, перечень сервисов и HTTP-контрактов, инструкция запуска и результаты тестов.

Что нужно решить до удаления функций

7. Как поймём, что очистка завершена

Что проверено сейчас: исходники основных HTTP-модулей, контракт миграции, сезонные расчёты, схемы БД, связанные компоненты фронтенда и workflow; выполнены AST-инвентаризация маршрутов и статический обход импортов. Очистка, её регрессионные тесты и stage-деплой ещё не выполнялись.