mirror of
https://github.com/oblien/openship.git
synced 2026-10-02 07:44:35 +08:00
fix(api): allow capacity previews through the startup route scan
This commit is contained in:
@@ -73,7 +73,7 @@ r.get("/resources", { tag: "billing:read", authorizationHandledByOperation: true
|
||||
r.get("/allowances", { tag: "billing:read", authorizationHandledByOperation: true, mcp: { description: "List resources consuming workspace allowances, including the projects holding managed domains." } }, billingLocal.listAllowanceDetail);
|
||||
|
||||
r.get("/capacity", { tag: "billing:read", authorizationHandledByOperation: true, mcp: { description: "Read the Cloud pool's allocated CPU, memory and disk, and authorized project allocations. Includes stopped workspaces. Read this again after an adjustment to verify capacity release." } }, billingLocal.getCapacity);
|
||||
r.post("/capacity/preview", { tag: "billing:read", authorizationHandledByOperation: true, body: BillingOperationSchemas.previewCapacity.input, mcp: { description: "Preview service resource edits and the services that may restart. Requires project and service write access, and the revision from capacity. Does not apply changes." } }, billingLocal.previewCapacity);
|
||||
r.post("/capacity/preview", { tag: "billing:read", readOnly: true, authorizationHandledByOperation: true, body: BillingOperationSchemas.previewCapacity.input, mcp: { description: "Preview service resource edits and the services that may restart. Requires project and service write access, and the revision from capacity. Does not apply changes." } }, billingLocal.previewCapacity);
|
||||
r.post("/capacity/apply", { tag: "billing:write", authorizationHandledByOperation: true, auditHandledByOperation: true, rateLimit: "billing-portal", body: BillingOperationSchemas.applyCapacity.input, mcp: { description: "Apply a reviewed capacity adjustment with explicit restart confirmation. Reuses deployed images through normal deployment logs. Requires project and service write access. Reuse the idempotency key for retries; poll the returned deployment and capacity before retrying a blocked deploy." } }, billingLocal.applyCapacity);
|
||||
|
||||
/* ---------- Top-ups ---------- */
|
||||
|
||||
@@ -71,7 +71,7 @@ r.get(
|
||||
r.get("/usage", { tag: "billing:read", authorizationHandledByOperation: true, mcp: { description: "Read metered Cloud usage over the requested date range, grouped by hour or day. This is billing data, not live workload metrics." }, query: BillingOperationSchemas.getUsage.input }, billingController.getUsage);
|
||||
r.get("/resources", { tag: "billing:read", authorizationHandledByOperation: true, mcp: { description: "List Cloud resources contributing to this workspace’s bill and usage." } }, billingController.getResources);
|
||||
r.get("/capacity", { tag: "billing:read", authorizationHandledByOperation: true, mcp: { description: "Read the Cloud pool's allocated CPU, memory and disk, and authorized project allocations. Includes stopped workspaces. Read this again after an adjustment to verify capacity release." } }, billingController.getCapacity);
|
||||
r.post("/capacity/preview", { tag: "billing:read", authorizationHandledByOperation: true, body: BillingOperationSchemas.previewCapacity.input, mcp: { description: "Preview service resource edits and the services that may restart. Requires project and service write access, and the revision from capacity. Does not apply changes." } }, billingController.previewCapacity);
|
||||
r.post("/capacity/preview", { tag: "billing:read", readOnly: true, authorizationHandledByOperation: true, body: BillingOperationSchemas.previewCapacity.input, mcp: { description: "Preview service resource edits and the services that may restart. Requires project and service write access, and the revision from capacity. Does not apply changes." } }, billingController.previewCapacity);
|
||||
r.post("/capacity/apply", { tag: "billing:write", authorizationHandledByOperation: true, auditHandledByOperation: true, rateLimit: "billing-portal", body: BillingOperationSchemas.applyCapacity.input, mcp: { description: "Apply a reviewed capacity adjustment with explicit restart confirmation. Reuses deployed images through normal deployment logs. Requires project and service write access. Reuse the idempotency key for retries; poll the returned deployment and capacity before retrying a blocked deploy." } }, billingController.applyCapacity);
|
||||
|
||||
/* ---------- Allowance detail ---------- */
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Hono } from "hono";
|
||||
import { afterEach, expect, it, vi } from "vitest";
|
||||
import { enforceRouteScanAtBoot, scanRoutes } from "../../src/lib/route-scanner";
|
||||
import { billingLocalRoutes } from "../../src/modules/billing/billing-local.routes";
|
||||
import { billingPlansRoutes, billingSaasRoutes } from "../../src/modules/billing/billing.routes";
|
||||
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
it.each([
|
||||
["Cloud", billingSaasRoutes],
|
||||
["self-hosted", billingLocalRoutes],
|
||||
] as const)("allows API startup with the real %s billing routes", (_mode, routes) => {
|
||||
const app = new Hono().route("/api/billing", billingPlansRoutes).route("/api/billing", routes);
|
||||
expect(
|
||||
app.routes.some(
|
||||
(route) => route.method === "POST" && route.path === "/api/billing/capacity/preview",
|
||||
),
|
||||
).toBe(true);
|
||||
|
||||
// Exercise the production scanner, not a duplicate rule or mocked route spec.
|
||||
const result = scanRoutes(app);
|
||||
expect(result.errors).toEqual([]);
|
||||
expect(result.ok).toBe(true);
|
||||
|
||||
const exit = vi.spyOn(process, "exit").mockImplementation(() => {
|
||||
throw new Error("The route scanner refused API startup");
|
||||
});
|
||||
vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
enforceRouteScanAtBoot(app);
|
||||
expect(exit).not.toHaveBeenCalled();
|
||||
});
|
||||
Reference in New Issue
Block a user