mirror of
https://origin.cursor.com/mrdevmx/panels.git
synced 2026-10-09 21:43:17 +00:00
- Add IAM v2 auth with role_id, is_owner, status checks, and API key without tenant_admin bypass - Enforce CRUD permissions and project/warehouse scope across API routes - Rewrite usuarios.vue with ribbon, tree, user/role management, and permission matrix - Add dynamic menu, route guards, usePermissions/useScope composables - Apply role templates on create; filter project docs by category; hide cost tab without permission - Add iam_test unit tests; update bootstrap and SaaS tenant admin to use role_id Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
64 lines
2.4 KiB
TypeScript
64 lines
2.4 KiB
TypeScript
import type { Hono } from "hono";
|
|
import type { AuthUser } from "./auth.ts";
|
|
import { tenantScope } from "./auth.ts";
|
|
import { requireCoreAuth } from "./scope.ts";
|
|
import type { Db } from "./db.ts";
|
|
import { callCoreFn } from "./rpc.ts";
|
|
import { respondRpc, routeLabel } from "./http_errors.ts";
|
|
import { requirePermission } from "./permissions.ts";
|
|
import { denyUnlessProjectScope } from "./scope_enforcement.ts";
|
|
|
|
type App = Hono<{ Variables: { user: AuthUser; db: Db } }>;
|
|
|
|
function tid(c: { get: (k: "user") => AuthUser }): number {
|
|
return tenantScope(c.get("user")) ?? 0;
|
|
}
|
|
|
|
export function registerCostControlRoutes(app: App) {
|
|
const auth = [...requireCoreAuth, requirePermission("cost_control.view")] as const;
|
|
|
|
app.get("/v1/projects/:id/cost-control/summary", ...auth, async (c) => {
|
|
const projectId = Number(c.req.param("id"));
|
|
if (!await denyUnlessProjectScope(c, projectId)) return;
|
|
const db = c.get("db");
|
|
const env = await callCoreFn(db, "core.fn_cost_control_project_summary", {
|
|
tenant_id: tid(c),
|
|
project_id: projectId,
|
|
}, { route: routeLabel(c) });
|
|
return respondRpc(c, env);
|
|
});
|
|
|
|
app.get("/v1/projects/:id/cost-control/items", ...auth, async (c) => {
|
|
const projectId = Number(c.req.param("id"));
|
|
if (!await denyUnlessProjectScope(c, projectId)) return;
|
|
const db = c.get("db");
|
|
const env = await callCoreFn(db, "core.fn_cost_control_by_item", {
|
|
tenant_id: tid(c),
|
|
project_id: projectId,
|
|
}, { route: routeLabel(c) });
|
|
return respondRpc(c, env);
|
|
});
|
|
|
|
app.get("/v1/projects/:id/cost-control/chapters", ...auth, async (c) => {
|
|
const projectId = Number(c.req.param("id"));
|
|
if (!await denyUnlessProjectScope(c, projectId)) return;
|
|
const db = c.get("db");
|
|
const env = await callCoreFn(db, "core.fn_cost_control_by_chapter", {
|
|
tenant_id: tid(c),
|
|
project_id: projectId,
|
|
}, { route: routeLabel(c) });
|
|
return respondRpc(c, env);
|
|
});
|
|
|
|
app.get("/v1/projects/:id/cost-control/deviations", ...auth, async (c) => {
|
|
const projectId = Number(c.req.param("id"));
|
|
if (!await denyUnlessProjectScope(c, projectId)) return;
|
|
const db = c.get("db");
|
|
const env = await callCoreFn(db, "core.fn_cost_control_top_deviations", {
|
|
tenant_id: tid(c),
|
|
project_id: projectId,
|
|
limit: c.req.query("limit"),
|
|
}, { route: routeLabel(c) });
|
|
return respondRpc(c, env);
|
|
});
|
|
}
|