Team mode shares one disk per Space. RPC and executor paths under bots/<otherBotId>/ were reachable with any member-owned entry bot, leaking another member's workspace files while same-user cross-bot access stays intentional. Add path guards, filter bot listings, regression tests. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
74 lines
2.1 KiB
TypeScript
74 lines
2.1 KiB
TypeScript
import type { ComputerMode } from "@rakazo/contracts";
|
|
import type { Prisma, PrismaClient } from "./client.js";
|
|
|
|
export type { ComputerMode } from "@rakazo/contracts";
|
|
|
|
export function parseComputerMode(scope: string): ComputerMode {
|
|
if (scope === "team" || scope === "dedicated") return scope;
|
|
throw new Error(`Unknown computer scope: ${scope}`);
|
|
}
|
|
|
|
export function computerScopeKey(mode: ComputerMode, spaceId: string, botId?: string) {
|
|
if (mode === "team") return `team:${spaceId}`;
|
|
if (!botId) throw new Error("Dedicated computers require a bot id");
|
|
return `bot:${botId}`;
|
|
}
|
|
|
|
export function computerHomeKey(mode: ComputerMode, spaceId: string, botId?: string) {
|
|
if (mode === "team") return `team-${spaceId}`;
|
|
if (!botId) throw new Error("Dedicated computers require a bot id");
|
|
return botId;
|
|
}
|
|
|
|
type ComputerDb = Pick<PrismaClient, "computer">;
|
|
type BotDb = Pick<PrismaClient, "bot">;
|
|
type ExecutionLeaseDb = Pick<PrismaClient, "computerExecutionLease">;
|
|
|
|
export async function listSpaceUserBotIds(
|
|
prisma: BotDb,
|
|
spaceId: string,
|
|
userId: string,
|
|
): Promise<Set<string>> {
|
|
const rows = await prisma.bot.findMany({
|
|
where: { spaceId, userId, archivedAt: null },
|
|
select: { id: true },
|
|
});
|
|
return new Set(rows.map((row) => row.id));
|
|
}
|
|
|
|
/** Expire leases as fencing tombstones so the next acquire increments fence. */
|
|
export async function expireComputerExecutionLeases(
|
|
prisma: ExecutionLeaseDb,
|
|
where: Prisma.ComputerExecutionLeaseWhereInput,
|
|
): Promise<void> {
|
|
await prisma.computerExecutionLease.updateMany({
|
|
where,
|
|
data: { expiresAt: new Date(0) },
|
|
});
|
|
}
|
|
|
|
export async function ensureComputerRecord(
|
|
prisma: ComputerDb,
|
|
input: {
|
|
mode: ComputerMode;
|
|
spaceId: string;
|
|
userId: string;
|
|
botId?: string;
|
|
kind: string;
|
|
},
|
|
) {
|
|
const scopeKey = computerScopeKey(input.mode, input.spaceId, input.botId);
|
|
return prisma.computer.upsert({
|
|
where: { scopeKey },
|
|
create: {
|
|
spaceId: input.spaceId,
|
|
userId: input.userId,
|
|
scope: input.mode,
|
|
scopeKey,
|
|
homeKey: computerHomeKey(input.mode, input.spaceId, input.botId),
|
|
kind: input.kind,
|
|
},
|
|
update: {},
|
|
});
|
|
}
|