Категория: feature · Приоритет: high · Размер: L · Ранг: 2/10
Что построить
Поле «Срок действия» в форме пользователя: режимы «Без срока» / «До даты» (date-picker) / «N дней от первого подключения» (число + чекбокс «активировать при первом подключении»); бейдж «истекает через N дней» в строке (жёлтый/красный у порога) и бейдж «истёк». Фоновый воркер раз в интервал отключает истёкших.
Паритет с конкурентами
Marzban (unix expire), Marzneshin (expire_strategy: fixed_date / start_on_first_use / never), 3x-ui (expiryTime, режим активации при первом подключении), Remnawave (статус expired), Hiddify (package days).
Чего не хватает сейчас (код)
fleet/build.go уже читает cs.Expiry, а sub/handler.go:139 отдаёт expire в Subscription-Userinfo, но это read-only зеркало панели — subgen не задаёт expiryTime (add_client.go шлёт expiryTime:0), не показывает дату/остаток дней и не отключает истёкших. Планировщика/cron в кодовой базе нет.
Зачем
Операторы продают планы с ограничением по времени (30/90/365 дней); «активация при первом подключении» критична для предоплаченных ваучеров — часы не идут, пока код не активирован. Subgen сегодня не умеет ограничивать доступ по времени вообще.
Предлагаемая реализация
ClientSpec.ExpiryTime + убрать хардкод; колонка expiry_ms в users (отрицательная для режима «от первого использования», как у 3x-ui) + поля в Create/EditParams. Новый internal/service/reconciler (горутина-тикер в cmd/service/main.go): читает флит, сравнивает Expiry с now, переводит истёкших в disabled (через флаг #89, re-bind без удаления uuid/subId). Режим first-use — флип отрицательной длительности в дату при первом успешном /sub (хендлер уже резолвит юзера по токену). Остаток дней — в users_get, бейдж во Vue.
Затронутые файлы
internal/entity/panel.go (ClientSpec.ExpiryTime), internal/entity/user.go
internal/clients/xui/add_client.go, update_client.go (новый) + тест
internal/service/provisioning/service.go, internal/service/reconciler/ (новый), cmd/service/main.go
migrations/NNNN-user-expiry.sql, openapi/{user_create,user_edit}.yaml
internal/handlers/users_get/, internal/handlers/sub/handler.go (first-use), internal/handlers/web/static/
Продуктовый ресёрч: изучены аналоги (Marzban/Marzneshin, 3x-ui, Remnawave, Hiddify, sub-store + клиентские стандарты импорта). Паритет и subgen-gap код-обоснованы. Ранг = продуктовый приоритет.
Категория: feature · Приоритет: high · Размер: L · Ранг: 2/10
Что построить
Поле «Срок действия» в форме пользователя: режимы «Без срока» / «До даты» (date-picker) / «N дней от первого подключения» (число + чекбокс «активировать при первом подключении»); бейдж «истекает через N дней» в строке (жёлтый/красный у порога) и бейдж «истёк». Фоновый воркер раз в интервал отключает истёкших.
Паритет с конкурентами
Marzban (unix expire), Marzneshin (
expire_strategy: fixed_date / start_on_first_use / never), 3x-ui (expiryTime, режим активации при первом подключении), Remnawave (статусexpired), Hiddify (package days).Чего не хватает сейчас (код)
fleet/build.goуже читаетcs.Expiry, аsub/handler.go:139отдаётexpireвSubscription-Userinfo, но это read-only зеркало панели — subgen не задаётexpiryTime(add_client.goшлётexpiryTime:0), не показывает дату/остаток дней и не отключает истёкших. Планировщика/cron в кодовой базе нет.Зачем
Операторы продают планы с ограничением по времени (30/90/365 дней); «активация при первом подключении» критична для предоплаченных ваучеров — часы не идут, пока код не активирован. Subgen сегодня не умеет ограничивать доступ по времени вообще.
Предлагаемая реализация
ClientSpec.ExpiryTime+ убрать хардкод; колонкаexpiry_msвusers(отрицательная для режима «от первого использования», как у 3x-ui) + поля в Create/EditParams. Новыйinternal/service/reconciler(горутина-тикер вcmd/service/main.go): читает флит, сравниваетExpiryс now, переводит истёкших в disabled (через флаг #89, re-bind без удаления uuid/subId). Режим first-use — флип отрицательной длительности в дату при первом успешном/sub(хендлер уже резолвит юзера по токену). Остаток дней — вusers_get, бейдж во Vue.Затронутые файлы
internal/entity/panel.go(ClientSpec.ExpiryTime),internal/entity/user.gointernal/clients/xui/add_client.go,update_client.go(новый) + тестinternal/service/provisioning/service.go,internal/service/reconciler/(новый),cmd/service/main.gomigrations/NNNN-user-expiry.sql,openapi/{user_create,user_edit}.yamlinternal/handlers/users_get/,internal/handlers/sub/handler.go(first-use),internal/handlers/web/static/Продуктовый ресёрч: изучены аналоги (Marzban/Marzneshin, 3x-ui, Remnawave, Hiddify, sub-store + клиентские стандарты импорта). Паритет и subgen-gap код-обоснованы. Ранг = продуктовый приоритет.