Да, я бы убирал подписки полностью. В текущем состоянии это не недоделанная мелочь, а мёртвый функциональный слой, который создаёт ложное ожидание у оператора и путает UX. По текущему коду видно следующее: ordinary Node URL / Node QR строятся отдельно и полезны; subscription-кнопки в UI есть, но backend endpoint `Hysteria2SubscribeUrl` фактически возвращает ошибку `subscription delivery is out of scope in HY2XS baseline`. При этом в service ещё лежит старая логика генерации подписок для Clash/Shadowrocket/v2rayN, но она не подключена нормальным публичным delivery route. То есть это уже рудимент: фронт показывает кнопки, backend говорит “не поддерживается”, а пользователи всё равно подключаются через обычный URI/QR. Я бы добавил в общий план отдельный пункт. --- ## 27. Удалить subscription layer как неподдерживаемый рудимент ### Решение Оставить только: ```text Node URL Node QR Code Copy URI Rotate Secret ``` Удалить: ```text Subscribe Subscribe QR Code Subscription URL Clash subscription extension Shadowrocket/V2rayN subscription delivery /hysteria2/hysteria2SubscribeUrl ``` То есть один peer — один обычный Hysteria2 URI/QR. Этого достаточно для текущей панели, особенно если цель — упрощённая, зрелая 3x-ui-like админка, а не subscription gateway. --- ## 28. Почему лучше удалить, а не чинить сейчас Подписка — это отдельный продуктовый слой, а не просто “ещё один QR”. Чтобы она была production-grade, нужны: ```text публичный endpoint доставки подписки; форматы под разные клиенты; нормальная авторизация или signed token; TTL / revoke / rotate; rate limit; логирование выдачи; обработка client type; совместимость Clash/Shadowrocket/v2rayN; безопасное сокрытие peer secret; корректные HTTP headers; контроль доступа после disable/expire peer; тесты на каждый формат клиента. ``` Сейчас этого нет. Более того, подписочный URL по старой логике строится вокруг `conPass`, то есть секрет peer’а становится частью URL. Это хуже обычного QR, потому что URL может попасть в browser history, reverse-proxy logs, access logs, Referer, скриншоты и т.д. Обычный Node QR тоже содержит секрет, но он показывается авторизованному админу в панели для конкретного peer’а. Подписка же предполагает публичную доставку конфигурации по ссылке. Это другой threat model. Поэтому чинить подписки сейчас — не “доделать кнопку”, а открывать отдельный блок безопасности и совместимости. Для текущего scope это лишнее. --- ## 29. Что удалить на backend Удалить или пометить deprecated с последующим удалением: ```go controller.Hysteria2SubscribeUrl controller.Hysteria2Subscribe service.Hysteria2SubscribeUrl service.Hysteria2Subscribe dto.Hysteria2SubscribeUrlDto vo.Hysteria2SubscribeVo ``` Из router убрать: ```go hysteria2.GET("/hysteria2SubscribeUrl", controller.Hysteria2SubscribeUrl) ``` Если будешь делать новую REST-модель после разделения `admin_user` / `peer`, оставить только: ```text GET /hui/peers/:id/client-uri GET /hui/peers/:id/client-qr ``` Или один endpoint: ```text GET /hui/peers/:id/client-config ``` Response: ```json { "uri": "hysteria2://...", "qrCode": "base64..." } ``` Важно: этот endpoint должен работать только под admin JWT. Никакой публичной выдачи по `conPass`. --- ## 30. Что удалить на frontend Из `src/views/account/list/index.vue` убрать: ```text Subscribe ``` Из будущего peer drawer убрать: ```text Subscribe Subscribe QR ``` Из бывшего `src/views/info/account/index.vue` это всё всё равно исчезнет вместе с самой страницей `Account Info`. Из `src/api/hysteria2/index.ts` убрать: ```ts hysteria2SubscribeUrlApi ``` Из `src/api/hysteria2/types.ts` убрать: ```ts Hysteria2SubscribeUrlDto Hysteria2SubscribeVo ``` Из i18n убрать или оставить deprecated на один релиз: ```text common.subscribe common.subscribeQrCode config.clashExtension ``` Если `CLASH_EXTENSION` используется только для подписки, убрать его из UI настроек. В базе можно не удалять сразу, чтобы не ломать существующие SQLite-файлы. Лучше сделать миграцию так: ```text релиз N: поле скрыто, backend не использует; релиз N+1: config key удаляется или игнорируется окончательно. ``` --- ## 31. Что оставить вместо подписки В peer list / peer drawer оставить понятные действия: ```text Copy URI Show QR Rotate Secret Edit Reset Traffic Kick Disable Delete ``` В detail drawer можно сделать блок: ```text Connection URI: hysteria2://... [Copy] [Show QR] [Rotate secret] ``` И короткий warning: ```text Rotating secret invalidates existing client configs. ``` Этого достаточно. Оператор создаёт peer, копирует URI или QR, отдаёт клиенту. Без псевдоподписок. --- ## 32. Если когда-нибудь возвращать подписки Возвращать их стоит только как отдельную feature, не как восстановление старого кода. Нормальная модель: ```text subscription_token id peer_id token_digest expires_at revoked_at created_at last_used_at ``` Публичный endpoint: ```text GET /sub/:token ``` Token должен быть random, revocable, не равен peer secret. В URL нельзя класть `conPass`. Формат: ```text GET /sub/:token?target=clash GET /sub/:token?target=shadowrocket GET /sub/:token?target=v2rayn ``` Но я бы сейчас этого не делал. Это увеличит scope, а ценность низкая, если обычные URI/QR уже закрывают основной сценарий. --- ## 33. Acceptance criteria для удаления подписок ```text В UI нигде нет Subscribe / Subscribe QR. В peer list нет действий, ведущих к subscription endpoint. В API клиента нет hysteria2SubscribeUrlApi. Backend router не регистрирует /hysteria2/hysteria2SubscribeUrl. Service-код генерации подписок удалён или явно deprecated и недоступен. Обычный Node URL и Node QR продолжают работать. Админ не получает Node URL/QR для себя, только для peer’ов. В настройках нет Clash subscription extension, если подписки отключены. ``` Итог: да, подписку лучше удалить. Не скрыть кнопку CSS’ом, не оставить endpoint с `Fail`, а именно вырезать из публичного UI/API. Это упростит продукт, уберёт ложные кнопки и снизит риск утечки peer secret через URL.