Files
pikasTech-HWLAB/internal/cloud/server-skills-http.ts
T
2026-05-31 11:45:10 +08:00

76 lines
2.6 KiB
TypeScript

import { CLOUD_API_SERVICE_ID } from "../audit/index.mjs";
import { getHeader, parsePositiveInteger, readBody, sendJson } from "./server-http-utils.ts";
import {
getSkillTree,
listSkills,
readSkillTextFile,
uploadUserSkill
} from "./skills-store.ts";
const DEFAULT_SKILL_UPLOAD_BODY_LIMIT_BYTES = 16 * 1024 * 1024;
export async function handleSkillsHttp(request, response, url, options = {}) {
try {
if (request.method === "GET" && url.pathname === "/v1/skills") {
sendJson(response, 200, await listSkills({ env: options.env ?? process.env }));
return;
}
if (request.method === "POST" && url.pathname === "/v1/skills/uploads") {
const payload = await readJsonBody(request, skillUploadBodyLimit(options.env));
sendJson(response, 201, await uploadUserSkill(payload, { env: options.env ?? process.env }));
return;
}
const treeMatch = url.pathname.match(/^\/v1\/skills\/([^/]+)\/tree$/u);
if (request.method === "GET" && treeMatch) {
sendJson(response, 200, await getSkillTree(decodeURIComponent(treeMatch[1]), { env: options.env ?? process.env }));
return;
}
const fileMatch = url.pathname.match(/^\/v1\/skills\/([^/]+)\/files$/u);
if (request.method === "GET" && fileMatch) {
sendJson(
response,
200,
await readSkillTextFile(decodeURIComponent(fileMatch[1]), url.searchParams.get("path") || "SKILL.md", { env: options.env ?? process.env })
);
return;
}
sendJson(response, 404, skillsErrorPayload(404, "skill_route_not_found", "Skill route is not implemented.", request));
} catch (error) {
const statusCode = Number.isInteger(error?.statusCode) ? error.statusCode : 500;
sendJson(response, statusCode, skillsErrorPayload(statusCode, error?.code || "skill_request_failed", error?.message || "Skill request failed.", request));
}
}
async function readJsonBody(request, limitBytes) {
const body = await readBody(request, limitBytes);
try {
return body ? JSON.parse(body) : {};
} catch (error) {
const wrapped = new Error(`Invalid JSON body: ${error.message}`);
Object.assign(wrapped, { statusCode: 400, code: "parse_error" });
throw wrapped;
}
}
function skillUploadBodyLimit(env = process.env) {
return parsePositiveInteger(env?.HWLAB_SKILL_UPLOAD_BODY_LIMIT_BYTES, DEFAULT_SKILL_UPLOAD_BODY_LIMIT_BYTES);
}
function skillsErrorPayload(statusCode, code, message, request) {
return {
serviceId: CLOUD_API_SERVICE_ID,
route: request?.url || "/v1/skills",
status: "failed",
error: {
code,
message,
httpStatus: statusCode,
traceId: getHeader(request, "x-trace-id") || null
}
};
}