@privateclaw/privateclaw-relay 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -0
- package/README.md +20 -0
- package/dist/admin-api.d.ts +11 -0
- package/dist/admin-api.js +111 -0
- package/dist/admin-api.js.map +1 -0
- package/dist/admin-metrics-store.d.ts +245 -0
- package/dist/admin-metrics-store.js +952 -0
- package/dist/admin-metrics-store.js.map +1 -0
- package/dist/admin-web/admin.css +379 -0
- package/dist/admin-web/admin.js +366 -0
- package/dist/admin-web/index.html +145 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/provider-setup.js +0 -12
- package/dist/provider-setup.js.map +1 -1
- package/dist/relay-server.d.ts +2 -0
- package/dist/relay-server.js +206 -12
- package/dist/relay-server.js.map +1 -1
- package/dist/relay-web.d.ts +2 -0
- package/dist/relay-web.js +35 -6
- package/dist/relay-web.js.map +1 -1
- package/package.json +1 -1
package/.env.example
CHANGED
|
@@ -5,6 +5,8 @@ PRIVATECLAW_FRAME_CACHE_SIZE=25
|
|
|
5
5
|
# PRIVATECLAW_MAX_MESSAGE_BYTES=25165824
|
|
6
6
|
# PRIVATECLAW_APP_MESSAGES_PER_MINUTE=120
|
|
7
7
|
# PRIVATECLAW_PROVIDER_MESSAGES_PER_MINUTE=600
|
|
8
|
+
# Optional: enable the built-in relay admin UI/API at /admin and /api/admin/*
|
|
9
|
+
# PRIVATECLAW_ADMIN_TOKEN=replace-with-a-long-random-token
|
|
8
10
|
# Optional: enable background wake through Firebase Cloud Messaging.
|
|
9
11
|
# Use either the full service-account JSON...
|
|
10
12
|
# PRIVATECLAW_FCM_SERVICE_ACCOUNT_JSON={"type":"service_account","project_id":"your-project","client_email":"...","private_key":"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"}
|
package/README.md
CHANGED
|
@@ -44,6 +44,25 @@ privateclaw-relay --host 0.0.0.0 --port 8787
|
|
|
44
44
|
privateclaw-relay --redis-url redis://127.0.0.1:6379
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
Enable the built-in relay admin UI/API with a fixed bearer token:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
export PRIVATECLAW_ADMIN_TOKEN=replace-with-a-long-random-token
|
|
51
|
+
privateclaw-relay --redis-url redis://127.0.0.1:6379
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
When `PRIVATECLAW_ADMIN_TOKEN` is configured, the relay serves a simple admin dashboard at `/admin/` and protects the JSON APIs under `/api/admin/*` with `Authorization: Bearer <token>`.
|
|
55
|
+
|
|
56
|
+
The admin dashboard shows:
|
|
57
|
+
|
|
58
|
+
- current active sessions and participants
|
|
59
|
+
- session history and per-session details
|
|
60
|
+
- participant online time and message counts
|
|
61
|
+
- relay request/error statistics
|
|
62
|
+
- live relay instance heartbeats
|
|
63
|
+
|
|
64
|
+
For multi-instance deployments, point every relay instance at the same Redis so the admin view can aggregate shared session history and instance status.
|
|
65
|
+
|
|
47
66
|
Expose the local relay to the internet through Tailscale Funnel:
|
|
48
67
|
|
|
49
68
|
```bash
|
|
@@ -94,6 +113,7 @@ The relay still reads its runtime config from the process environment:
|
|
|
94
113
|
- `PRIVATECLAW_RELAY_INSTANCE_ID`
|
|
95
114
|
- `PRIVATECLAW_REDIS_URL`
|
|
96
115
|
- `REDIS_URL`
|
|
116
|
+
- `PRIVATECLAW_ADMIN_TOKEN`
|
|
97
117
|
- `PRIVATECLAW_FCM_SERVICE_ACCOUNT_JSON`
|
|
98
118
|
- `PRIVATECLAW_FCM_PROJECT_ID`
|
|
99
119
|
- `PRIVATECLAW_FCM_CLIENT_EMAIL`
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import type { RelayAdminMetricsStore } from "./admin-metrics-store.js";
|
|
3
|
+
export interface HandleRelayAdminRequestOptions {
|
|
4
|
+
request: IncomingMessage;
|
|
5
|
+
response: ServerResponse;
|
|
6
|
+
url: URL;
|
|
7
|
+
adminToken?: string;
|
|
8
|
+
metricsStore: RelayAdminMetricsStore;
|
|
9
|
+
now?: () => number;
|
|
10
|
+
}
|
|
11
|
+
export declare function handleRelayAdminRequest(options: HandleRelayAdminRequestOptions): Promise<boolean>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
function writeJson(response, statusCode, payload) {
|
|
2
|
+
response.writeHead(statusCode, { "content-type": "application/json" });
|
|
3
|
+
response.end(JSON.stringify(payload));
|
|
4
|
+
}
|
|
5
|
+
function normalizeBearerToken(headerValue) {
|
|
6
|
+
if (!headerValue) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const match = headerValue.match(/^Bearer\s+(.+)$/i);
|
|
10
|
+
if (!match) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
return match[1]?.trim() || undefined;
|
|
14
|
+
}
|
|
15
|
+
function isAuthorized(request, expectedToken) {
|
|
16
|
+
const headerValue = request.headers.authorization;
|
|
17
|
+
if (typeof headerValue !== "string") {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return normalizeBearerToken(headerValue) === expectedToken;
|
|
21
|
+
}
|
|
22
|
+
function parsePositiveInteger(rawValue, fallback) {
|
|
23
|
+
if (!rawValue) {
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
const parsed = Number.parseInt(rawValue, 10);
|
|
27
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
28
|
+
return fallback;
|
|
29
|
+
}
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
function parseStatus(rawValue) {
|
|
33
|
+
if (!rawValue || rawValue === "all") {
|
|
34
|
+
return "all";
|
|
35
|
+
}
|
|
36
|
+
if (rawValue === "active" ||
|
|
37
|
+
rawValue === "closed" ||
|
|
38
|
+
rawValue === "expired") {
|
|
39
|
+
return rawValue;
|
|
40
|
+
}
|
|
41
|
+
return "all";
|
|
42
|
+
}
|
|
43
|
+
export async function handleRelayAdminRequest(options) {
|
|
44
|
+
if (!options.url.pathname.startsWith("/api/admin")) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (!options.adminToken) {
|
|
48
|
+
writeJson(options.response, 404, { error: "not_found" });
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (options.request.method !== "GET") {
|
|
52
|
+
options.response.writeHead(405, {
|
|
53
|
+
"content-type": "application/json",
|
|
54
|
+
allow: "GET",
|
|
55
|
+
});
|
|
56
|
+
options.response.end(JSON.stringify({ error: "method_not_allowed" }));
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (!isAuthorized(options.request, options.adminToken)) {
|
|
60
|
+
options.response.writeHead(401, {
|
|
61
|
+
"content-type": "application/json",
|
|
62
|
+
"www-authenticate": 'Bearer realm="PrivateClaw Relay Admin"',
|
|
63
|
+
});
|
|
64
|
+
options.response.end(JSON.stringify({ error: "unauthorized" }));
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
const now = options.now?.() ?? Date.now();
|
|
68
|
+
if (options.url.pathname === "/api/admin" ||
|
|
69
|
+
options.url.pathname === "/api/admin/overview") {
|
|
70
|
+
writeJson(options.response, 200, await options.metricsStore.getOverview(now));
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (options.url.pathname === "/api/admin/sessions") {
|
|
74
|
+
const page = parsePositiveInteger(options.url.searchParams.get("page"), 1);
|
|
75
|
+
const pageSize = parsePositiveInteger(options.url.searchParams.get("pageSize"), 50);
|
|
76
|
+
const query = options.url.searchParams.get("query") ?? undefined;
|
|
77
|
+
const status = parseStatus(options.url.searchParams.get("status"));
|
|
78
|
+
writeJson(options.response, 200, await options.metricsStore.listSessions({
|
|
79
|
+
page,
|
|
80
|
+
pageSize,
|
|
81
|
+
...(query ? { query } : {}),
|
|
82
|
+
...(status ? { status } : {}),
|
|
83
|
+
now,
|
|
84
|
+
}));
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
if (options.url.pathname.startsWith("/api/admin/sessions/")) {
|
|
88
|
+
const sessionId = decodeURIComponent(options.url.pathname.slice("/api/admin/sessions/".length));
|
|
89
|
+
if (!sessionId) {
|
|
90
|
+
writeJson(options.response, 404, { error: "not_found" });
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
const detail = await options.metricsStore.getSessionDetail(sessionId, now);
|
|
94
|
+
if (!detail) {
|
|
95
|
+
writeJson(options.response, 404, { error: "unknown_session" });
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
writeJson(options.response, 200, detail);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (options.url.pathname === "/api/admin/instances") {
|
|
102
|
+
writeJson(options.response, 200, {
|
|
103
|
+
generatedAt: now,
|
|
104
|
+
instances: await options.metricsStore.listInstances(now),
|
|
105
|
+
});
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
writeJson(options.response, 404, { error: "not_found" });
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=admin-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-api.js","sourceRoot":"","sources":["../src/admin-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import type { RelaySessionRecord } from "./session-store.js";
|
|
2
|
+
export type RelayAdminSessionStatus = "active" | "closed" | "expired";
|
|
3
|
+
export type RelayAdminRequestActor = "app" | "provider";
|
|
4
|
+
export interface RelayAdminRequestTypeCount {
|
|
5
|
+
actor: RelayAdminRequestActor;
|
|
6
|
+
type: string;
|
|
7
|
+
ok: number;
|
|
8
|
+
error: number;
|
|
9
|
+
}
|
|
10
|
+
export interface RelayAdminErrorCodeCount {
|
|
11
|
+
code: string;
|
|
12
|
+
count: number;
|
|
13
|
+
}
|
|
14
|
+
export interface RelayAdminRequestStats {
|
|
15
|
+
appRequests: number;
|
|
16
|
+
providerRequests: number;
|
|
17
|
+
appSuccesses: number;
|
|
18
|
+
providerSuccesses: number;
|
|
19
|
+
appErrors: number;
|
|
20
|
+
providerErrors: number;
|
|
21
|
+
appFrames: number;
|
|
22
|
+
providerFrames: number;
|
|
23
|
+
errorCodes: RelayAdminErrorCodeCount[];
|
|
24
|
+
requestTypes: RelayAdminRequestTypeCount[];
|
|
25
|
+
}
|
|
26
|
+
export interface RelayAdminOverviewTotals {
|
|
27
|
+
sessions: number;
|
|
28
|
+
activeSessions: number;
|
|
29
|
+
closedSessions: number;
|
|
30
|
+
expiredSessions: number;
|
|
31
|
+
knownParticipants: number;
|
|
32
|
+
activeParticipants: number;
|
|
33
|
+
instances: number;
|
|
34
|
+
}
|
|
35
|
+
export interface RelayAdminOverview {
|
|
36
|
+
generatedAt: number;
|
|
37
|
+
totals: RelayAdminOverviewTotals;
|
|
38
|
+
requestStats: RelayAdminRequestStats;
|
|
39
|
+
}
|
|
40
|
+
export interface RelayAdminSessionSummary {
|
|
41
|
+
sessionId: string;
|
|
42
|
+
providerId: string;
|
|
43
|
+
groupMode: boolean;
|
|
44
|
+
createdAt: number;
|
|
45
|
+
updatedAt: number;
|
|
46
|
+
expiresAt: number;
|
|
47
|
+
closedAt?: number;
|
|
48
|
+
closeReason?: string;
|
|
49
|
+
status: RelayAdminSessionStatus;
|
|
50
|
+
appMessageCount: number;
|
|
51
|
+
providerMessageCount: number;
|
|
52
|
+
distinctParticipantCount: number;
|
|
53
|
+
activeParticipantCount: number;
|
|
54
|
+
providerOnline: boolean;
|
|
55
|
+
lastAppMessageAt?: number;
|
|
56
|
+
lastProviderMessageAt?: number;
|
|
57
|
+
}
|
|
58
|
+
export interface RelayAdminSessionParticipant {
|
|
59
|
+
appId: string;
|
|
60
|
+
firstSeenAt: number;
|
|
61
|
+
lastSeenAt: number;
|
|
62
|
+
lastConnectedAt?: number;
|
|
63
|
+
lastDisconnectedAt?: number;
|
|
64
|
+
lastDisconnectReason?: string;
|
|
65
|
+
connectionCount: number;
|
|
66
|
+
messageCount: number;
|
|
67
|
+
totalConnectedMs: number;
|
|
68
|
+
currentConnectedMs: number;
|
|
69
|
+
isOnline: boolean;
|
|
70
|
+
}
|
|
71
|
+
export interface RelayAdminSessionDetail {
|
|
72
|
+
session: RelayAdminSessionSummary;
|
|
73
|
+
participants: RelayAdminSessionParticipant[];
|
|
74
|
+
}
|
|
75
|
+
export interface RelayAdminSessionListOptions {
|
|
76
|
+
status?: RelayAdminSessionStatus | "all";
|
|
77
|
+
page?: number;
|
|
78
|
+
pageSize?: number;
|
|
79
|
+
query?: string;
|
|
80
|
+
now?: number;
|
|
81
|
+
}
|
|
82
|
+
export interface RelayAdminSessionListResult {
|
|
83
|
+
total: number;
|
|
84
|
+
page: number;
|
|
85
|
+
pageSize: number;
|
|
86
|
+
sessions: RelayAdminSessionSummary[];
|
|
87
|
+
}
|
|
88
|
+
export interface RelayAdminInstanceBinding {
|
|
89
|
+
sessionId: string;
|
|
90
|
+
appId: string;
|
|
91
|
+
}
|
|
92
|
+
export interface RelayAdminInstanceStatus {
|
|
93
|
+
instanceId: string;
|
|
94
|
+
startedAt: number;
|
|
95
|
+
lastSeenAt: number;
|
|
96
|
+
activeProviders: number;
|
|
97
|
+
activeApps: number;
|
|
98
|
+
localSessions: number;
|
|
99
|
+
providerIds: string[];
|
|
100
|
+
sessionIds: string[];
|
|
101
|
+
participantBindings: RelayAdminInstanceBinding[];
|
|
102
|
+
memoryUsage: {
|
|
103
|
+
rss: number;
|
|
104
|
+
heapTotal: number;
|
|
105
|
+
heapUsed: number;
|
|
106
|
+
external: number;
|
|
107
|
+
arrayBuffers: number;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export interface RelayAdminLocalSnapshot {
|
|
111
|
+
activeProviders: number;
|
|
112
|
+
activeApps: number;
|
|
113
|
+
localSessions: number;
|
|
114
|
+
providerIds: string[];
|
|
115
|
+
sessionIds: string[];
|
|
116
|
+
participantBindings: RelayAdminInstanceBinding[];
|
|
117
|
+
memoryUsage: NodeJS.MemoryUsage;
|
|
118
|
+
}
|
|
119
|
+
export interface RelayAdminInstanceHeartbeat {
|
|
120
|
+
instanceId: string;
|
|
121
|
+
startedAt: number;
|
|
122
|
+
recordedAt: number;
|
|
123
|
+
snapshot: RelayAdminLocalSnapshot;
|
|
124
|
+
}
|
|
125
|
+
export interface RelayAdminMetricsStore {
|
|
126
|
+
readonly persistent: boolean;
|
|
127
|
+
recordSessionCreated(session: RelaySessionRecord, recordedAt: number): Promise<void>;
|
|
128
|
+
recordSessionRenewed(session: RelaySessionRecord, recordedAt: number): Promise<void>;
|
|
129
|
+
recordSessionClosed(sessionId: string, reason: string, recordedAt: number): Promise<void>;
|
|
130
|
+
recordAppAttached(session: RelaySessionRecord, appId: string, recordedAt: number): Promise<void>;
|
|
131
|
+
recordAppDetached(sessionId: string, appId: string, recordedAt: number, reason: string): Promise<void>;
|
|
132
|
+
recordAppFrame(sessionId: string, appId: string, recordedAt: number): Promise<void>;
|
|
133
|
+
recordProviderFrame(sessionId: string, recordedAt: number): Promise<void>;
|
|
134
|
+
recordRequest(params: {
|
|
135
|
+
actor: RelayAdminRequestActor;
|
|
136
|
+
type: string;
|
|
137
|
+
ok: boolean;
|
|
138
|
+
errorCode?: string;
|
|
139
|
+
}): Promise<void>;
|
|
140
|
+
recordInstanceHeartbeat(heartbeat: RelayAdminInstanceHeartbeat): Promise<void>;
|
|
141
|
+
unregisterInstance(instanceId: string): Promise<void>;
|
|
142
|
+
listSessions(options?: RelayAdminSessionListOptions): Promise<RelayAdminSessionListResult>;
|
|
143
|
+
getSessionDetail(sessionId: string, now?: number): Promise<RelayAdminSessionDetail | undefined>;
|
|
144
|
+
getOverview(now?: number): Promise<RelayAdminOverview>;
|
|
145
|
+
listInstances(now?: number): Promise<RelayAdminInstanceStatus[]>;
|
|
146
|
+
close(): Promise<void>;
|
|
147
|
+
}
|
|
148
|
+
declare abstract class BaseRelayAdminMetricsStore implements RelayAdminMetricsStore {
|
|
149
|
+
abstract readonly persistent: boolean;
|
|
150
|
+
protected buildOverview(params: {
|
|
151
|
+
sessions: RelayAdminSessionSummary[];
|
|
152
|
+
instances: RelayAdminInstanceStatus[];
|
|
153
|
+
requestStats: RelayAdminRequestStats;
|
|
154
|
+
generatedAt: number;
|
|
155
|
+
}): RelayAdminOverview;
|
|
156
|
+
abstract recordSessionCreated(session: RelaySessionRecord, recordedAt: number): Promise<void>;
|
|
157
|
+
abstract recordSessionRenewed(session: RelaySessionRecord, recordedAt: number): Promise<void>;
|
|
158
|
+
abstract recordSessionClosed(sessionId: string, reason: string, recordedAt: number): Promise<void>;
|
|
159
|
+
abstract recordAppAttached(session: RelaySessionRecord, appId: string, recordedAt: number): Promise<void>;
|
|
160
|
+
abstract recordAppDetached(sessionId: string, appId: string, recordedAt: number, reason: string): Promise<void>;
|
|
161
|
+
abstract recordAppFrame(sessionId: string, appId: string, recordedAt: number): Promise<void>;
|
|
162
|
+
abstract recordProviderFrame(sessionId: string, recordedAt: number): Promise<void>;
|
|
163
|
+
abstract recordRequest(params: {
|
|
164
|
+
actor: RelayAdminRequestActor;
|
|
165
|
+
type: string;
|
|
166
|
+
ok: boolean;
|
|
167
|
+
errorCode?: string;
|
|
168
|
+
}): Promise<void>;
|
|
169
|
+
abstract recordInstanceHeartbeat(heartbeat: RelayAdminInstanceHeartbeat): Promise<void>;
|
|
170
|
+
abstract unregisterInstance(instanceId: string): Promise<void>;
|
|
171
|
+
abstract listSessions(options?: RelayAdminSessionListOptions): Promise<RelayAdminSessionListResult>;
|
|
172
|
+
abstract getSessionDetail(sessionId: string, now?: number): Promise<RelayAdminSessionDetail | undefined>;
|
|
173
|
+
abstract getOverview(now?: number): Promise<RelayAdminOverview>;
|
|
174
|
+
abstract listInstances(now?: number): Promise<RelayAdminInstanceStatus[]>;
|
|
175
|
+
abstract close(): Promise<void>;
|
|
176
|
+
}
|
|
177
|
+
export declare class InMemoryRelayAdminMetricsStore extends BaseRelayAdminMetricsStore {
|
|
178
|
+
readonly persistent = false;
|
|
179
|
+
private readonly sessions;
|
|
180
|
+
private readonly participants;
|
|
181
|
+
private readonly instances;
|
|
182
|
+
private readonly errorCodeCounts;
|
|
183
|
+
private readonly requestTypeCounts;
|
|
184
|
+
private readonly globalStats;
|
|
185
|
+
private ensureSession;
|
|
186
|
+
private ensureParticipant;
|
|
187
|
+
private activeInstances;
|
|
188
|
+
private liveState;
|
|
189
|
+
private requestStats;
|
|
190
|
+
recordSessionCreated(session: RelaySessionRecord, recordedAt: number): Promise<void>;
|
|
191
|
+
recordSessionRenewed(session: RelaySessionRecord, recordedAt: number): Promise<void>;
|
|
192
|
+
recordSessionClosed(sessionId: string, reason: string, recordedAt: number): Promise<void>;
|
|
193
|
+
recordAppAttached(session: RelaySessionRecord, appId: string, recordedAt: number): Promise<void>;
|
|
194
|
+
recordAppDetached(sessionId: string, appId: string, recordedAt: number, reason: string): Promise<void>;
|
|
195
|
+
recordAppFrame(sessionId: string, appId: string, recordedAt: number): Promise<void>;
|
|
196
|
+
recordProviderFrame(sessionId: string, recordedAt: number): Promise<void>;
|
|
197
|
+
recordRequest(params: {
|
|
198
|
+
actor: RelayAdminRequestActor;
|
|
199
|
+
type: string;
|
|
200
|
+
ok: boolean;
|
|
201
|
+
errorCode?: string;
|
|
202
|
+
}): Promise<void>;
|
|
203
|
+
recordInstanceHeartbeat(heartbeat: RelayAdminInstanceHeartbeat): Promise<void>;
|
|
204
|
+
unregisterInstance(instanceId: string): Promise<void>;
|
|
205
|
+
listSessions(options?: RelayAdminSessionListOptions): Promise<RelayAdminSessionListResult>;
|
|
206
|
+
getSessionDetail(sessionId: string, now?: number): Promise<RelayAdminSessionDetail | undefined>;
|
|
207
|
+
getOverview(now?: number): Promise<RelayAdminOverview>;
|
|
208
|
+
listInstances(now?: number): Promise<RelayAdminInstanceStatus[]>;
|
|
209
|
+
close(): Promise<void>;
|
|
210
|
+
}
|
|
211
|
+
export declare class RedisRelayAdminMetricsStore extends BaseRelayAdminMetricsStore {
|
|
212
|
+
readonly persistent = true;
|
|
213
|
+
private readonly redis;
|
|
214
|
+
constructor(redisUrl: string);
|
|
215
|
+
private ensureSession;
|
|
216
|
+
private loadSession;
|
|
217
|
+
private loadParticipant;
|
|
218
|
+
private pruneStaleInstances;
|
|
219
|
+
private loadLiveState;
|
|
220
|
+
private loadRequestStats;
|
|
221
|
+
recordSessionCreated(session: RelaySessionRecord, recordedAt: number): Promise<void>;
|
|
222
|
+
recordSessionRenewed(session: RelaySessionRecord, recordedAt: number): Promise<void>;
|
|
223
|
+
recordSessionClosed(sessionId: string, reason: string, recordedAt: number): Promise<void>;
|
|
224
|
+
recordAppAttached(session: RelaySessionRecord, appId: string, recordedAt: number): Promise<void>;
|
|
225
|
+
recordAppDetached(sessionId: string, appId: string, recordedAt: number, reason: string): Promise<void>;
|
|
226
|
+
recordAppFrame(sessionId: string, appId: string, recordedAt: number): Promise<void>;
|
|
227
|
+
recordProviderFrame(sessionId: string, recordedAt: number): Promise<void>;
|
|
228
|
+
recordRequest(params: {
|
|
229
|
+
actor: RelayAdminRequestActor;
|
|
230
|
+
type: string;
|
|
231
|
+
ok: boolean;
|
|
232
|
+
errorCode?: string;
|
|
233
|
+
}): Promise<void>;
|
|
234
|
+
recordInstanceHeartbeat(heartbeat: RelayAdminInstanceHeartbeat): Promise<void>;
|
|
235
|
+
unregisterInstance(instanceId: string): Promise<void>;
|
|
236
|
+
listSessions(options?: RelayAdminSessionListOptions): Promise<RelayAdminSessionListResult>;
|
|
237
|
+
getSessionDetail(sessionId: string, now?: number): Promise<RelayAdminSessionDetail | undefined>;
|
|
238
|
+
getOverview(now?: number): Promise<RelayAdminOverview>;
|
|
239
|
+
listInstances(now?: number): Promise<RelayAdminInstanceStatus[]>;
|
|
240
|
+
close(): Promise<void>;
|
|
241
|
+
}
|
|
242
|
+
export declare function createRelayAdminMetricsStore(params: {
|
|
243
|
+
redisUrl?: string;
|
|
244
|
+
}): RelayAdminMetricsStore;
|
|
245
|
+
export {};
|