feat(sub): expose live online status and add ?format=info endpoint

Custom subscription templates only received the lastOnline timestamp, so
template authors had to fake an online indicator by comparing it against
the current time, and the page was a one-shot server render with no way
to refresh usage without reloading the whole HTML.

The template context (and window.__SUB_PAGE_DATA__) now carries isOnline,
computed from the panel's own online-client tracking (local xray plus
remote nodes) at render time. The subscription URL also answers
?format=info with the page view-model as JSON — minus the links, with
emails deduplicated — so templates can poll live status cheaply. The
shared view-model construction moved into buildSubPageData/subPageContext
so the HTML page, the SPA payload and the info JSON cannot drift apart.

Also documents the previously injected but undocumented announce
template variable.
This commit is contained in:
Sanaei
2026-07-23 23:57:29 +02:00
parent b319dd0c3a
commit cd674c8d4f
7 changed files with 312 additions and 38 deletions
+18
View File
@@ -2454,6 +2454,7 @@ type PageData struct {
BasePath string
SId string
Enabled bool
IsOnline bool
Download string
Upload string
Total string
@@ -2618,6 +2619,7 @@ func (s *SubService) BuildPageData(subId string, hostHeader string, traffic xray
BasePath: basePath,
SId: subId,
Enabled: traffic.Enable,
IsOnline: subIsOnline(emails, s.inboundService.GetOnlineClients()),
Download: download,
Upload: upload,
Total: total,
@@ -2639,6 +2641,22 @@ func (s *SubService) BuildPageData(subId string, hostHeader string, traffic xray
}
}
func subIsOnline(subEmails, onlineEmails []string) bool {
if len(subEmails) == 0 || len(onlineEmails) == 0 {
return false
}
onlineSet := make(map[string]struct{}, len(onlineEmails))
for _, email := range onlineEmails {
onlineSet[email] = struct{}{}
}
for _, email := range subEmails {
if _, online := onlineSet[email]; online {
return true
}
}
return false
}
func getHostFromXFH(s string) (string, error) {
if strings.Contains(s, ":") {
realHost, _, err := net.SplitHostPort(s)