Да, я бы убирал подписки полностью. В текущем состоянии это не недоделанная мелочь, а мёртвый функциональный слой, который создаёт ложное ожидание у оператора и путает 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.
