feat: Add sdk openapi generators

This commit is contained in:
Wanjohi
2025-11-05 13:23:59 +03:00
parent bcb4763532
commit dc3642153d
34 changed files with 2827 additions and 133 deletions

View File

@@ -1,21 +1,29 @@
{
"name": "@nestri/function",
"type": "module",
"version":"0.0.1",
"version": "0.0.1",
"private": true,
"exports": {
"./*": "./src/*.ts"
},
"scripts": {
"build": "./script/build.ts"
},
"devDependencies": {
"@cloudflare/workers-types": "catalog:",
"@types/bun": "catalog:",
"@tsconfig/node22": "catalog:"
"@hono/standard-validator": "^0.1.5",
"@tsconfig/node22": "catalog:",
"@types/bun": "catalog:"
},
"peerDependencies": {
"typescript": "^5",
"@nestri/core": "workspace:^"
},
"dependencies": {
"@hey-api/openapi-ts": "^0.87.0",
"@openauthjs/openauth": "catalog:",
"hono": "catalog:",
"hono-openapi": "^1.1.0",
"hono-openapi": "1.1.1",
"zod": "catalog:"
}
}

View File

@@ -3,110 +3,135 @@ import { Hono } from "hono";
import { cors } from "hono/cors";
import { auth } from "./utils/auth";
import { Log } from "@nestri/core/utils/log";
import { lazy } from "@nestri/core/utils/lazy";
import { SessionApi } from "./routes/session.js";
import { HTTPException } from "hono/http-exception";
import { VisibleError, ErrorCodes } from "@nestri/core/error";
import { openAPIRouteHandler, validator } from "hono-openapi";
import packageJson from "../../package.json" assert { type: "json" };
import { openAPIRouteHandler, validator, generateSpecs } from "hono-openapi";
const log = Log.create({ service: "api" });
export namespace Api {
const log = Log.create({ service: "api" });
export const app = new Hono();
app
.use(async (c, next) => {
c.header("Cache-Control", "no-store");
return next();
})
.use(async (c, next) => {
log.info("request", {
method: c.req.method,
path: c.req.path,
});
const start = Date.now();
await next();
log.info("response", {
duration: Date.now() - start,
});
})
.use(
validator("header", z.object({ "x-nestri-steam": z.string().optional() })),
)
.use(validator("query", z.object({ steamID: z.string().optional() })))
.use(auth)
.use(cors());
export const app = new Hono();
export const App = lazy(() =>
app
.use(async (c, next) => {
c.header("Cache-Control", "no-store");
return next();
})
.use(async (c, next) => {
log.info("request", {
method: c.req.method,
path: c.req.path,
});
const start = Date.now();
await next();
log.info("response", {
duration: Date.now() - start,
});
})
.use(
validator(
"header",
z.object({ "x-nestri-steam": z.string().optional() }),
),
)
.use(validator("query", z.object({ steamID: z.string().optional() })))
.use(auth)
.use(cors())
.get("/", async (c) => {
return c.text("ok");
})
.route("/session", SessionApi.route)
.onError((error, c) => {
// Handle our custom VisibleError
if (error instanceof VisibleError) {
return c.json(error.toResponse(), error.statusCode());
}
export const routes = app
.get("/", async (c) => {
return c.text("ok");
})
.route("/session", SessionApi.route)
.onError((error, c) => {
// Handle our custom VisibleError
if (error instanceof VisibleError) {
return c.json(error.toResponse(), error.statusCode());
}
// Handle HTTP exceptions
if (error instanceof HTTPException) {
console.error("http error:", error);
return c.json(
{
type: "validation",
code: ErrorCodes.Validation.INVALID_PARAMETER,
message: "Invalid request",
},
400,
);
}
// Handle HTTP exceptions
if (error instanceof HTTPException) {
console.error("http error:", error);
return c.json(
{
type: "validation",
code: ErrorCodes.Validation.INVALID_PARAMETER,
message: "Invalid request",
},
400,
);
}
// Handle any other errors as internal server errors
log.error("Unhandled API error:", { error });
return c.json(
{
type: "internal",
code: ErrorCodes.Server.INTERNAL_ERROR,
message: "Internal server error",
},
500,
);
});
app.get(
"/doc",
openAPIRouteHandler(routes, {
documentation: {
info: {
title: "Nestri API",
termsOfService: "https://nestri.io/terms",
description:
"The Nestri API gives you the power deploy and stream games/apps in the cloud. Run on our GPUs or bring your own.",
version: packageJson.version ?? "1.0.0",
},
components: {
securitySchemes: {
Bearer: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
// Handle any other errors as internal server errors
log.error("Unhandled API error:", { error });
return c.json(
{
type: "internal",
code: ErrorCodes.Server.INTERNAL_ERROR,
message: "Internal server error",
},
SteamID: {
type: "apiKey",
description: "The steamID to use for this query",
in: "header",
name: "x-nestri-steam",
500,
);
})
.get(
"/doc",
openAPIRouteHandler(app, {
documentation: {
info: {
title: "Nestri API",
termsOfService: "https://nestri.io/terms",
description:
"The Nestri API gives you the power deploy and stream games/apps in the cloud. Run on our GPUs or bring your own.",
version: packageJson.version ?? "1.0.0",
},
components: {
securitySchemes: {
Bearer: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
},
SteamID: {
type: "apiKey",
description: "The steamID to use for this query",
in: "header",
name: "x-nestri-steam",
},
},
},
openapi: "3.1.1",
security: [{ Bearer: [], SteamID: [] }],
servers: [
{ description: "Production", url: "https://api.nestri.io" },
{ description: "Sandbox", url: "https://api.dev.nestri.io" },
],
},
},
},
security: [{ Bearer: [], SteamID: [] }],
servers: [
{ description: "Production", url: "https://api.nestri.io" },
{ description: "Sandbox", url: "https://api.dev.nestri.io" },
],
},
}),
);
}),
),
);
export default {
port: 3001,
fetch: app.fetch,
};
export const generate = async () => {
const result = await generateSpecs(App(), {
documentation: {
info: {
title: "nestri",
version: packageJson.version ?? "1.0.0",
description: "Nestri api",
},
openapi: "3.1.1",
},
});
return result;
};
export function serve(opts: { port: number; hostname: string }) {
const server = Bun.serve({
port: opts.port,
hostname: opts.hostname,
idleTimeout: 0,
fetch: App().fetch,
});
return server;
}
}