feat(device-core): add authorized project read model

This commit is contained in:
Codex
2026-08-10 20:10:29 +03:00
parent 43dc9b1f45
commit f5d7916338
7 changed files with 629 additions and 3 deletions
@@ -149,6 +149,61 @@ export function createControlCoreApp({
});
}
if (
request.method === "GET"
&& requestUrl.pathname === "/internal/v1/query/projects"
) {
if (!managementApiEnabled) {
return writeJson(response, 404, {
ok: false,
error: "device_management_api_disabled",
});
}
if (!matchesBearer(request.headers.authorization, managementToken)) {
return writeJson(response, 401, {
ok: false,
error: "device_management_auth_required",
});
}
if (typeof repository.listAccessibleProjects !== "function") {
return writeJson(response, 503, {
ok: false,
error: "device_query_repository_unavailable",
});
}
const actor = managementActorFromHeaders(request.headers);
const projects = await repository.listAccessibleProjects(actor);
return writeJson(response, 200, { ok: true, projects });
}
const workspaceProjectId = projectWorkspaceId(requestUrl.pathname);
if (request.method === "GET" && workspaceProjectId) {
if (!managementApiEnabled) {
return writeJson(response, 404, {
ok: false,
error: "device_management_api_disabled",
});
}
if (!matchesBearer(request.headers.authorization, managementToken)) {
return writeJson(response, 401, {
ok: false,
error: "device_management_auth_required",
});
}
if (typeof repository.getProjectWorkspace !== "function") {
return writeJson(response, 503, {
ok: false,
error: "device_query_repository_unavailable",
});
}
const actor = managementActorFromHeaders(request.headers);
const workspace = await repository.getProjectWorkspace(
actor,
workspaceProjectId,
);
return writeJson(response, 200, { ok: true, workspace });
}
if (
request.method === "POST"
&& requestUrl.pathname === "/internal/v1/device-discoveries:observe"
@@ -208,6 +263,13 @@ export function createControlCoreApp({
return server;
}
function projectWorkspaceId(pathname) {
const match = pathname.match(
/^\/internal\/v1\/query\/projects\/([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\/workspace$/i,
);
return match?.[1]?.toLowerCase() ?? null;
}
function managementActorFromHeaders(headers) {
return normalizeManagementActor({
userRef: singleHeader(headers["x-nodedc-user-ref"]),