Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
463
qa/convex-credential-broker/convex/http.ts
Normal file
463
qa/convex-credential-broker/convex/http.ts
Normal file
@@ -0,0 +1,463 @@
|
||||
// Http module supports OpenClaw QA credential workflows.
|
||||
import { httpRouter } from "convex/server";
|
||||
import { internal } from "./_generated/api";
|
||||
import type { Id } from "./_generated/dataModel";
|
||||
import { httpAction } from "./_generated/server";
|
||||
import { normalizeCredentialPayloadForKind } from "./payload-validation";
|
||||
|
||||
type ActorRole = "ci" | "maintainer";
|
||||
|
||||
class BrokerHttpError extends Error {
|
||||
code: string;
|
||||
httpStatus: number;
|
||||
|
||||
constructor(httpStatus: number, code: string, message: string) {
|
||||
super(message);
|
||||
this.name = "BrokerHttpError";
|
||||
this.httpStatus = httpStatus;
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
function jsonResponse(status: number, payload: unknown) {
|
||||
return new Response(JSON.stringify(payload), {
|
||||
status,
|
||||
headers: {
|
||||
"content-type": "application/json; charset=utf-8",
|
||||
"cache-control": "no-store",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function parseBearerToken(request: Request) {
|
||||
const header = request.headers.get("authorization")?.trim();
|
||||
if (!header) {
|
||||
return null;
|
||||
}
|
||||
const [scheme, token] = header.split(/\s+/u, 2);
|
||||
if (scheme?.toLowerCase() !== "bearer" || !token) {
|
||||
return null;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
function resolveAuthRole(token: string | null): ActorRole {
|
||||
if (!token) {
|
||||
throw new BrokerHttpError(
|
||||
401,
|
||||
"AUTH_REQUIRED",
|
||||
"Missing Authorization: Bearer <secret> header.",
|
||||
);
|
||||
}
|
||||
const maintainerSecret = process.env.OPENCLAW_QA_CONVEX_SECRET_MAINTAINER?.trim();
|
||||
const ciSecret = process.env.OPENCLAW_QA_CONVEX_SECRET_CI?.trim();
|
||||
|
||||
if (!maintainerSecret && !ciSecret) {
|
||||
throw new BrokerHttpError(
|
||||
500,
|
||||
"SERVER_MISCONFIGURED",
|
||||
"No Convex broker role secrets are configured on this deployment.",
|
||||
);
|
||||
}
|
||||
if (maintainerSecret && token === maintainerSecret) {
|
||||
return "maintainer";
|
||||
}
|
||||
if (ciSecret && token === ciSecret) {
|
||||
return "ci";
|
||||
}
|
||||
throw new BrokerHttpError(401, "AUTH_INVALID", "Credential broker secret is invalid.");
|
||||
}
|
||||
|
||||
function assertMaintainerAdminAuth(token: string | null) {
|
||||
if (!token) {
|
||||
throw new BrokerHttpError(
|
||||
401,
|
||||
"AUTH_REQUIRED",
|
||||
"Missing Authorization: Bearer <secret> header.",
|
||||
);
|
||||
}
|
||||
const maintainerSecret = process.env.OPENCLAW_QA_CONVEX_SECRET_MAINTAINER?.trim();
|
||||
if (!maintainerSecret) {
|
||||
throw new BrokerHttpError(
|
||||
500,
|
||||
"SERVER_MISCONFIGURED",
|
||||
"Admin endpoints require OPENCLAW_QA_CONVEX_SECRET_MAINTAINER on this deployment.",
|
||||
);
|
||||
}
|
||||
if (token === maintainerSecret) {
|
||||
return;
|
||||
}
|
||||
const ciSecret = process.env.OPENCLAW_QA_CONVEX_SECRET_CI?.trim();
|
||||
if (ciSecret && token === ciSecret) {
|
||||
throw new BrokerHttpError(
|
||||
403,
|
||||
"AUTH_ROLE_MISMATCH",
|
||||
"Admin endpoints require maintainer credentials.",
|
||||
);
|
||||
}
|
||||
throw new BrokerHttpError(401, "AUTH_INVALID", "Credential broker secret is invalid.");
|
||||
}
|
||||
|
||||
function asObject(value: unknown) {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
return null;
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
async function parseJsonObject(request: Request) {
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = await request.json();
|
||||
} catch {
|
||||
throw new BrokerHttpError(400, "INVALID_JSON", "Request body must be valid JSON.");
|
||||
}
|
||||
const body = asObject(parsed);
|
||||
if (!body) {
|
||||
throw new BrokerHttpError(400, "INVALID_BODY", "Request body must be a JSON object.");
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
function requireString(body: Record<string, unknown>, key: string) {
|
||||
const raw = body[key];
|
||||
if (typeof raw !== "string") {
|
||||
throw new BrokerHttpError(400, "INVALID_BODY", `Expected "${key}" to be a string.`);
|
||||
}
|
||||
const value = raw.trim();
|
||||
if (!value) {
|
||||
throw new BrokerHttpError(400, "INVALID_BODY", `Expected "${key}" to be non-empty.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function optionalString(body: Record<string, unknown>, key: string) {
|
||||
if (!(key in body) || body[key] === undefined || body[key] === null) {
|
||||
return undefined;
|
||||
}
|
||||
const raw = body[key];
|
||||
if (typeof raw !== "string") {
|
||||
throw new BrokerHttpError(400, "INVALID_BODY", `Expected "${key}" to be a string.`);
|
||||
}
|
||||
const value = raw.trim();
|
||||
return value.length > 0 ? value : undefined;
|
||||
}
|
||||
|
||||
function requireObject(body: Record<string, unknown>, key: string) {
|
||||
const raw = body[key];
|
||||
const parsed = asObject(raw);
|
||||
if (!parsed) {
|
||||
throw new BrokerHttpError(400, "INVALID_BODY", `Expected "${key}" to be a JSON object.`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function optionalPositiveInteger(body: Record<string, unknown>, key: string) {
|
||||
if (!(key in body) || body[key] === undefined || body[key] === null) {
|
||||
return undefined;
|
||||
}
|
||||
const raw = body[key];
|
||||
if (typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw) || raw < 1) {
|
||||
throw new BrokerHttpError(400, "INVALID_BODY", `Expected "${key}" to be a positive integer.`);
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
function optionalNonnegativeInteger(body: Record<string, unknown>, key: string) {
|
||||
if (!(key in body) || body[key] === undefined || body[key] === null) {
|
||||
return undefined;
|
||||
}
|
||||
const raw = body[key];
|
||||
if (typeof raw !== "number" || !Number.isFinite(raw) || !Number.isInteger(raw) || raw < 0) {
|
||||
throw new BrokerHttpError(
|
||||
400,
|
||||
"INVALID_BODY",
|
||||
`Expected "${key}" to be a non-negative integer.`,
|
||||
);
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
function optionalBoolean(body: Record<string, unknown>, key: string) {
|
||||
if (!(key in body) || body[key] === undefined || body[key] === null) {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof body[key] !== "boolean") {
|
||||
throw new BrokerHttpError(400, "INVALID_BODY", `Expected "${key}" to be a boolean.`);
|
||||
}
|
||||
return body[key];
|
||||
}
|
||||
|
||||
function optionalCredentialStatus(body: Record<string, unknown>, key: string) {
|
||||
const value = optionalString(body, key);
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
if (value !== "active" && value !== "disabled") {
|
||||
throw new BrokerHttpError(
|
||||
400,
|
||||
"INVALID_BODY",
|
||||
`Expected "${key}" to be "active" or "disabled".`,
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function optionalListStatus(body: Record<string, unknown>, key: string) {
|
||||
const value = optionalString(body, key);
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
if (value !== "active" && value !== "disabled" && value !== "all") {
|
||||
throw new BrokerHttpError(
|
||||
400,
|
||||
"INVALID_BODY",
|
||||
`Expected "${key}" to be "active", "disabled", or "all".`,
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseActorRole(body: Record<string, unknown>) {
|
||||
const actorRole = requireString(body, "actorRole");
|
||||
if (actorRole !== "ci" && actorRole !== "maintainer") {
|
||||
throw new BrokerHttpError(
|
||||
400,
|
||||
"INVALID_ACTOR_ROLE",
|
||||
'Expected "actorRole" to be "maintainer" or "ci".',
|
||||
);
|
||||
}
|
||||
return actorRole as ActorRole;
|
||||
}
|
||||
|
||||
function assertRoleAllowed(tokenRole: ActorRole, requestedRole: ActorRole) {
|
||||
if (tokenRole !== requestedRole) {
|
||||
throw new BrokerHttpError(
|
||||
403,
|
||||
"AUTH_ROLE_MISMATCH",
|
||||
`Secret role "${tokenRole}" cannot be used as actorRole "${requestedRole}".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeCredentialId(raw: string) {
|
||||
// Convex Ids are opaque strings. We only enforce non-empty shape at HTTP boundary.
|
||||
return raw;
|
||||
}
|
||||
|
||||
function normalizeError(error: unknown) {
|
||||
if (error instanceof BrokerHttpError) {
|
||||
return {
|
||||
httpStatus: error.httpStatus,
|
||||
payload: {
|
||||
status: "error",
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
return {
|
||||
httpStatus: 500,
|
||||
payload: {
|
||||
status: "error",
|
||||
code: "INTERNAL_ERROR",
|
||||
message: error.message || "Internal credential broker error.",
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
httpStatus: 500,
|
||||
payload: {
|
||||
status: "error",
|
||||
code: "INTERNAL_ERROR",
|
||||
message: "Internal credential broker error.",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const http = httpRouter();
|
||||
|
||||
http.route({
|
||||
path: "/qa-credentials/v1/acquire",
|
||||
method: "POST",
|
||||
handler: httpAction(async (ctx, request) => {
|
||||
try {
|
||||
const tokenRole = resolveAuthRole(parseBearerToken(request));
|
||||
const body = await parseJsonObject(request);
|
||||
const actorRole = parseActorRole(body);
|
||||
assertRoleAllowed(tokenRole, actorRole);
|
||||
|
||||
const result = await ctx.runMutation(internal.credentials.acquireLease, {
|
||||
kind: requireString(body, "kind"),
|
||||
ownerId: requireString(body, "ownerId"),
|
||||
actorRole,
|
||||
leaseTtlMs: optionalPositiveInteger(body, "leaseTtlMs"),
|
||||
heartbeatIntervalMs: optionalPositiveInteger(body, "heartbeatIntervalMs"),
|
||||
});
|
||||
|
||||
return jsonResponse(200, result);
|
||||
} catch (error) {
|
||||
const normalized = normalizeError(error);
|
||||
return jsonResponse(normalized.httpStatus, normalized.payload);
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
http.route({
|
||||
path: "/qa-credentials/v1/heartbeat",
|
||||
method: "POST",
|
||||
handler: httpAction(async (ctx, request) => {
|
||||
try {
|
||||
const tokenRole = resolveAuthRole(parseBearerToken(request));
|
||||
const body = await parseJsonObject(request);
|
||||
const actorRole = parseActorRole(body);
|
||||
assertRoleAllowed(tokenRole, actorRole);
|
||||
|
||||
const result = await ctx.runMutation(internal.credentials.heartbeatLease, {
|
||||
kind: requireString(body, "kind"),
|
||||
ownerId: requireString(body, "ownerId"),
|
||||
actorRole,
|
||||
credentialId: normalizeCredentialId(
|
||||
requireString(body, "credentialId"),
|
||||
) as Id<"credential_sets">,
|
||||
leaseToken: requireString(body, "leaseToken"),
|
||||
leaseTtlMs: optionalPositiveInteger(body, "leaseTtlMs"),
|
||||
});
|
||||
|
||||
return jsonResponse(200, result);
|
||||
} catch (error) {
|
||||
const normalized = normalizeError(error);
|
||||
return jsonResponse(normalized.httpStatus, normalized.payload);
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
http.route({
|
||||
path: "/qa-credentials/v1/payload-chunk",
|
||||
method: "POST",
|
||||
handler: httpAction(async (ctx, request) => {
|
||||
try {
|
||||
const tokenRole = resolveAuthRole(parseBearerToken(request));
|
||||
const body = await parseJsonObject(request);
|
||||
const actorRole = parseActorRole(body);
|
||||
assertRoleAllowed(tokenRole, actorRole);
|
||||
|
||||
const result = await ctx.runQuery(internal.credentials.getPayloadChunk, {
|
||||
kind: requireString(body, "kind"),
|
||||
ownerId: requireString(body, "ownerId"),
|
||||
actorRole,
|
||||
credentialId: normalizeCredentialId(
|
||||
requireString(body, "credentialId"),
|
||||
) as Id<"credential_sets">,
|
||||
leaseToken: requireString(body, "leaseToken"),
|
||||
index: optionalNonnegativeInteger(body, "index") ?? 0,
|
||||
});
|
||||
|
||||
return jsonResponse(200, result);
|
||||
} catch (error) {
|
||||
const normalized = normalizeError(error);
|
||||
return jsonResponse(normalized.httpStatus, normalized.payload);
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
http.route({
|
||||
path: "/qa-credentials/v1/release",
|
||||
method: "POST",
|
||||
handler: httpAction(async (ctx, request) => {
|
||||
try {
|
||||
const tokenRole = resolveAuthRole(parseBearerToken(request));
|
||||
const body = await parseJsonObject(request);
|
||||
const actorRole = parseActorRole(body);
|
||||
assertRoleAllowed(tokenRole, actorRole);
|
||||
|
||||
const result = await ctx.runMutation(internal.credentials.releaseLease, {
|
||||
kind: requireString(body, "kind"),
|
||||
ownerId: requireString(body, "ownerId"),
|
||||
actorRole,
|
||||
credentialId: normalizeCredentialId(
|
||||
requireString(body, "credentialId"),
|
||||
) as Id<"credential_sets">,
|
||||
leaseToken: requireString(body, "leaseToken"),
|
||||
});
|
||||
|
||||
return jsonResponse(200, result);
|
||||
} catch (error) {
|
||||
const normalized = normalizeError(error);
|
||||
return jsonResponse(normalized.httpStatus, normalized.payload);
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
http.route({
|
||||
path: "/qa-credentials/v1/admin/add",
|
||||
method: "POST",
|
||||
handler: httpAction(async (ctx, request) => {
|
||||
try {
|
||||
assertMaintainerAdminAuth(parseBearerToken(request));
|
||||
const body = await parseJsonObject(request);
|
||||
const kind = requireString(body, "kind");
|
||||
const payload = normalizeCredentialPayloadForKind(
|
||||
kind,
|
||||
requireObject(body, "payload"),
|
||||
(httpStatus, code, message) => new BrokerHttpError(httpStatus, code, message),
|
||||
);
|
||||
const result = await ctx.runMutation(internal.credentials.addCredentialSet, {
|
||||
kind,
|
||||
payload,
|
||||
note: optionalString(body, "note"),
|
||||
actorId: optionalString(body, "actorId"),
|
||||
status: optionalCredentialStatus(body, "status"),
|
||||
});
|
||||
return jsonResponse(200, result);
|
||||
} catch (error) {
|
||||
const normalized = normalizeError(error);
|
||||
return jsonResponse(normalized.httpStatus, normalized.payload);
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
http.route({
|
||||
path: "/qa-credentials/v1/admin/remove",
|
||||
method: "POST",
|
||||
handler: httpAction(async (ctx, request) => {
|
||||
try {
|
||||
assertMaintainerAdminAuth(parseBearerToken(request));
|
||||
const body = await parseJsonObject(request);
|
||||
const result = await ctx.runMutation(internal.credentials.disableCredentialSet, {
|
||||
credentialId: normalizeCredentialId(
|
||||
requireString(body, "credentialId"),
|
||||
) as Id<"credential_sets">,
|
||||
actorId: optionalString(body, "actorId"),
|
||||
});
|
||||
return jsonResponse(200, result);
|
||||
} catch (error) {
|
||||
const normalized = normalizeError(error);
|
||||
return jsonResponse(normalized.httpStatus, normalized.payload);
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
http.route({
|
||||
path: "/qa-credentials/v1/admin/list",
|
||||
method: "POST",
|
||||
handler: httpAction(async (ctx, request) => {
|
||||
try {
|
||||
assertMaintainerAdminAuth(parseBearerToken(request));
|
||||
const body = await parseJsonObject(request);
|
||||
const result = await ctx.runQuery(internal.credentials.listCredentialSets, {
|
||||
kind: optionalString(body, "kind"),
|
||||
status: optionalListStatus(body, "status"),
|
||||
includePayload: optionalBoolean(body, "includePayload"),
|
||||
limit: optionalPositiveInteger(body, "limit"),
|
||||
});
|
||||
return jsonResponse(200, result);
|
||||
} catch (error) {
|
||||
const normalized = normalizeError(error);
|
||||
return jsonResponse(normalized.httpStatus, normalized.payload);
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
export default http;
|
||||
Reference in New Issue
Block a user