feat(sub): expose last subscription fetch time (#6217)

* feat(sub): expose last subscription fetch time

Record successful GET subscription fetches per client and surface the timestamp in the client API and UI.

* fix(frontend): include last subscription fetch in client traffic

* Update sub_fetch_test.go

---------

Co-authored-by: Hermes Agent <hermes-agent@localhost>
This commit is contained in:
Maxim Myalin
2026-08-15 16:22:11 +03:00
committed by GitHub
parent ad32144c42
commit 2217213e9f
26 changed files with 187 additions and 14 deletions
+8
View File
@@ -1324,6 +1324,11 @@
"format": "int64",
"type": "integer"
},
"lastSubFetch": {
"example": 1735680000000,
"format": "int64",
"type": "integer"
},
"reset": {
"example": 0,
"type": "integer"
@@ -1355,6 +1360,7 @@
"id",
"inboundId",
"lastOnline",
"lastSubFetch",
"reset",
"subId",
"total",
@@ -3139,6 +3145,7 @@
"id": 14825,
"inboundId": 1,
"lastOnline": 1735680000000,
"lastSubFetch": 1735680000000,
"reset": 0,
"subId": "i7tvdpeffi0hvvf1",
"total": 10737418240,
@@ -7786,6 +7793,7 @@
"id": 14825,
"inboundId": 1,
"lastOnline": 1735680000000,
"lastSubFetch": 1735680000000,
"reset": 0,
"subId": "i7tvdpeffi0hvvf1",
"total": 10737418240,
+2
View File
@@ -305,6 +305,7 @@ export const EXAMPLES: Record<string, unknown> = {
"id": 14825,
"inboundId": 1,
"lastOnline": 1735680000000,
"lastSubFetch": 1735680000000,
"reset": 0,
"subId": "i7tvdpeffi0hvvf1",
"total": 10737418240,
@@ -422,6 +423,7 @@ export const EXAMPLES: Record<string, unknown> = {
"id": 14825,
"inboundId": 1,
"lastOnline": 1735680000000,
"lastSubFetch": 1735680000000,
"reset": 0,
"subId": "i7tvdpeffi0hvvf1",
"total": 10737418240,
+6
View File
@@ -1298,6 +1298,11 @@ export const SCHEMAS: Record<string, unknown> = {
"format": "int64",
"type": "integer"
},
"lastSubFetch": {
"example": 1735680000000,
"format": "int64",
"type": "integer"
},
"reset": {
"example": 0,
"type": "integer"
@@ -1329,6 +1334,7 @@ export const SCHEMAS: Record<string, unknown> = {
"id",
"inboundId",
"lastOnline",
"lastSubFetch",
"reset",
"subId",
"total",
+1
View File
@@ -318,6 +318,7 @@ export interface ClientTraffic {
id: number;
inboundId: number;
lastOnline: number;
lastSubFetch: number;
reset: number;
subId: string;
total: number;
+1
View File
@@ -340,6 +340,7 @@ export const ClientTrafficSchema = z.object({
id: z.number().int(),
inboundId: z.number().int(),
lastOnline: z.number().int(),
lastSubFetch: z.number().int(),
reset: z.number().int(),
subId: z.string(),
total: z.number().int(),
@@ -218,7 +218,10 @@ export default function ClientInfoModal({
{client.enable && isOnline
? <Tag color="green">{t('pages.clients.online')}</Tag>
: <Tag>{t('pages.clients.offline')}</Tag>}
<span className="hint">{t('lastOnline')}: {dateLabel(traffic?.lastOnline)}</span>
<span className="hint">
{t('lastOnline')}: {dateLabel(traffic?.lastOnline)}
{' · '}{t('lastSubFetch')}: {dateLabel(traffic?.lastSubFetch)}
</span>
</td>
</tr>
<tr>
+2 -1
View File
@@ -852,7 +852,8 @@ export default function ClientsPage() {
render: (_v, record) => {
const bucket = clientBucket(record);
const lastOnline = record.traffic?.lastOnline ?? 0;
const lastOnlineTitle = `${t('lastOnline')}: ${lastOnline > 0 ? IntlUtil.formatDate(lastOnline, datepicker) : '-'}`;
const lastSubFetch = record.traffic?.lastSubFetch ?? 0;
const lastOnlineTitle = `${t('lastOnline')}: ${lastOnline > 0 ? IntlUtil.formatDate(lastOnline, datepicker) : '-'}\n${t('lastSubFetch')}: ${lastSubFetch > 0 ? IntlUtil.formatDate(lastSubFetch, datepicker) : '-'}`;
if (bucket === 'depleted') return (
<Tooltip title={lastOnlineTitle}>
<Tag color="red">{t('depleted')}</Tag>
+1
View File
@@ -10,6 +10,7 @@ export const ClientTrafficSchema = z.object({
expiryTime: z.number().optional(),
enable: z.boolean().optional(),
lastOnline: z.number().optional(),
lastSubFetch: z.number().optional(),
});
export const ClientRecordSchema = z.object({