feat: some shit
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m4s
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m4s
This commit is contained in:
@@ -3,280 +3,360 @@ import { HTTPException } from "hono/http-exception";
|
||||
import { idRegex, NodeDefinitionSchema } from "./validations/types.ts";
|
||||
import * as service from "./node.service.ts";
|
||||
import { bodyLimit } from "hono/body-limit";
|
||||
import { ZodSchema } from "zod";
|
||||
import { CustomError } from "./errors.ts";
|
||||
|
||||
const nodeRouter = new OpenAPIHono();
|
||||
|
||||
const SingleParam = (name: string) =>
|
||||
const createParamSchema = (name: string) =>
|
||||
z
|
||||
.string()
|
||||
.min(3)
|
||||
.max(20)
|
||||
.refine(
|
||||
(value) => idRegex.test(value),
|
||||
`${name} should contain only letters, numbers, "-" or "_"`,
|
||||
`${name} must contain only letters, numbers, "-", or "_"`,
|
||||
)
|
||||
.openapi({ param: { name, in: "path" } });
|
||||
|
||||
const ParamsSchema = z.object({
|
||||
user: SingleParam("user"),
|
||||
system: SingleParam("system"),
|
||||
nodeId: SingleParam("nodeId"),
|
||||
});
|
||||
|
||||
const getUserNodesRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{user}.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: SingleParam("user").optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.array(NodeDefinitionSchema),
|
||||
},
|
||||
},
|
||||
description: "Retrieve a single node definition",
|
||||
},
|
||||
},
|
||||
});
|
||||
nodeRouter.openapi(getUserNodesRoute, async (c) => {
|
||||
const userId = c.req.param("user.json").replace(/\.json$/, "");
|
||||
const nodes = await service.getNodeDefinitionsByUser(
|
||||
userId,
|
||||
);
|
||||
return c.json(nodes);
|
||||
});
|
||||
|
||||
const getNodeCollectionRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: SingleParam("user"),
|
||||
system: SingleParam("system").optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.array(NodeDefinitionSchema),
|
||||
},
|
||||
},
|
||||
description: "Retrieve a single node definition",
|
||||
},
|
||||
},
|
||||
});
|
||||
nodeRouter.openapi(getNodeCollectionRoute, async (c) => {
|
||||
const { user } = c.req.valid("param");
|
||||
const nodeSystemId = c.req.param("system.json").replace(/\.json$/, "");
|
||||
|
||||
const nodes = await service.getNodesBySystem(user, nodeSystemId);
|
||||
return c.json(nodes);
|
||||
});
|
||||
|
||||
const getNodeDefinitionRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: SingleParam("user"),
|
||||
system: SingleParam("system"),
|
||||
nodeId: SingleParam("nodeId").optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: NodeDefinitionSchema,
|
||||
},
|
||||
},
|
||||
description: "Retrieve a single node definition",
|
||||
},
|
||||
},
|
||||
});
|
||||
nodeRouter.openapi(getNodeDefinitionRoute, async (c) => {
|
||||
const { user, system } = c.req.valid("param");
|
||||
const nodeId = c.req.param("nodeId.json").replace(/\.json$/, "");
|
||||
|
||||
const node = await service.getNodeDefinitionById(
|
||||
user,
|
||||
system,
|
||||
nodeId,
|
||||
);
|
||||
|
||||
if (!node) {
|
||||
throw new HTTPException(404);
|
||||
}
|
||||
|
||||
return c.json(node);
|
||||
});
|
||||
|
||||
const getNodeWasmRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}.wasm",
|
||||
request: {
|
||||
params: ParamsSchema,
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/wasm": {
|
||||
schema: z.any(),
|
||||
},
|
||||
},
|
||||
description: "Retrieve a single node",
|
||||
},
|
||||
},
|
||||
});
|
||||
nodeRouter.openapi(getNodeWasmRoute, async (c) => {
|
||||
const { user, system, nodeId } = c.req.valid("param");
|
||||
|
||||
const wasmContent = await service.getNodeWasmById(
|
||||
user,
|
||||
system,
|
||||
nodeId.replace(/\.wasm/, ""),
|
||||
);
|
||||
|
||||
c.header("Content-Type", "application/wasm");
|
||||
|
||||
return c.body(wasmContent);
|
||||
});
|
||||
|
||||
const getNodeVersionWasmRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}@{hash}.wasm",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: SingleParam("user"),
|
||||
system: SingleParam("system"),
|
||||
nodeId: SingleParam("nodeId"),
|
||||
hash: SingleParam("hash"),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/wasm": {
|
||||
schema: z.any(),
|
||||
},
|
||||
},
|
||||
description: "Create a single node",
|
||||
},
|
||||
const createResponseSchema = <T extends ZodSchema>(
|
||||
description: string,
|
||||
schema: T,
|
||||
) => ({
|
||||
200: {
|
||||
content: { "application/json": { schema } },
|
||||
description,
|
||||
},
|
||||
});
|
||||
|
||||
nodeRouter.openapi(getNodeVersionWasmRoute, async (c) => {
|
||||
const { user, system, nodeId, hash } = c.req.valid("param");
|
||||
|
||||
const nodes = await service.getNodeVersionWasm(user, system, nodeId, hash);
|
||||
|
||||
return c.json(nodes);
|
||||
});
|
||||
|
||||
const getNodeVersionRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}@{hash}.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: SingleParam("user"),
|
||||
system: SingleParam("system"),
|
||||
nodeId: SingleParam("nodeId"),
|
||||
hash: SingleParam("hash"),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: NodeDefinitionSchema,
|
||||
},
|
||||
},
|
||||
description: "Create a single node",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
nodeRouter.openapi(getNodeVersionRoute, async (c) => {
|
||||
const { user, system, nodeId, hash } = c.req.valid("param");
|
||||
|
||||
const nodes = await service.getNodeVersion(user, system, nodeId, hash);
|
||||
|
||||
return c.json(nodes);
|
||||
});
|
||||
|
||||
const getNodeVersionsRoute = createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}/versions.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: SingleParam("user"),
|
||||
system: SingleParam("system"),
|
||||
nodeId: SingleParam("nodeId"),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: z.array(NodeDefinitionSchema),
|
||||
},
|
||||
},
|
||||
description: "Create a single node",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
nodeRouter.openapi(getNodeVersionsRoute, async (c) => {
|
||||
const { user, system, nodeId } = c.req.valid("param");
|
||||
|
||||
const node = await service.getNodeVersions(user, system, nodeId);
|
||||
|
||||
return c.json(node);
|
||||
});
|
||||
|
||||
const createNodeRoute = createRoute({
|
||||
method: "post",
|
||||
path: "/",
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: NodeDefinitionSchema,
|
||||
},
|
||||
},
|
||||
description: "Create a single node",
|
||||
},
|
||||
},
|
||||
middleware: [
|
||||
bodyLimit({
|
||||
maxSize: 128 * 1024, // 128kb
|
||||
onError: (c) => {
|
||||
return c.text("Node content too large", 413);
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
nodeRouter.openapi(createNodeRoute, async (c) => {
|
||||
const buffer = await c.req.arrayBuffer();
|
||||
const bytes = await (await c.req.blob()).bytes();
|
||||
|
||||
try {
|
||||
const node = await service.createNode(buffer, bytes);
|
||||
return c.json(node);
|
||||
} catch (error) {
|
||||
if (error instanceof Error && "code" in error) {
|
||||
switch (error.code) {
|
||||
case "23505":
|
||||
throw new HTTPException(409, { message: "node already exists" });
|
||||
async function getNodeByVersion(
|
||||
user: string,
|
||||
system: string,
|
||||
nodeId: string,
|
||||
hash?: string,
|
||||
) {
|
||||
console.log("Get Node by Version", { user, system, nodeId, hash });
|
||||
if (hash) {
|
||||
if (nodeId.includes("wasm")) {
|
||||
return await service.getNodeVersionWasm(
|
||||
user,
|
||||
system,
|
||||
nodeId.replace(".wasm", ""),
|
||||
hash,
|
||||
);
|
||||
} else {
|
||||
const wasmContent = await service.getNodeVersion(
|
||||
user,
|
||||
system,
|
||||
nodeId,
|
||||
hash,
|
||||
);
|
||||
return wasmContent;
|
||||
}
|
||||
} else {
|
||||
if (nodeId.includes(".wasm")) {
|
||||
const [id, version] = nodeId.replace(/\.wasm$/, "").split("@");
|
||||
console.log({ user, system, id, version });
|
||||
if (version) {
|
||||
return service.getNodeVersionWasm(
|
||||
user,
|
||||
system,
|
||||
id,
|
||||
version,
|
||||
);
|
||||
} else {
|
||||
return service.getNodeWasmById(
|
||||
user,
|
||||
system,
|
||||
id,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const [id, version] = nodeId.replace(/\.json$/, "").split("@");
|
||||
if (!version) {
|
||||
return service.getNodeDefinitionById(
|
||||
user,
|
||||
system,
|
||||
id,
|
||||
);
|
||||
} else {
|
||||
return await service.getNodeVersion(
|
||||
user,
|
||||
system,
|
||||
id,
|
||||
version,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new HTTPException(500);
|
||||
});
|
||||
}
|
||||
|
||||
nodeRouter.openapi(
|
||||
createRoute({
|
||||
method: "post",
|
||||
path: "/",
|
||||
responses: createResponseSchema(
|
||||
"Create a single node",
|
||||
NodeDefinitionSchema,
|
||||
),
|
||||
middleware: [
|
||||
bodyLimit({
|
||||
maxSize: 128 * 1024, // 128 KB
|
||||
onError: (c) => c.text("Node content too large", 413),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
async (c) => {
|
||||
const buffer = await c.req.arrayBuffer();
|
||||
const bytes = new Uint8Array(buffer);
|
||||
try {
|
||||
const node = await service.createNode(buffer, bytes);
|
||||
return c.json(node);
|
||||
} catch (error) {
|
||||
if (error instanceof CustomError) {
|
||||
throw new HTTPException(error.status, { message: error.message });
|
||||
}
|
||||
throw new HTTPException(500, { message: "Internal server error" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
nodeRouter.openapi(
|
||||
createRoute({
|
||||
method: "get",
|
||||
path: "/{user}.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: createParamSchema("user").optional(),
|
||||
}),
|
||||
},
|
||||
responses: createResponseSchema(
|
||||
"Retrieve nodes for a user",
|
||||
z.array(NodeDefinitionSchema),
|
||||
),
|
||||
}),
|
||||
async (c) => {
|
||||
const user = c.req.param("user.json").replace(/\.json$/, "");
|
||||
try {
|
||||
const nodes = await service.getNodeDefinitionsByUser(user);
|
||||
return c.json(nodes);
|
||||
} catch (error) {
|
||||
if (error instanceof CustomError) {
|
||||
throw new HTTPException(error.status, { message: error.message });
|
||||
}
|
||||
throw new HTTPException(500, { message: "Internal server error" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
nodeRouter.openapi(
|
||||
createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: createParamSchema("user"),
|
||||
system: createParamSchema("system").optional(),
|
||||
}),
|
||||
},
|
||||
responses: createResponseSchema(
|
||||
"Retrieve nodes for a system",
|
||||
z.array(NodeDefinitionSchema),
|
||||
),
|
||||
}),
|
||||
async (c) => {
|
||||
const { user } = c.req.valid("param");
|
||||
const system = c.req.param("system.json").replace(/\.json$/, "");
|
||||
console.log("Get Nodes by System", { user, system });
|
||||
try {
|
||||
const nodes = await service.getNodesBySystem(user, system);
|
||||
return c.json(nodes);
|
||||
} catch (error) {
|
||||
if (error instanceof CustomError) {
|
||||
throw new HTTPException(error.status, { message: error.message });
|
||||
}
|
||||
throw new HTTPException(500, { message: "Internal server error" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
nodeRouter.openapi(
|
||||
createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: createParamSchema("user"),
|
||||
system: createParamSchema("system"),
|
||||
nodeId: createParamSchema("nodeId").optional(),
|
||||
}),
|
||||
},
|
||||
responses: createResponseSchema(
|
||||
"Retrieve a single node definition",
|
||||
NodeDefinitionSchema,
|
||||
),
|
||||
}),
|
||||
async (c) => {
|
||||
const { user, system } = c.req.valid("param");
|
||||
const nodeId = c.req.param("nodeId.json").replace(/\.json$/, "");
|
||||
console.log("Get Node by Id", { user, system, nodeId });
|
||||
try {
|
||||
const node = await service.getNodeDefinitionById(user, system, nodeId);
|
||||
return c.json(node);
|
||||
} catch (error) {
|
||||
if (error instanceof CustomError) {
|
||||
throw new HTTPException(error.status, { message: error.message });
|
||||
}
|
||||
throw new HTTPException(500, { message: "Internal server error" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
nodeRouter.openapi(
|
||||
createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}@{version}.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: createParamSchema("user"),
|
||||
system: createParamSchema("system"),
|
||||
nodeId: createParamSchema("nodeId"),
|
||||
version: createParamSchema("version").optional(),
|
||||
}),
|
||||
},
|
||||
responses: createResponseSchema(
|
||||
"Retrieve a single node definition",
|
||||
NodeDefinitionSchema,
|
||||
),
|
||||
}),
|
||||
async (c) => {
|
||||
const { user, system, nodeId } = c.req.valid("param");
|
||||
const hash = c.req.param("version.json");
|
||||
try {
|
||||
const res = await getNodeByVersion(user, system, nodeId, hash);
|
||||
if (res instanceof ArrayBuffer) {
|
||||
c.header("Content-Type", "application/wasm");
|
||||
return c.body(res);
|
||||
} else {
|
||||
return c.json(res);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof CustomError) {
|
||||
throw new HTTPException(error.status, { message: error.message });
|
||||
}
|
||||
throw new HTTPException(500, { message: "Internal server error" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
nodeRouter.openapi(
|
||||
createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}/versions.json",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: createParamSchema("user"),
|
||||
system: createParamSchema("system"),
|
||||
nodeId: createParamSchema("nodeId"),
|
||||
}),
|
||||
},
|
||||
responses: createResponseSchema(
|
||||
"Retrieve a single node definition",
|
||||
z.array(NodeDefinitionSchema),
|
||||
),
|
||||
}),
|
||||
async (c) => {
|
||||
const { user, system, nodeId } = c.req.valid("param");
|
||||
|
||||
try {
|
||||
const node = await service.getNodeVersions(user, system, nodeId);
|
||||
|
||||
return c.json(node);
|
||||
} catch (error) {
|
||||
if (error instanceof CustomError) {
|
||||
throw new HTTPException(error.status, { message: error.message });
|
||||
}
|
||||
throw new HTTPException(500, { message: "Internal server error" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
nodeRouter.openapi(
|
||||
createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}.wasm",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: createParamSchema("user"),
|
||||
system: createParamSchema("system"),
|
||||
nodeId: createParamSchema("nodeId").optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: { "application/wasm": { schema: z.any() } },
|
||||
description: "Retrieve a node's WASM file",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
const { user, system } = c.req.valid("param");
|
||||
const nodeId = c.req.param("nodeId.wasm");
|
||||
console.log("Get NodeWasm by Id", { user, system, nodeId });
|
||||
try {
|
||||
const res = await getNodeByVersion(user, system, nodeId);
|
||||
if (res instanceof ArrayBuffer) {
|
||||
c.header("Content-Type", "application/wasm");
|
||||
return c.body(res);
|
||||
} else {
|
||||
return c.json(res);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof CustomError) {
|
||||
throw new HTTPException(error.status, { message: error.message });
|
||||
}
|
||||
throw new HTTPException(500, { message: "Internal server error" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
nodeRouter.openapi(
|
||||
createRoute({
|
||||
method: "get",
|
||||
path: "/{user}/{system}/{nodeId}@{version}.wasm",
|
||||
request: {
|
||||
params: z.object({
|
||||
user: createParamSchema("user"),
|
||||
system: createParamSchema("system"),
|
||||
nodeId: createParamSchema("nodeId"),
|
||||
version: createParamSchema("version").optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
content: { "application/wasm": { schema: z.any() } },
|
||||
description: "Retrieve a node's WASM file",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
const { user, system, nodeId } = c.req.valid("param");
|
||||
const hash = c.req.param("version.wasm");
|
||||
try {
|
||||
const res = await getNodeByVersion(user, system, nodeId, hash);
|
||||
if (res instanceof ArrayBuffer) {
|
||||
c.header("Content-Type", "application/wasm");
|
||||
return c.body(res);
|
||||
} else {
|
||||
return c.json(res);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof CustomError) {
|
||||
throw new HTTPException(error.status, { message: error.message });
|
||||
}
|
||||
throw new HTTPException(500, { message: "Internal server error" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export { nodeRouter };
|
||||
|
||||
Reference in New Issue
Block a user