diff --git a/apps/api/test/fleet.test.ts b/apps/api/test/fleet.test.ts index 628280a1..488bc619 100644 --- a/apps/api/test/fleet.test.ts +++ b/apps/api/test/fleet.test.ts @@ -123,6 +123,18 @@ describe('POST /box on the fleet', () => { expect((await Box.listByUser(p.owner.userId)).length).toBe(1); }); + test('a listed fleet box names the machine whose Steam sign-in it opens', async () => { + const f = await fleet(1); + useFleet(f.organisationId); + const p = await person('fleet-host'); + expect((await createBox(p.headers, { on: 'fleet' })).status).toBe(201); + const listed = ((await (await app.request('/box', { headers: p.headers })).json()) as any).data; + const [slug] = await sql`select slug from machine where id = ${f.machines[0]!.id}`; + expect(listed).toHaveLength(1); + expect(listed[0].fleet).toBe(true); + expect(listed[0].host).toBe(slug!.slug); + }); + test('`on` defaults to your own host', async () => { const f = await fleet(1); useFleet(f.organisationId); diff --git a/packages/core/src/box/index.ts b/packages/core/src/box/index.ts index e0b14148..6d894572 100644 --- a/packages/core/src/box/index.ts +++ b/packages/core/src/box/index.ts @@ -56,6 +56,15 @@ export namespace Box { description: 'The card it runs on, as its host reports it: what a person recognises the box by. Null until the host has reported one', example: Examples.Box.gpu + }), + host: z.string().nullable().optional().meta({ + description: + "The DNS label of the machine it is placed on: that machine's Steam sign-in is at `https://./_nestri/auth`. Listed boxes only", + example: Examples.Box.host + }), + fleet: z.boolean().optional().meta({ + description: 'Whether it is on Nestri GPUs rather than your own host. Listed boxes only', + example: Examples.Box.fleet }) }) .meta({ @@ -124,12 +133,21 @@ export namespace Box { export const listByUser = fn(Info.shape.userId, async (userId) => { return Database.use(async (tx) => { return tx - .select({ box: BoxTable, gpus: MachineTable.gpus }) + .select({ + box: BoxTable, + gpus: MachineTable.gpus, + host: MachineTable.slug, + organisationId: MachineTable.organisationId + }) .from(BoxTable) .leftJoin(MachineTable, eq(MachineTable.id, BoxTable.machineId)) .where(and(eq(BoxTable.userId, userId), isNull(BoxTable.timeDeleted))) .orderBy(BoxTable.timeCreated) - .then((rows) => rows.map((r) => serialize(r.box, Machine.gpuName(r.gpus)))); + .then((rows) => + rows.map((r) => + serialize(r.box, Machine.gpuName(r.gpus), { host: r.host, fleet: r.organisationId !== null }) + ) + ); }); }); @@ -385,7 +403,11 @@ export namespace Box { } ); - export function serialize(input: typeof BoxTable.$inferSelect, gpu: string | null = null): z.infer { + export function serialize( + input: typeof BoxTable.$inferSelect, + gpu: string | null = null, + machine?: { host: string | null; fleet: boolean } + ): z.infer { return { id: input.id, userId: input.userId, @@ -395,7 +417,8 @@ export namespace Box { state: input.state as Info['state'], stopReason: input.stopReason, stopClean: input.stopClean, - gpu + gpu, + ...machine }; } } diff --git a/packages/core/src/examples.ts b/packages/core/src/examples.ts index 70ba3e16..8cf82830 100644 --- a/packages/core/src/examples.ts +++ b/packages/core/src/examples.ts @@ -162,7 +162,9 @@ export namespace Examples { state: 'created' as const, stopReason: null, stopClean: null, - gpu: 'NVIDIA GeForce RTX 3060' + gpu: 'NVIDIA GeForce RTX 3060', + host: 'm7k2q9', + fleet: false }; export const Session = {