mirror of
https://github.com/nestriness/nestri.git
synced 2026-08-02 01:35:18 +03:00
feat: Add sdk openapi generators
This commit is contained in:
@@ -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:"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user