313 lines
9.8 KiB
TypeScript
313 lines
9.8 KiB
TypeScript
import type { FastifyInstance } from "fastify";
|
|
import { eq, and } from "drizzle-orm";
|
|
import { db } from "../db/index.js";
|
|
import { wms, packages, projects } from "../db/schema.js";
|
|
|
|
export async function wmRoutes(app: FastifyInstance) {
|
|
// GET /projects/:projectId/wms — list WMs for a project
|
|
app.get<{ Params: { projectId: string } }>(
|
|
"/projects/:projectId/wms",
|
|
{
|
|
preHandler: [app.authenticate, app.requireAccount],
|
|
schema: {
|
|
description: "List all WMs for a project",
|
|
tags: ["WMs"],
|
|
security: [{ bearerAuth: [] }],
|
|
params: {
|
|
type: "object",
|
|
properties: {
|
|
accountId: { type: "string", format: "uuid" },
|
|
projectId: { type: "string", format: "uuid" },
|
|
},
|
|
},
|
|
response: {
|
|
200: {
|
|
description: "List of WMs",
|
|
type: "array",
|
|
items: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "string" },
|
|
projectId: { type: "string" },
|
|
name: { type: "string" },
|
|
packageId: { type: "string", nullable: true },
|
|
vcpu: { type: "number" },
|
|
ram: { type: "integer" },
|
|
disk: { type: "integer" },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
updatedAt: { type: "string", format: "date-time" },
|
|
},
|
|
},
|
|
},
|
|
404: {
|
|
description: "Project not found",
|
|
type: "object",
|
|
properties: { error: { type: "string" } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
async (request, reply) => {
|
|
// Verify project belongs to the account
|
|
const [project] = await db
|
|
.select()
|
|
.from(projects)
|
|
.where(
|
|
and(eq(projects.id, request.params.projectId), eq(projects.accountId, request.accountId)),
|
|
);
|
|
|
|
if (!project) {
|
|
return reply.status(404).send({ error: "Project not found" });
|
|
}
|
|
|
|
return db.select().from(wms).where(eq(wms.projectId, request.params.projectId));
|
|
},
|
|
);
|
|
|
|
// POST /projects/:projectId/wms — create a WM
|
|
app.post<{
|
|
Params: { projectId: string };
|
|
Body: { name: string; packageId?: string; vcpu?: number; ram?: number; disk?: number };
|
|
}>(
|
|
"/projects/:projectId/wms",
|
|
{
|
|
preHandler: [app.authenticate, app.requireAccount],
|
|
schema: {
|
|
description:
|
|
"Create a WM from a package or with custom settings. Provide packageId to use a package, or vcpu/ram/disk for custom settings.",
|
|
tags: ["WMs"],
|
|
security: [{ bearerAuth: [] }],
|
|
params: {
|
|
type: "object",
|
|
properties: {
|
|
accountId: { type: "string", format: "uuid" },
|
|
projectId: { type: "string", format: "uuid" },
|
|
},
|
|
},
|
|
body: {
|
|
type: "object",
|
|
required: ["name"],
|
|
properties: {
|
|
name: { type: "string", minLength: 1 },
|
|
packageId: { type: "string", format: "uuid" },
|
|
vcpu: { type: "number", minimum: 0.25 },
|
|
ram: { type: "integer", minimum: 1 },
|
|
disk: { type: "integer", minimum: 1 },
|
|
},
|
|
},
|
|
response: {
|
|
201: {
|
|
description: "WM created successfully",
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "string" },
|
|
projectId: { type: "string" },
|
|
name: { type: "string" },
|
|
packageId: { type: "string", nullable: true },
|
|
vcpu: { type: "number" },
|
|
ram: { type: "integer" },
|
|
disk: { type: "integer" },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
updatedAt: { type: "string", format: "date-time" },
|
|
},
|
|
},
|
|
400: {
|
|
description: "Invalid input",
|
|
type: "object",
|
|
properties: { error: { type: "string" } },
|
|
},
|
|
404: {
|
|
description: "Project or package not found",
|
|
type: "object",
|
|
properties: { error: { type: "string" } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
async (request, reply) => {
|
|
const { name, packageId, vcpu, ram, disk } = request.body;
|
|
|
|
// Verify project belongs to the account
|
|
const [project] = await db
|
|
.select()
|
|
.from(projects)
|
|
.where(
|
|
and(eq(projects.id, request.params.projectId), eq(projects.accountId, request.accountId)),
|
|
);
|
|
|
|
if (!project) {
|
|
return reply.status(404).send({ error: "Project not found" });
|
|
}
|
|
|
|
let settings: { vcpu: number; ram: number; disk: number; packageId: string | null };
|
|
|
|
if (packageId) {
|
|
const [pkg] = await db.select().from(packages).where(eq(packages.id, packageId));
|
|
if (!pkg) {
|
|
return reply.status(404).send({ error: "Package not found" });
|
|
}
|
|
settings = { vcpu: pkg.vcpu, ram: pkg.ram, disk: pkg.disk, packageId: pkg.id };
|
|
} else if (vcpu !== undefined && ram !== undefined && disk !== undefined) {
|
|
settings = { vcpu, ram, disk, packageId: null };
|
|
} else {
|
|
return reply
|
|
.status(400)
|
|
.send({ error: "Provide either packageId or all of vcpu, ram, and disk" });
|
|
}
|
|
|
|
const [wm] = await db
|
|
.insert(wms)
|
|
.values({
|
|
projectId: request.params.projectId,
|
|
name,
|
|
...settings,
|
|
})
|
|
.returning();
|
|
|
|
return reply.status(201).send(wm);
|
|
},
|
|
);
|
|
|
|
// PATCH /projects/:projectId/wms/:id — update a WM
|
|
app.patch<{
|
|
Params: { projectId: string; id: string };
|
|
Body: { name?: string; vcpu?: number; ram?: number; disk?: number };
|
|
}>(
|
|
"/projects/:projectId/wms/:id",
|
|
{
|
|
preHandler: [app.authenticate, app.requireAccount],
|
|
schema: {
|
|
description: "Update a WM's name or settings",
|
|
tags: ["WMs"],
|
|
security: [{ bearerAuth: [] }],
|
|
params: {
|
|
type: "object",
|
|
properties: {
|
|
accountId: { type: "string", format: "uuid" },
|
|
projectId: { type: "string", format: "uuid" },
|
|
id: { type: "string", format: "uuid" },
|
|
},
|
|
},
|
|
body: {
|
|
type: "object",
|
|
properties: {
|
|
name: { type: "string", minLength: 1 },
|
|
vcpu: { type: "number", minimum: 0.25 },
|
|
ram: { type: "integer", minimum: 1 },
|
|
disk: { type: "integer", minimum: 1 },
|
|
},
|
|
},
|
|
response: {
|
|
200: {
|
|
description: "WM updated successfully",
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "string" },
|
|
projectId: { type: "string" },
|
|
name: { type: "string" },
|
|
packageId: { type: "string", nullable: true },
|
|
vcpu: { type: "number" },
|
|
ram: { type: "integer" },
|
|
disk: { type: "integer" },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
updatedAt: { type: "string", format: "date-time" },
|
|
},
|
|
},
|
|
404: {
|
|
description: "WM not found",
|
|
type: "object",
|
|
properties: { error: { type: "string" } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
async (request, reply) => {
|
|
// Verify project belongs to the account
|
|
const [project] = await db
|
|
.select()
|
|
.from(projects)
|
|
.where(
|
|
and(eq(projects.id, request.params.projectId), eq(projects.accountId, request.accountId)),
|
|
);
|
|
|
|
if (!project) {
|
|
return reply.status(404).send({ error: "Project not found" });
|
|
}
|
|
|
|
const { name, vcpu, ram, disk } = request.body;
|
|
const updates: Record<string, unknown> = { updatedAt: new Date() };
|
|
if (name !== undefined) updates.name = name;
|
|
if (vcpu !== undefined) updates.vcpu = vcpu;
|
|
if (ram !== undefined) updates.ram = ram;
|
|
if (disk !== undefined) updates.disk = disk;
|
|
|
|
const [wm] = await db
|
|
.update(wms)
|
|
.set(updates)
|
|
.where(and(eq(wms.id, request.params.id), eq(wms.projectId, request.params.projectId)))
|
|
.returning();
|
|
|
|
if (!wm) {
|
|
return reply.status(404).send({ error: "WM not found" });
|
|
}
|
|
|
|
return wm;
|
|
},
|
|
);
|
|
|
|
// DELETE /projects/:projectId/wms/:id — delete a WM
|
|
app.delete<{ Params: { projectId: string; id: string } }>(
|
|
"/projects/:projectId/wms/:id",
|
|
{
|
|
preHandler: [app.authenticate, app.requireAccount],
|
|
schema: {
|
|
description: "Delete a WM",
|
|
tags: ["WMs"],
|
|
security: [{ bearerAuth: [] }],
|
|
params: {
|
|
type: "object",
|
|
properties: {
|
|
accountId: { type: "string", format: "uuid" },
|
|
projectId: { type: "string", format: "uuid" },
|
|
id: { type: "string", format: "uuid" },
|
|
},
|
|
},
|
|
response: {
|
|
204: {
|
|
description: "WM deleted successfully",
|
|
},
|
|
404: {
|
|
description: "WM not found",
|
|
type: "object",
|
|
properties: { error: { type: "string" } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
async (request, reply) => {
|
|
// Verify project belongs to the account
|
|
const [project] = await db
|
|
.select()
|
|
.from(projects)
|
|
.where(
|
|
and(eq(projects.id, request.params.projectId), eq(projects.accountId, request.accountId)),
|
|
);
|
|
|
|
if (!project) {
|
|
return reply.status(404).send({ error: "Project not found" });
|
|
}
|
|
|
|
const [deleted] = await db
|
|
.delete(wms)
|
|
.where(and(eq(wms.id, request.params.id), eq(wms.projectId, request.params.projectId)))
|
|
.returning();
|
|
|
|
if (!deleted) {
|
|
return reply.status(404).send({ error: "WM not found" });
|
|
}
|
|
|
|
return reply.status(204).send();
|
|
},
|
|
);
|
|
}
|