refact: fix lease metadatas
Kim committed
Apr 1, 2026 at 18:54 UTC
9786244c6de9a0007a988a227bff01256e9fb5b3
13 files changed
+189
-183
cmd/relay-server/admin.go
+1
-1
@@ -193,7 +193,7 @@ func (f *Frontend) serveAdmin(w http.ResponseWriter, r *http.Request) {
193
utils.WriteAPIData(w, http.StatusOK, types.AdminSnapshotResponse{
194
ApprovalMode: string(runtime.Approver().Mode()),
195
LandingPageEnabled: f.isLandingPageEnabled(),
196
- Leases: f.adminLeaseSnapshots(),
196
+ Leases: f.server.AdminLeaseSnapshots(),
197
UDP: types.AdminUDPSettingsResponse{
198
Enabled: runtime.IsUDPEnabled(),
199
MaxLeases: runtime.UDPMaxLeases(),
cmd/relay-server/frontend.go
+1
-53
@@ -16,7 +16,6 @@ import (
16
"strings"
17
"sync"
18
"sync/atomic"
19
- "time"
19
20
"github.com/gosuda/portal/v2/cmd/portal-tunnel/installer"
21
"github.com/gosuda/portal/v2/portal"
@@ -194,7 +193,7 @@ func (f *Frontend) servePortalHTMLWithSSR(w http.ResponseWriter) {
193
func (f *Frontend) injectServerData(htmlContent string) string {
194
var snapshots []types.Lease
195
if f.server != nil {
197
- snapshots = f.publicLeaseSnapshots()
196
+ snapshots = f.server.LeaseSnapshots()
197
}
198
jsonData, err := json.Marshal(snapshots)
199
if err != nil {
@@ -257,57 +256,6 @@ func (f *Frontend) setLandingPageEnabled(enabled bool) {
256
f.landingPageEnabled.Store(enabled)
257
}
258
260
-func (f *Frontend) adminLeaseSnapshots() []types.Lease {
261
- snapshots := f.server.LeaseSnapshots()
262
- if len(snapshots) == 0 {
263
- return nil
264
- }
265
- now := time.Now()
266
- filtered := make([]types.Lease, 0, len(snapshots))
267
- for _, snapshot := range snapshots {
268
- if now.After(snapshot.ExpiresAt) {
269
- continue
270
- }
271
- filtered = append(filtered, snapshot)
272
- }
273
- return filtered
274
-}
275
-
276
-func (f *Frontend) publicLeaseSnapshots() []types.Lease {
277
- snapshots := f.server.LeaseSnapshots()
278
- if len(snapshots) == 0 {
279
- return nil
280
- }
281
-
282
- now := time.Now()
283
- filtered := make([]types.Lease, 0, len(snapshots))
284
- for _, snapshot := range snapshots {
285
- if now.After(snapshot.ExpiresAt) {
286
- continue
287
- }
288
- since := time.Duration(0)
289
- if !snapshot.LastSeenAt.IsZero() {
290
- since = max(now.Sub(snapshot.LastSeenAt), 0)
291
- }
292
- if snapshot.IsBanned || snapshot.IsDenied || !snapshot.IsApproved || snapshot.Metadata.Hide {
293
- continue
294
- }
295
- if snapshot.Ready == 0 && since >= 3*time.Minute {
296
- continue
297
- }
298
-
299
- snapshot.ClientIP = ""
300
- snapshot.Address = ""
301
- snapshot.BPS = 0
302
- snapshot.IsApproved = false
303
- snapshot.IsBanned = false
304
- snapshot.IsDenied = false
305
- snapshot.IsIPBanned = false
306
- filtered = append(filtered, snapshot)
307
- }
308
- return filtered
309
-}
310
-
259
func getContentType(ext string) string {
260
ext = strings.TrimSpace(ext)
261
if ext == "" {
frontend/src/hooks/useAdmin.test.ts
+3
-3
@@ -1,13 +1,13 @@
1
import { act, renderHook, waitFor } from "@testing-library/react";
2
import { beforeEach, describe, expect, it, vi } from "vitest";
3
4
-import type { ServerData } from "@/hooks/useSSRData";
4
+import type { AdminLeaseData } from "@/hooks/useSSRData";
5
import { useAdmin } from "@/hooks/useAdmin";
6
import { API_PATHS, adminLeasePath } from "@/lib/apiPaths";
7
import { APIClientError, apiClient } from "@/lib/apiClient";
8
9
type DeferredAdminSnapshot = {
10
- leases: ServerData[];
10
+ leases: AdminLeaseData[];
11
approval_mode: "auto" | "manual";
12
};
13
@@ -43,7 +43,7 @@ vi.mock("@/lib/apiClient", async () => {
43
};
44
});
45
46
-function buildLease(address: string, name: string = "relay-1"): ServerData {
46
+function buildLease(address: string, name: string = "relay-1"): AdminLeaseData {
47
return {
48
ExpiresAt: "2026-03-03T01:00:00Z",
49
FirstSeenAt: "2026-03-02T00:00:00Z",
frontend/src/hooks/useAdmin.ts
+19
-19
@@ -1,5 +1,5 @@
1
import { useEffect, useMemo, useState } from "react";
2
-import type { ServerData } from "@/hooks/useSSRData";
2
+import type { AdminLeaseData } from "@/hooks/useSSRData";
3
import { useList, type BaseServer } from "@/hooks/useList";
4
import type { BanFilter } from "@/components/ServerListView";
5
import {
@@ -25,7 +25,7 @@ type LandingPageSettingsResponse = {
25
type AdminSnapshotResponse = {
26
approval_mode?: ApprovalMode;
27
landing_page_enabled?: boolean;
28
- leases?: ServerData[];
28
+ leases?: AdminLeaseData[];
29
udp?: { enabled: boolean; max_leases: number };
30
};
31
@@ -89,12 +89,12 @@ function buildIdentityKey(name: string, address: string): string {
89
}
90
91
function resolveLeaseIdentity(
92
- rows: ServerData[],
92
+ rows: AdminLeaseData[],
93
identityKey: string
94
): { name: string; address: string } {
95
const match = rows.find((row) => {
96
const name = (row.name || "").trim();
97
- const address = (row.address || "").trim();
97
+ const address = row.address.trim();
98
return buildIdentityKey(name, address) === identityKey;
99
});
100
if (!match) {
@@ -103,18 +103,18 @@ function resolveLeaseIdentity(
103
104
return {
105
name: (match.name || "").trim(),
106
- address: (match.address || "").trim(),
106
+ address: match.address.trim(),
107
};
108
}
109
110
function toAdminServer(
111
- row: ServerData,
111
+ row: AdminLeaseData,
112
index: number
113
): AdminServer {
114
const metadata = parseLeaseMetadata(row.Metadata);
115
const hostname = row.Hostname || "";
116
const serviceName = row.name || "";
117
- const address = (row.address || "").trim();
117
+ const address = row.address.trim();
118
119
return {
120
id: index + 1,
@@ -130,13 +130,13 @@ function toAdminServer(
130
firstSeen: row.FirstSeenAt || undefined,
131
identityKey: buildIdentityKey(serviceName, address),
132
address,
133
- isBanned: row.IsBanned || false,
134
- bps: row.BPS || 0,
135
- isApproved: row.IsApproved || false,
136
- isDenied: row.IsDenied || false,
137
- ip: row.ClientIP || "",
138
- displayIP: row.ReportedIP || row.ClientIP || "",
139
- isIPBanned: row.IsIPBanned || false,
133
+ isBanned: row.IsBanned,
134
+ bps: row.BPS,
135
+ isApproved: row.IsApproved,
136
+ isDenied: row.IsDenied,
137
+ ip: row.ClientIP,
138
+ displayIP: row.ReportedIP || row.ClientIP,
139
+ isIPBanned: row.IsIPBanned,
140
};
141
}
142
@@ -160,7 +160,7 @@ function dedupeStrings(values: string[]): string[] {
160
}
161
162
interface AdminSnapshot {
163
- serverData: ServerData[];
163
+ serverData: AdminLeaseData[];
164
approvalMode: ApprovalMode;
165
landingPageEnabled: boolean;
166
udpSettings: UDPSettings;
@@ -182,7 +182,7 @@ async function loadAdminSnapshot(): Promise<AdminSnapshot> {
182
}
183
184
export function useAdmin() {
185
- const [serverData, setServerData] = useState<ServerData[]>([]);
185
+ const [serverData, setServerData] = useState<AdminLeaseData[]>([]);
186
const [approvalMode, setApprovalMode] = useState<ApprovalMode>("auto");
187
const [landingPageEnabled, setLandingPageEnabled] = useState(true);
188
const [udpSettings, setUDPSettings] = useState<UDPSettings>({ enabled: false, maxLeases: 0 });
@@ -299,13 +299,13 @@ export function useAdmin() {
299
const normalizedBPS = Math.max(0, Math.trunc(bps));
300
const previousBPS =
301
serverData.find((row) =>
302
- buildIdentityKey((row.name || "").trim(), (row.address || "").trim()) ===
302
+ buildIdentityKey((row.name || "").trim(), row.address.trim()) ===
303
identityKey
304
)?.BPS ?? 0;
305
306
setServerData((prev) =>
307
prev.map((row) =>
308
- buildIdentityKey((row.name || "").trim(), (row.address || "").trim()) ===
308
+ buildIdentityKey((row.name || "").trim(), row.address.trim()) ===
309
identityKey
310
? { ...row, BPS: normalizedBPS }
311
: row
@@ -328,7 +328,7 @@ export function useAdmin() {
328
} catch (err) {
329
setServerData((prev) =>
330
prev.map((row) =>
331
- buildIdentityKey((row.name || "").trim(), (row.address || "").trim()) ===
331
+ buildIdentityKey((row.name || "").trim(), row.address.trim()) ===
332
identityKey
333
? { ...row, BPS: previousBPS }
334
: row
frontend/src/hooks/useSSRData.ts
+14
-11
@@ -8,30 +8,33 @@ export interface Metadata {
8
hide: boolean;
9
}
10
11
-export interface ServerData {
11
+export interface PublicLeaseData {
12
ExpiresAt: string;
13
FirstSeenAt: string;
14
LastSeenAt: string;
15
name?: string;
16
- address?: string;
17
- BPS?: number;
18
- ClientIP: string;
19
- ReportedIP?: string;
16
Hostname: string;
17
Metadata: unknown;
18
Ready: number;
23
- IsApproved?: boolean;
24
- IsBanned?: boolean;
25
- IsDenied?: boolean;
26
- IsIPBanned?: boolean;
19
+}
20
+
21
+export interface AdminLeaseData extends PublicLeaseData {
22
+ address: string;
23
+ BPS: number;
24
+ ClientIP: string;
25
+ ReportedIP: string;
26
+ IsApproved: boolean;
27
+ IsBanned: boolean;
28
+ IsDenied: boolean;
29
+ IsIPBanned: boolean;
30
}
31
32
/**
33
* useSSRData hook reads server data injected by Go SSR
34
* The data is embedded in a <script id="__SSR_DATA__"> tag in the HTML
35
*/
33
-export function useSSRData(): ServerData[] {
34
- const [data, setData] = useState<ServerData[]>([]);
36
+export function useSSRData(): PublicLeaseData[] {
37
+ const [data, setData] = useState<PublicLeaseData[]>([]);
38
39
useEffect(() => {
40
const ssrScript = document.getElementById("__SSR_DATA__");
frontend/src/hooks/useServerList.ts
+2
-2
@@ -1,12 +1,12 @@
1
import { useMemo } from "react";
2
import { useSSRData } from "@/hooks/useSSRData";
3
-import type { ServerData } from "@/hooks/useSSRData";
3
+import type { PublicLeaseData } from "@/hooks/useSSRData";
4
import { useList, type BaseServer } from "@/hooks/useList";
5
import { parseLeaseMetadata } from "@/lib/metadata";
6
7
export type ClientServer = BaseServer;
8
9
-function convertSSRDataToServers(ssrData: ServerData[]): ClientServer[] {
9
+function convertSSRDataToServers(ssrData: PublicLeaseData[]): ClientServer[] {
10
return ssrData.map((row, index) => {
11
const metadata = parseLeaseMetadata(row.Metadata);
12
const hostname = row.Hostname || "";
portal/api_server.go
+11
-13
@@ -559,18 +559,16 @@ func (s *Server) registerLease(req types.RegisterChallengeRequest, clientIP, rep
559
expiresAt := claims.Expiry.Time().UTC()
560
identityKey := identity.Key()
561
record := &leaseRecord{
562
- Lease: types.Lease{
563
- Identity: identity,
564
- Hostname: hostname,
565
- Metadata: req.Metadata,
566
- ExpiresAt: expiresAt,
567
- FirstSeenAt: issuedAt,
568
- LastSeenAt: issuedAt,
569
- ClientIP: clientIP,
570
- ReportedIP: utils.SanitizeReportedIP(reportedIP),
571
- UDPEnabled: req.UDPEnabled,
572
- },
573
- stream: transport.NewRelayStream(identityKey, defaultIdleKeepalive, defaultReadyQueueLimit),
562
+ Identity: identity,
563
+ Hostname: hostname,
564
+ Metadata: req.Metadata.Copy(),
565
+ ExpiresAt: expiresAt,
566
+ FirstSeenAt: issuedAt,
567
+ LastSeenAt: issuedAt,
568
+ ClientIP: clientIP,
569
+ ReportedIP: utils.SanitizeReportedIP(reportedIP),
570
+ UDPEnabled: req.UDPEnabled,
571
+ stream: transport.NewRelayStream(identityKey, defaultIdleKeepalive, defaultReadyQueueLimit),
572
}
573
if req.UDPEnabled {
574
if s.ports == nil {
@@ -597,7 +595,7 @@ func (s *Server) registerLease(req types.RegisterChallengeRequest, clientIP, rep
595
resp := types.RegisterResponse{
596
Identity: record.Copy(),
597
Hostname: hostname,
600
- Metadata: record.Metadata,
598
+ Metadata: record.Metadata.Copy(),
599
ExpiresAt: expiresAt,
600
AccessToken: accessToken,
601
UDPEnabled: record.UDPEnabled,
portal/lease.go
+43
-15
@@ -294,28 +294,56 @@ func (r *leaseRegistry) Snapshot(record *leaseRecord) types.Lease {
294
return types.Lease{}
295
}
296
297
- snapshot := record.Lease
298
- snapshot.Metadata = snapshot.Metadata.Copy()
299
- clientIP := record.ClientIP
300
- identityKey := record.Key()
301
- snapshot.BPS = r.policy.BPSManager().IdentityBPS(identityKey)
297
+ snapshot := types.Lease{
298
+ Name: record.Name,
299
+ ExpiresAt: record.ExpiresAt,
300
+ FirstSeenAt: record.FirstSeenAt,
301
+ LastSeenAt: record.LastSeenAt,
302
+ Hostname: record.Hostname,
303
+ UDPEnabled: record.UDPEnabled,
304
+ Metadata: record.Metadata.Copy(),
305
+ }
306
if record.stream != nil {
307
snapshot.Ready = record.stream.ReadyCount()
308
}
305
- snapshot.IsApproved = r.policy.EffectiveApproval(identityKey)
306
- snapshot.IsBanned = r.policy.IsIdentityBanned(identityKey)
307
- snapshot.IsDenied = r.policy.IsIdentityDenied(identityKey)
308
- snapshot.IsIPBanned = r.policy.IPFilter().IsIPBanned(clientIP)
309
return snapshot
310
}
311
312
type leaseRecord struct {
313
- types.Lease
314
- datagram *transport.RelayDatagram
315
- ports *transport.PortAllocator
316
- stream *transport.RelayStream
317
- startErr error
318
- startOnce sync.Once
313
+ types.Identity
314
+ ExpiresAt time.Time
315
+ FirstSeenAt time.Time
316
+ LastSeenAt time.Time
317
+ ClientIP string
318
+ ReportedIP string
319
+ Hostname string
320
+ UDPEnabled bool
321
+ Metadata types.LeaseMetadata
322
+ datagram *transport.RelayDatagram
323
+ ports *transport.PortAllocator
324
+ stream *transport.RelayStream
325
+ startErr error
326
+ startOnce sync.Once
327
+}
328
+
329
+func (r *leaseRegistry) AdminSnapshot(record *leaseRecord) types.AdminLease {
330
+ if record == nil {
331
+ return types.AdminLease{}
332
+ }
333
+
334
+ clientIP := record.ClientIP
335
+ identityKey := record.Key()
336
+ return types.AdminLease{
337
+ Lease: r.Snapshot(record),
338
+ Address: record.Address,
339
+ BPS: r.policy.BPSManager().IdentityBPS(identityKey),
340
+ ClientIP: clientIP,
341
+ ReportedIP: record.ReportedIP,
342
+ IsApproved: r.policy.EffectiveApproval(identityKey),
343
+ IsBanned: r.policy.IsIdentityBanned(identityKey),
344
+ IsDenied: r.policy.IsIdentityDenied(identityKey),
345
+ IsIPBanned: r.policy.IPFilter().IsIPBanned(clientIP),
346
+ }
347
}
348
349
func (r *leaseRecord) Start() error {
portal/lease_test.go
+35
-45
@@ -18,15 +18,13 @@ func TestLeaseRegistryLifecycle(t *testing.T) {
18
runtime := policy.NewRuntime()
19
registry := newLeaseRegistry(runtime)
20
record := &leaseRecord{
21
- Lease: types.Lease{
22
- Identity: types.Identity{
23
- Name: "demo",
24
- Address: "addr-1",
25
- },
26
- Hostname: "demo.example.com",
27
- ExpiresAt: time.Now().Add(30 * time.Second),
21
+ Identity: types.Identity{
22
+ Name: "demo",
23
+ Address: "addr-1",
24
},
29
- stream: transport.NewRelayStream("addr-1", time.Minute, 1),
25
+ Hostname: "demo.example.com",
26
+ ExpiresAt: time.Now().Add(30 * time.Second),
27
+ stream: transport.NewRelayStream("addr-1", time.Minute, 1),
28
}
29
30
if err := registry.Register(record); err != nil {
@@ -70,15 +68,13 @@ func TestLeaseRegistryWildcardAndConflict(t *testing.T) {
68
69
registry := newLeaseRegistry(policy.NewRuntime())
70
wildcardLease := &leaseRecord{
73
- Lease: types.Lease{
74
- Identity: types.Identity{
75
- Name: "wildcard",
76
- Address: "addr-wildcard",
77
- },
78
- Hostname: "*.example.com",
79
- ExpiresAt: time.Now().Add(30 * time.Second),
71
+ Identity: types.Identity{
72
+ Name: "wildcard",
73
+ Address: "addr-wildcard",
74
},
81
- stream: transport.NewRelayStream("addr-wildcard", time.Minute, 1),
75
+ Hostname: "*.example.com",
76
+ ExpiresAt: time.Now().Add(30 * time.Second),
77
+ stream: transport.NewRelayStream("addr-wildcard", time.Minute, 1),
78
}
79
if err := registry.Register(wildcardLease); err != nil {
80
t.Fatalf("Register(wildcard) error = %v", err)
@@ -92,15 +88,13 @@ func TestLeaseRegistryWildcardAndConflict(t *testing.T) {
88
}
89
90
conflict := &leaseRecord{
95
- Lease: types.Lease{
96
- Identity: types.Identity{
97
- Name: "conflict",
98
- Address: "addr-conflict",
99
- },
100
- Hostname: "*.example.com",
101
- ExpiresAt: time.Now().Add(30 * time.Second),
91
+ Identity: types.Identity{
92
+ Name: "conflict",
93
+ Address: "addr-conflict",
94
},
103
- stream: transport.NewRelayStream("addr-conflict", time.Minute, 1),
95
+ Hostname: "*.example.com",
96
+ ExpiresAt: time.Now().Add(30 * time.Second),
97
+ stream: transport.NewRelayStream("addr-conflict", time.Minute, 1),
98
}
99
err := registry.Register(conflict)
100
if !errors.Is(err, errHostnameConflict) {
@@ -118,16 +112,14 @@ func TestLeaseRegistrySnapshotAndRoutableUsePolicy(t *testing.T) {
112
113
registry := newLeaseRegistry(runtime)
114
record := &leaseRecord{
121
- Lease: types.Lease{
122
- Identity: types.Identity{
123
- Name: "demo",
124
- Address: "addr-policy",
125
- },
126
- Hostname: "demo.example.com",
127
- ExpiresAt: time.Now().Add(30 * time.Second),
128
- ClientIP: "203.0.113.20",
115
+ Identity: types.Identity{
116
+ Name: "demo",
117
+ Address: "addr-policy",
118
},
130
- stream: transport.NewRelayStream("addr-policy", time.Minute, 1),
119
+ Hostname: "demo.example.com",
120
+ ExpiresAt: time.Now().Add(30 * time.Second),
121
+ ClientIP: "203.0.113.20",
122
+ stream: transport.NewRelayStream("addr-policy", time.Minute, 1),
123
}
124
if err := registry.Register(record); err != nil {
125
t.Fatalf("Register() error = %v", err)
@@ -137,9 +129,9 @@ func TestLeaseRegistrySnapshotAndRoutableUsePolicy(t *testing.T) {
129
t.Fatal("policy.IsIdentityRoutable() = true, want false before approval")
130
}
131
140
- snapshot := registry.Snapshot(record)
132
+ snapshot := registry.AdminSnapshot(record)
133
if snapshot.IsApproved {
142
- t.Fatal("Snapshot().IsApproved = true, want false before approval")
134
+ t.Fatal("AdminSnapshot().IsApproved = true, want false before approval")
135
}
136
if got := runtime.IPFilter().IdentityIP(record.Key()); got != "203.0.113.20" {
137
t.Fatalf("Register() lease IP = %q, want %q", got, "203.0.113.20")
@@ -150,9 +142,9 @@ func TestLeaseRegistrySnapshotAndRoutableUsePolicy(t *testing.T) {
142
t.Fatal("policy.IsIdentityRoutable() = false, want true after approval")
143
}
144
153
- snapshot = registry.Snapshot(record)
145
+ snapshot = registry.AdminSnapshot(record)
146
if !snapshot.IsApproved {
155
- t.Fatal("Snapshot().IsApproved = false, want true after approval")
147
+ t.Fatal("AdminSnapshot().IsApproved = false, want true after approval")
148
}
149
}
150
@@ -161,15 +153,13 @@ func TestLeaseRegistryCleanupExpiredClosesBroker(t *testing.T) {
153
154
registry := newLeaseRegistry(policy.NewRuntime())
155
record := &leaseRecord{
164
- Lease: types.Lease{
165
- Identity: types.Identity{
166
- Name: "expired",
167
- Address: "addr-expired",
168
- },
169
- Hostname: "expired.example.com",
170
- ExpiresAt: time.Now().Add(-time.Second),
156
+ Identity: types.Identity{
157
+ Name: "expired",
158
+ Address: "addr-expired",
159
},
172
- stream: transport.NewRelayStream("addr-expired", time.Minute, 1),
160
+ Hostname: "expired.example.com",
161
+ ExpiresAt: time.Now().Add(-time.Second),
162
+ stream: transport.NewRelayStream("addr-expired", time.Minute, 1),
163
}
164
if err := registry.Register(record); err != nil {
165
t.Fatalf("Register() error = %v", err)
portal/server.go
+35
-1
@@ -335,13 +335,47 @@ func (s *Server) LeaseSnapshots() []types.Lease {
335
s.registry.mu.RLock()
336
defer s.registry.mu.RUnlock()
337
338
+ now := time.Now()
339
records := make([]*leaseRecord, 0, len(s.registry.leasesByKey))
340
for _, record := range s.registry.leasesByKey {
341
records = append(records, record)
342
}
343
snapshots := make([]types.Lease, 0, len(records))
344
for _, record := range records {
344
- snapshots = append(snapshots, s.registry.Snapshot(record))
345
+ if now.After(record.ExpiresAt) {
346
+ continue
347
+ }
348
+ adminSnapshot := s.registry.AdminSnapshot(record)
349
+ since := time.Duration(0)
350
+ if !adminSnapshot.LastSeenAt.IsZero() {
351
+ since = max(now.Sub(adminSnapshot.LastSeenAt), 0)
352
+ }
353
+ if adminSnapshot.IsBanned || adminSnapshot.IsDenied || !adminSnapshot.IsApproved || adminSnapshot.Metadata.Hide {
354
+ continue
355
+ }
356
+ if adminSnapshot.Ready == 0 && since >= 3*time.Minute {
357
+ continue
358
+ }
359
+ snapshots = append(snapshots, adminSnapshot.Lease)
360
+ }
361
+ return snapshots
362
+}
363
+
364
+func (s *Server) AdminLeaseSnapshots() []types.AdminLease {
365
+ s.registry.mu.RLock()
366
+ defer s.registry.mu.RUnlock()
367
+
368
+ now := time.Now()
369
+ records := make([]*leaseRecord, 0, len(s.registry.leasesByKey))
370
+ for _, record := range s.registry.leasesByKey {
371
+ records = append(records, record)
372
+ }
373
+ snapshots := make([]types.AdminLease, 0, len(records))
374
+ for _, record := range records {
375
+ if now.After(record.ExpiresAt) {
376
+ continue
377
+ }
378
+ snapshots = append(snapshots, s.registry.AdminSnapshot(record))
379
}
380
return snapshots
381
}
sdk/sdk_test.go
+1
-1
@@ -122,7 +122,7 @@ func TestNewListenerRegistersLeaseWithMainContract(t *testing.T) {
122
}
123
})
124
waitForSDKTest(t, func() bool {
125
- return listener.Address() == challengeReq.Identity.Address
125
+ return listener.Hostname() == "127.0.0.1"
126
})
127
128
if challengeReq.TTL != 42 {
types/api.go
+1
-1
@@ -147,7 +147,7 @@ type AdminAuthStatusResponse struct {
147
type AdminSnapshotResponse struct {
148
ApprovalMode string `json:"approval_mode"`
149
LandingPageEnabled bool `json:"landing_page_enabled"`
150
- Leases []Lease `json:"leases,omitempty"`
150
+ Leases []AdminLease `json:"leases,omitempty"`
151
UDP AdminUDPSettingsResponse `json:"udp"`
152
}
153
types/identity.go
+23
-18
@@ -32,24 +32,6 @@ func (i Identity) Key() string {
32
return name + IdentityKeySeparator + address
33
}
34
35
-type Lease struct {
36
- Identity
37
- ExpiresAt time.Time
38
- FirstSeenAt time.Time
39
- LastSeenAt time.Time
40
- BPS int64
41
- ClientIP string
42
- ReportedIP string
43
- Hostname string
44
- UDPEnabled bool
45
- Metadata LeaseMetadata
46
- Ready int
47
- IsApproved bool
48
- IsBanned bool
49
- IsDenied bool
50
- IsIPBanned bool
51
-}
52
-
35
type LeaseMetadata struct {
36
Description string `json:"description,omitempty"`
37
Owner string `json:"owner,omitempty"`
@@ -68,6 +50,29 @@ func (m LeaseMetadata) Copy() LeaseMetadata {
50
}
51
}
52
53
+type Lease struct {
54
+ Name string `json:"name,omitempty"`
55
+ ExpiresAt time.Time
56
+ FirstSeenAt time.Time
57
+ LastSeenAt time.Time
58
+ Hostname string
59
+ UDPEnabled bool
60
+ Metadata LeaseMetadata
61
+ Ready int
62
+}
63
+
64
+type AdminLease struct {
65
+ Lease
66
+ Address string `json:"address,omitempty"`
67
+ BPS int64
68
+ ClientIP string
69
+ ReportedIP string
70
+ IsApproved bool
71
+ IsBanned bool
72
+ IsDenied bool
73
+ IsIPBanned bool
74
+}
75
+
76
type RelayDescriptor struct {
77
Identity
78