mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-16 15:17:14 +00:00
feat(sub): add opt-in month-end expiry presentation (#6517)
Offer monthly calendar subscriptions an explicit last-valid-second display without moving their real billing boundary or spending renewal allowances. Keep the option off by default and limit conversion to a shared fixed day-1 midnight cutoff at an actual month transition in the panel timezone. Use the authoritative client calendar mode when aggregating node traffic, and share the header formatter across raw, JSON, and Clash exports. Expose the setting in the existing settings API/UI, regenerate its schemas, and document that clients may report expiry one second early or format the date differently in another timezone. Add HTTP, settings, and DST coverage. Stored deadlines, access enforcement, info/remark expiry values, and renewal accounting remain unchanged. Refs: #6516 Co-authored-by: JacktheRanger <219502738+JacktheRanger@users.noreply.github.com>
This commit is contained in:
@@ -94,6 +94,31 @@ Subscriptions return standard headers that compatible apps read:
|
||||
- **`Profile-Title`**, **`Support-Url`**, **`Profile-Web-Page-Url`**,
|
||||
**`Announce`** — optional branding shown by some clients.
|
||||
|
||||
### Optional month-end expiry display
|
||||
|
||||
Under **Subscription → Information**, **Month-end subscription expiry display**
|
||||
(`subCalendarExpireInclusive`, default `false`) reports the last valid second
|
||||
of the month in `Subscription-Userinfo` instead of the next month's midnight.
|
||||
It applies only when every client contributing to the subscription has calendar
|
||||
renewal day `1`, shares the same fixed expiry, and that expiry is exactly day `1`
|
||||
at `00:00:00` in the configured panel timezone, immediately after the previous
|
||||
month's last second. A later repeated midnight during a DST rollback is not
|
||||
converted. Raw, JSON, Mihomo, and legacy
|
||||
Clash subscriptions use the same conversion.
|
||||
|
||||
For example, the real cutoff `2030-10-01 00:00:00` is presented as
|
||||
`2030-09-30 23:59:59`. The stored expiry, access cutoff, traffic accounting,
|
||||
renewal schedule, remark expiry variables, and HTML/JSON info-page cutoff stay
|
||||
unchanged. Arbitrary times, other renewal days, interval renewal, first-use
|
||||
durations, unlimited expiries, mixed renewal modes, and different cutoffs are
|
||||
not converted.
|
||||
|
||||
This is an opt-in compatibility tradeoff, not a change to expiry semantics by
|
||||
default: apps receive a timestamp one second before the real cutoff and may
|
||||
consider the subscription expired one second early. Apps format it in their own
|
||||
timezone; matching the panel timezone is needed to display the same month-end
|
||||
date. Cached subscription information changes only after the app refreshes it.
|
||||
|
||||
## Custom page templates
|
||||
|
||||
Point `subThemeDir` at a folder containing a custom info-page template to brand
|
||||
|
||||
Reference in New Issue
Block a user