feat(box): a listed box names its machine's host and whether it is the fleet

The desktop app gives a new cloud user a fleet box and then opens that
machine's Steam sign-in on its hostname; GET /box now says which hostname,
and which box is the fleet one.
This commit is contained in:
Wanjohi
2026-10-11 12:08:07 +03:00
parent 4fc93ca4aa
commit aa824a1905
3 changed files with 42 additions and 5 deletions
+12
View File
@@ -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);
+27 -4
View File
@@ -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://<host>.<edge zone>/_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<typeof Info> {
export function serialize(
input: typeof BoxTable.$inferSelect,
gpu: string | null = null,
machine?: { host: string | null; fleet: boolean }
): z.infer<typeof Info> {
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
};
}
}
+3 -1
View File
@@ -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 = {