@fatagnus/remote-cmd-relay-convex 1.0.0
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/package.json +41 -0
- package/src/README.md +413 -0
- package/src/_generated/api.ts +62 -0
- package/src/_generated/component.ts +565 -0
- package/src/_generated/dataModel.ts +60 -0
- package/src/_generated/server.ts +161 -0
- package/src/assignments.test.ts +233 -0
- package/src/assignments.ts +222 -0
- package/src/commands.test.ts +331 -0
- package/src/commands.ts +286 -0
- package/src/component.ts +11 -0
- package/src/configPush.ts +96 -0
- package/src/convex.config.ts +5 -0
- package/src/credentials.ts +112 -0
- package/src/public.test.ts +576 -0
- package/src/public.ts +436 -0
- package/src/schema.ts +180 -0
- package/src/status.test.ts +308 -0
- package/src/status.ts +172 -0
- package/src/test.helpers.ts +399 -0
- package/src/test.setup.ts +256 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { convexTest } from "convex-test";
|
|
2
|
+
import type { Id } from "./_generated/dataModel";
|
|
3
|
+
|
|
4
|
+
// Types for mock data
|
|
5
|
+
export type CommandStatus =
|
|
6
|
+
| "pending"
|
|
7
|
+
| "claimed"
|
|
8
|
+
| "executing"
|
|
9
|
+
| "completed"
|
|
10
|
+
| "failed"
|
|
11
|
+
| "timeout";
|
|
12
|
+
export type TargetType = "local" | "ssh";
|
|
13
|
+
export type Capability = "ssh" | "local_cmd" | "perf_metrics";
|
|
14
|
+
export type CredentialType = "ssh_key" | "password" | "api_key";
|
|
15
|
+
export type StorageMode = "relay_only" | "shared";
|
|
16
|
+
export type ConfigPushType =
|
|
17
|
+
| "credential"
|
|
18
|
+
| "ssh_targets"
|
|
19
|
+
| "allowed_commands"
|
|
20
|
+
| "metrics_interval";
|
|
21
|
+
export type ConfigPushStatus = "pending" | "sent" | "acked" | "failed";
|
|
22
|
+
|
|
23
|
+
// Mock relay assignment data structure
|
|
24
|
+
export interface MockRelayAssignment {
|
|
25
|
+
_id: Id<"relayAssignments">;
|
|
26
|
+
apiKeyId: string;
|
|
27
|
+
machineId: string;
|
|
28
|
+
name: string;
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
lastSeenAt?: number;
|
|
31
|
+
createdBy: string;
|
|
32
|
+
createdAt: number;
|
|
33
|
+
updatedAt: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Options for creating a mock relay assignment
|
|
37
|
+
export interface CreateMockRelayAssignmentOptions {
|
|
38
|
+
apiKeyId?: string;
|
|
39
|
+
machineId?: string;
|
|
40
|
+
name?: string;
|
|
41
|
+
enabled?: boolean;
|
|
42
|
+
createdBy?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create a mock relay assignment in the test database.
|
|
47
|
+
*/
|
|
48
|
+
export async function createMockRelayAssignment(
|
|
49
|
+
t: ReturnType<typeof convexTest>,
|
|
50
|
+
options: CreateMockRelayAssignmentOptions = {}
|
|
51
|
+
): Promise<MockRelayAssignment> {
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
const apiKeyId = options.apiKeyId ?? `api-key-${now}`;
|
|
54
|
+
const machineId = options.machineId ?? `machine-${now}`;
|
|
55
|
+
const name = options.name ?? "Test Relay";
|
|
56
|
+
const enabled = options.enabled ?? true;
|
|
57
|
+
const createdBy = options.createdBy ?? "test-user";
|
|
58
|
+
|
|
59
|
+
const assignmentId = await t.run(async (ctx) => {
|
|
60
|
+
return await ctx.db.insert("relayAssignments", {
|
|
61
|
+
apiKeyId,
|
|
62
|
+
machineId,
|
|
63
|
+
name,
|
|
64
|
+
enabled,
|
|
65
|
+
createdBy,
|
|
66
|
+
createdAt: now,
|
|
67
|
+
updatedAt: now,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
_id: assignmentId as Id<"relayAssignments">,
|
|
73
|
+
apiKeyId,
|
|
74
|
+
machineId,
|
|
75
|
+
name,
|
|
76
|
+
enabled,
|
|
77
|
+
createdBy,
|
|
78
|
+
createdAt: now,
|
|
79
|
+
updatedAt: now,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Mock command data structure
|
|
84
|
+
export interface MockCommand {
|
|
85
|
+
_id: Id<"commandQueue">;
|
|
86
|
+
machineId: string;
|
|
87
|
+
command: string;
|
|
88
|
+
targetType: TargetType;
|
|
89
|
+
targetHost?: string;
|
|
90
|
+
targetPort?: number;
|
|
91
|
+
targetUsername?: string;
|
|
92
|
+
timeoutMs: number;
|
|
93
|
+
status: CommandStatus;
|
|
94
|
+
createdBy: string;
|
|
95
|
+
createdAt: number;
|
|
96
|
+
updatedAt: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Options for creating a mock command
|
|
100
|
+
export interface CreateMockCommandOptions {
|
|
101
|
+
machineId?: string;
|
|
102
|
+
command?: string;
|
|
103
|
+
targetType?: TargetType;
|
|
104
|
+
targetHost?: string;
|
|
105
|
+
targetPort?: number;
|
|
106
|
+
targetUsername?: string;
|
|
107
|
+
timeoutMs?: number;
|
|
108
|
+
status?: CommandStatus;
|
|
109
|
+
createdBy?: string;
|
|
110
|
+
claimedBy?: string;
|
|
111
|
+
claimedAt?: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Create a mock command in the test database.
|
|
116
|
+
*/
|
|
117
|
+
export async function createMockCommand(
|
|
118
|
+
t: ReturnType<typeof convexTest>,
|
|
119
|
+
options: CreateMockCommandOptions = {}
|
|
120
|
+
): Promise<MockCommand> {
|
|
121
|
+
const now = Date.now();
|
|
122
|
+
const machineId = options.machineId ?? `machine-${now}`;
|
|
123
|
+
const command = options.command ?? "echo hello";
|
|
124
|
+
const targetType = options.targetType ?? "local";
|
|
125
|
+
const timeoutMs = options.timeoutMs ?? 30000;
|
|
126
|
+
const status = options.status ?? "pending";
|
|
127
|
+
const createdBy = options.createdBy ?? "test-user";
|
|
128
|
+
|
|
129
|
+
const commandId = await t.run(async (ctx) => {
|
|
130
|
+
return await ctx.db.insert("commandQueue", {
|
|
131
|
+
machineId,
|
|
132
|
+
command,
|
|
133
|
+
targetType,
|
|
134
|
+
targetHost: options.targetHost,
|
|
135
|
+
targetPort: options.targetPort ?? (targetType === "ssh" ? 22 : undefined),
|
|
136
|
+
targetUsername: options.targetUsername,
|
|
137
|
+
timeoutMs,
|
|
138
|
+
status,
|
|
139
|
+
claimedBy: options.claimedBy,
|
|
140
|
+
claimedAt: options.claimedAt,
|
|
141
|
+
createdBy,
|
|
142
|
+
createdAt: now,
|
|
143
|
+
updatedAt: now,
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
_id: commandId as Id<"commandQueue">,
|
|
149
|
+
machineId,
|
|
150
|
+
command,
|
|
151
|
+
targetType,
|
|
152
|
+
targetHost: options.targetHost,
|
|
153
|
+
targetPort: options.targetPort,
|
|
154
|
+
targetUsername: options.targetUsername,
|
|
155
|
+
timeoutMs,
|
|
156
|
+
status,
|
|
157
|
+
createdBy,
|
|
158
|
+
createdAt: now,
|
|
159
|
+
updatedAt: now,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Mock config push data structure
|
|
164
|
+
export interface MockConfigPush {
|
|
165
|
+
_id: Id<"configPushQueue">;
|
|
166
|
+
relayId: string;
|
|
167
|
+
pushType: ConfigPushType;
|
|
168
|
+
payload: string;
|
|
169
|
+
status: ConfigPushStatus;
|
|
170
|
+
createdBy: string;
|
|
171
|
+
createdAt: number;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Options for creating a mock config push
|
|
175
|
+
export interface CreateMockConfigPushOptions {
|
|
176
|
+
relayId?: string;
|
|
177
|
+
pushType?: ConfigPushType;
|
|
178
|
+
payload?: string;
|
|
179
|
+
status?: ConfigPushStatus;
|
|
180
|
+
createdBy?: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Create a mock config push in the test database.
|
|
185
|
+
*/
|
|
186
|
+
export async function createMockConfigPush(
|
|
187
|
+
t: ReturnType<typeof convexTest>,
|
|
188
|
+
options: CreateMockConfigPushOptions = {}
|
|
189
|
+
): Promise<MockConfigPush> {
|
|
190
|
+
const now = Date.now();
|
|
191
|
+
const relayId = options.relayId ?? `relay-${now}`;
|
|
192
|
+
const pushType = options.pushType ?? "credential";
|
|
193
|
+
const payload = options.payload ?? JSON.stringify({ test: "data" });
|
|
194
|
+
const status = options.status ?? "pending";
|
|
195
|
+
const createdBy = options.createdBy ?? "test-user";
|
|
196
|
+
|
|
197
|
+
const pushId = await t.run(async (ctx) => {
|
|
198
|
+
return await ctx.db.insert("configPushQueue", {
|
|
199
|
+
relayId,
|
|
200
|
+
pushType,
|
|
201
|
+
payload,
|
|
202
|
+
status,
|
|
203
|
+
createdBy,
|
|
204
|
+
createdAt: now,
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
_id: pushId as Id<"configPushQueue">,
|
|
210
|
+
relayId,
|
|
211
|
+
pushType,
|
|
212
|
+
payload,
|
|
213
|
+
status,
|
|
214
|
+
createdBy,
|
|
215
|
+
createdAt: now,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Mock relay status data structure
|
|
220
|
+
export interface MockRelayStatus {
|
|
221
|
+
_id: Id<"relayStatus">;
|
|
222
|
+
relayId: string;
|
|
223
|
+
capabilities: Capability[];
|
|
224
|
+
version?: string;
|
|
225
|
+
hostname?: string;
|
|
226
|
+
platform?: string;
|
|
227
|
+
lastHeartbeatAt: number;
|
|
228
|
+
createdAt: number;
|
|
229
|
+
updatedAt: number;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Options for creating a mock relay status
|
|
233
|
+
export interface CreateMockRelayStatusOptions {
|
|
234
|
+
relayId?: string;
|
|
235
|
+
capabilities?: Capability[];
|
|
236
|
+
version?: string;
|
|
237
|
+
hostname?: string;
|
|
238
|
+
platform?: string;
|
|
239
|
+
lastHeartbeatAt?: number;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Create a mock relay status in the test database.
|
|
244
|
+
*/
|
|
245
|
+
export async function createMockRelayStatus(
|
|
246
|
+
t: ReturnType<typeof convexTest>,
|
|
247
|
+
options: CreateMockRelayStatusOptions = {}
|
|
248
|
+
): Promise<MockRelayStatus> {
|
|
249
|
+
const now = Date.now();
|
|
250
|
+
const relayId = options.relayId ?? `relay-${now}`;
|
|
251
|
+
const capabilities = options.capabilities ?? ["local_cmd"];
|
|
252
|
+
const lastHeartbeatAt = options.lastHeartbeatAt ?? now;
|
|
253
|
+
|
|
254
|
+
const statusId = await t.run(async (ctx) => {
|
|
255
|
+
return await ctx.db.insert("relayStatus", {
|
|
256
|
+
relayId,
|
|
257
|
+
capabilities,
|
|
258
|
+
version: options.version,
|
|
259
|
+
hostname: options.hostname,
|
|
260
|
+
platform: options.platform,
|
|
261
|
+
lastHeartbeatAt,
|
|
262
|
+
createdAt: now,
|
|
263
|
+
updatedAt: now,
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
_id: statusId as Id<"relayStatus">,
|
|
269
|
+
relayId,
|
|
270
|
+
capabilities,
|
|
271
|
+
version: options.version,
|
|
272
|
+
hostname: options.hostname,
|
|
273
|
+
platform: options.platform,
|
|
274
|
+
lastHeartbeatAt,
|
|
275
|
+
createdAt: now,
|
|
276
|
+
updatedAt: now,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Mock credential inventory data structure
|
|
281
|
+
export interface MockCredentialInventory {
|
|
282
|
+
_id: Id<"relayCredentialInventory">;
|
|
283
|
+
relayId: string;
|
|
284
|
+
credentialName: string;
|
|
285
|
+
credentialType: CredentialType;
|
|
286
|
+
targetHost?: string;
|
|
287
|
+
storageMode: StorageMode;
|
|
288
|
+
lastUpdatedAt: number;
|
|
289
|
+
reportedAt: number;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Options for creating a mock credential inventory
|
|
293
|
+
export interface CreateMockCredentialInventoryOptions {
|
|
294
|
+
relayId?: string;
|
|
295
|
+
credentialName?: string;
|
|
296
|
+
credentialType?: CredentialType;
|
|
297
|
+
targetHost?: string;
|
|
298
|
+
storageMode?: StorageMode;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Create a mock credential inventory entry in the test database.
|
|
303
|
+
*/
|
|
304
|
+
export async function createMockCredentialInventory(
|
|
305
|
+
t: ReturnType<typeof convexTest>,
|
|
306
|
+
options: CreateMockCredentialInventoryOptions = {}
|
|
307
|
+
): Promise<MockCredentialInventory> {
|
|
308
|
+
const now = Date.now();
|
|
309
|
+
const relayId = options.relayId ?? `relay-${now}`;
|
|
310
|
+
const credentialName = options.credentialName ?? `cred-${now}`;
|
|
311
|
+
const credentialType = options.credentialType ?? "ssh_key";
|
|
312
|
+
const storageMode = options.storageMode ?? "relay_only";
|
|
313
|
+
|
|
314
|
+
const credId = await t.run(async (ctx) => {
|
|
315
|
+
return await ctx.db.insert("relayCredentialInventory", {
|
|
316
|
+
relayId,
|
|
317
|
+
credentialName,
|
|
318
|
+
credentialType,
|
|
319
|
+
targetHost: options.targetHost,
|
|
320
|
+
storageMode,
|
|
321
|
+
lastUpdatedAt: now,
|
|
322
|
+
reportedAt: now,
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
_id: credId as Id<"relayCredentialInventory">,
|
|
328
|
+
relayId,
|
|
329
|
+
credentialName,
|
|
330
|
+
credentialType,
|
|
331
|
+
targetHost: options.targetHost,
|
|
332
|
+
storageMode,
|
|
333
|
+
lastUpdatedAt: now,
|
|
334
|
+
reportedAt: now,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Mock shared credential data structure
|
|
339
|
+
export interface MockSharedCredential {
|
|
340
|
+
_id: Id<"sharedCredentials">;
|
|
341
|
+
name: string;
|
|
342
|
+
credentialType: CredentialType;
|
|
343
|
+
targetHost?: string;
|
|
344
|
+
encryptedValue: string;
|
|
345
|
+
assignedRelays: string[];
|
|
346
|
+
createdBy: string;
|
|
347
|
+
createdAt: number;
|
|
348
|
+
updatedAt: number;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Options for creating a mock shared credential
|
|
352
|
+
export interface CreateMockSharedCredentialOptions {
|
|
353
|
+
name?: string;
|
|
354
|
+
credentialType?: CredentialType;
|
|
355
|
+
targetHost?: string;
|
|
356
|
+
encryptedValue?: string;
|
|
357
|
+
assignedRelays?: string[];
|
|
358
|
+
createdBy?: string;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Create a mock shared credential in the test database.
|
|
363
|
+
*/
|
|
364
|
+
export async function createMockSharedCredential(
|
|
365
|
+
t: ReturnType<typeof convexTest>,
|
|
366
|
+
options: CreateMockSharedCredentialOptions = {}
|
|
367
|
+
): Promise<MockSharedCredential> {
|
|
368
|
+
const now = Date.now();
|
|
369
|
+
const name = options.name ?? `shared-cred-${now}`;
|
|
370
|
+
const credentialType = options.credentialType ?? "ssh_key";
|
|
371
|
+
const encryptedValue = options.encryptedValue ?? "encrypted-test-value";
|
|
372
|
+
const assignedRelays = options.assignedRelays ?? [];
|
|
373
|
+
const createdBy = options.createdBy ?? "test-user";
|
|
374
|
+
|
|
375
|
+
const credId = await t.run(async (ctx) => {
|
|
376
|
+
return await ctx.db.insert("sharedCredentials", {
|
|
377
|
+
name,
|
|
378
|
+
credentialType,
|
|
379
|
+
targetHost: options.targetHost,
|
|
380
|
+
encryptedValue,
|
|
381
|
+
assignedRelays,
|
|
382
|
+
createdBy,
|
|
383
|
+
createdAt: now,
|
|
384
|
+
updatedAt: now,
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
return {
|
|
389
|
+
_id: credId as Id<"sharedCredentials">,
|
|
390
|
+
name,
|
|
391
|
+
credentialType,
|
|
392
|
+
targetHost: options.targetHost,
|
|
393
|
+
encryptedValue,
|
|
394
|
+
assignedRelays,
|
|
395
|
+
createdBy,
|
|
396
|
+
createdAt: now,
|
|
397
|
+
updatedAt: now,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { convexTest } from "convex-test";
|
|
2
|
+
import schema from "./schema";
|
|
3
|
+
import type { Id } from "./_generated/dataModel";
|
|
4
|
+
|
|
5
|
+
// Import modules for convex-test
|
|
6
|
+
const modules: Record<string, () => Promise<unknown>> = import.meta.glob(
|
|
7
|
+
"./**/*.ts"
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create a test Convex instance for remoteCmdRelay component.
|
|
12
|
+
*/
|
|
13
|
+
export function createRelayTestConvex() {
|
|
14
|
+
return convexTest(schema, modules);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type RelayTestConvex = ReturnType<typeof createRelayTestConvex>;
|
|
18
|
+
|
|
19
|
+
// ===== Test Data Helpers =====
|
|
20
|
+
|
|
21
|
+
export interface CreateRelayAssignmentOptions {
|
|
22
|
+
apiKeyId?: string;
|
|
23
|
+
machineId?: string;
|
|
24
|
+
name?: string;
|
|
25
|
+
enabled?: boolean;
|
|
26
|
+
createdBy?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create a relay assignment in the test database
|
|
31
|
+
*/
|
|
32
|
+
export async function createTestRelayAssignment(
|
|
33
|
+
t: RelayTestConvex,
|
|
34
|
+
options: CreateRelayAssignmentOptions = {}
|
|
35
|
+
): Promise<{
|
|
36
|
+
_id: Id<"relayAssignments">;
|
|
37
|
+
apiKeyId: string;
|
|
38
|
+
machineId: string;
|
|
39
|
+
name: string;
|
|
40
|
+
enabled: boolean;
|
|
41
|
+
createdBy: string;
|
|
42
|
+
}> {
|
|
43
|
+
const now = Date.now();
|
|
44
|
+
const apiKeyId = options.apiKeyId ?? `test-api-key-${now}`;
|
|
45
|
+
const machineId = options.machineId ?? `test-machine-${now}`;
|
|
46
|
+
const name = options.name ?? "Test Relay";
|
|
47
|
+
const enabled = options.enabled ?? true;
|
|
48
|
+
const createdBy = options.createdBy ?? "test-user";
|
|
49
|
+
|
|
50
|
+
const id = await t.run(async (ctx) => {
|
|
51
|
+
return await ctx.db.insert("relayAssignments", {
|
|
52
|
+
apiKeyId,
|
|
53
|
+
machineId,
|
|
54
|
+
name,
|
|
55
|
+
enabled,
|
|
56
|
+
createdBy,
|
|
57
|
+
createdAt: now,
|
|
58
|
+
updatedAt: now,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
_id: id,
|
|
64
|
+
apiKeyId,
|
|
65
|
+
machineId,
|
|
66
|
+
name,
|
|
67
|
+
enabled,
|
|
68
|
+
createdBy,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface CreateCommandOptions {
|
|
73
|
+
machineId: string;
|
|
74
|
+
command?: string;
|
|
75
|
+
targetType?: "local" | "ssh";
|
|
76
|
+
targetHost?: string;
|
|
77
|
+
targetPort?: number;
|
|
78
|
+
targetUsername?: string;
|
|
79
|
+
timeoutMs?: number;
|
|
80
|
+
status?: "pending" | "claimed" | "executing" | "completed" | "failed" | "timeout";
|
|
81
|
+
createdBy?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Create a command in the test database
|
|
86
|
+
*/
|
|
87
|
+
export async function createTestCommand(
|
|
88
|
+
t: RelayTestConvex,
|
|
89
|
+
options: CreateCommandOptions
|
|
90
|
+
): Promise<{
|
|
91
|
+
_id: Id<"commandQueue">;
|
|
92
|
+
machineId: string;
|
|
93
|
+
command: string;
|
|
94
|
+
targetType: "local" | "ssh";
|
|
95
|
+
status: "pending" | "claimed" | "executing" | "completed" | "failed" | "timeout";
|
|
96
|
+
}> {
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
const command = options.command ?? "echo test";
|
|
99
|
+
const targetType = options.targetType ?? "local";
|
|
100
|
+
const status = options.status ?? "pending";
|
|
101
|
+
const createdBy = options.createdBy ?? "test-user";
|
|
102
|
+
|
|
103
|
+
const id = await t.run(async (ctx) => {
|
|
104
|
+
return await ctx.db.insert("commandQueue", {
|
|
105
|
+
machineId: options.machineId,
|
|
106
|
+
command,
|
|
107
|
+
targetType,
|
|
108
|
+
targetHost: options.targetHost,
|
|
109
|
+
targetPort: options.targetPort ?? 22,
|
|
110
|
+
targetUsername: options.targetUsername,
|
|
111
|
+
timeoutMs: options.timeoutMs ?? 30000,
|
|
112
|
+
status,
|
|
113
|
+
createdBy,
|
|
114
|
+
createdAt: now,
|
|
115
|
+
updatedAt: now,
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
_id: id,
|
|
121
|
+
machineId: options.machineId,
|
|
122
|
+
command,
|
|
123
|
+
targetType,
|
|
124
|
+
status,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface CreateRelayStatusOptions {
|
|
129
|
+
relayId: string;
|
|
130
|
+
capabilities?: ("ssh" | "local_cmd" | "perf_metrics")[];
|
|
131
|
+
metrics?: {
|
|
132
|
+
cpuPercent?: number;
|
|
133
|
+
memoryPercent?: number;
|
|
134
|
+
memoryUsedMb?: number;
|
|
135
|
+
memoryTotalMb?: number;
|
|
136
|
+
};
|
|
137
|
+
version?: string;
|
|
138
|
+
hostname?: string;
|
|
139
|
+
platform?: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Create a relay status record in the test database
|
|
144
|
+
*/
|
|
145
|
+
export async function createTestRelayStatus(
|
|
146
|
+
t: RelayTestConvex,
|
|
147
|
+
options: CreateRelayStatusOptions
|
|
148
|
+
): Promise<Id<"relayStatus">> {
|
|
149
|
+
const now = Date.now();
|
|
150
|
+
const capabilities = options.capabilities ?? ["local_cmd"];
|
|
151
|
+
|
|
152
|
+
return await t.run(async (ctx) => {
|
|
153
|
+
return await ctx.db.insert("relayStatus", {
|
|
154
|
+
relayId: options.relayId,
|
|
155
|
+
capabilities,
|
|
156
|
+
metrics: options.metrics,
|
|
157
|
+
version: options.version ?? "1.0.0",
|
|
158
|
+
hostname: options.hostname ?? "test-host",
|
|
159
|
+
platform: options.platform ?? "linux-x64",
|
|
160
|
+
lastHeartbeatAt: now,
|
|
161
|
+
createdAt: now,
|
|
162
|
+
updatedAt: now,
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface CreateCredentialInventoryOptions {
|
|
168
|
+
relayId: string;
|
|
169
|
+
credentialName?: string;
|
|
170
|
+
credentialType?: "ssh_key" | "password" | "api_key";
|
|
171
|
+
targetHost?: string;
|
|
172
|
+
storageMode?: "relay_only" | "shared";
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Create a credential inventory record in the test database
|
|
177
|
+
*/
|
|
178
|
+
export async function createTestCredentialInventory(
|
|
179
|
+
t: RelayTestConvex,
|
|
180
|
+
options: CreateCredentialInventoryOptions
|
|
181
|
+
): Promise<Id<"relayCredentialInventory">> {
|
|
182
|
+
const now = Date.now();
|
|
183
|
+
|
|
184
|
+
return await t.run(async (ctx) => {
|
|
185
|
+
return await ctx.db.insert("relayCredentialInventory", {
|
|
186
|
+
relayId: options.relayId,
|
|
187
|
+
credentialName: options.credentialName ?? `cred-${now}`,
|
|
188
|
+
credentialType: options.credentialType ?? "ssh_key",
|
|
189
|
+
targetHost: options.targetHost,
|
|
190
|
+
storageMode: options.storageMode ?? "relay_only",
|
|
191
|
+
lastUpdatedAt: now,
|
|
192
|
+
reportedAt: now,
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface CreateSharedCredentialOptions {
|
|
198
|
+
name?: string;
|
|
199
|
+
credentialType?: "ssh_key" | "password" | "api_key";
|
|
200
|
+
targetHost?: string;
|
|
201
|
+
encryptedValue?: string;
|
|
202
|
+
assignedRelays?: string[];
|
|
203
|
+
createdBy?: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Create a shared credential in the test database
|
|
208
|
+
*/
|
|
209
|
+
export async function createTestSharedCredential(
|
|
210
|
+
t: RelayTestConvex,
|
|
211
|
+
options: CreateSharedCredentialOptions = {}
|
|
212
|
+
): Promise<Id<"sharedCredentials">> {
|
|
213
|
+
const now = Date.now();
|
|
214
|
+
|
|
215
|
+
return await t.run(async (ctx) => {
|
|
216
|
+
return await ctx.db.insert("sharedCredentials", {
|
|
217
|
+
name: options.name ?? `shared-cred-${now}`,
|
|
218
|
+
credentialType: options.credentialType ?? "ssh_key",
|
|
219
|
+
targetHost: options.targetHost,
|
|
220
|
+
encryptedValue: options.encryptedValue ?? "encrypted-value",
|
|
221
|
+
assignedRelays: options.assignedRelays ?? [],
|
|
222
|
+
createdBy: options.createdBy ?? "test-user",
|
|
223
|
+
createdAt: now,
|
|
224
|
+
updatedAt: now,
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface CreateConfigPushOptions {
|
|
230
|
+
relayId: string;
|
|
231
|
+
pushType?: "credential" | "ssh_targets" | "allowed_commands" | "metrics_interval";
|
|
232
|
+
payload?: string;
|
|
233
|
+
status?: "pending" | "sent" | "acked" | "failed";
|
|
234
|
+
createdBy?: string;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Create a config push in the test database
|
|
239
|
+
*/
|
|
240
|
+
export async function createTestConfigPush(
|
|
241
|
+
t: RelayTestConvex,
|
|
242
|
+
options: CreateConfigPushOptions
|
|
243
|
+
): Promise<Id<"configPushQueue">> {
|
|
244
|
+
const now = Date.now();
|
|
245
|
+
|
|
246
|
+
return await t.run(async (ctx) => {
|
|
247
|
+
return await ctx.db.insert("configPushQueue", {
|
|
248
|
+
relayId: options.relayId,
|
|
249
|
+
pushType: options.pushType ?? "credential",
|
|
250
|
+
payload: options.payload ?? JSON.stringify({ test: true }),
|
|
251
|
+
status: options.status ?? "pending",
|
|
252
|
+
createdBy: options.createdBy ?? "test-user",
|
|
253
|
+
createdAt: now,
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
}
|