feat(docs): add auto-generated API documentation with Scalar UI
- Install next-openapi-gen for automatic OpenAPI spec generation from routes - Add /api-docs page with Scalar UI for interactive API documentation - Add generate step to build script (runs on every deploy) - Configure to scan Zod schemas and App Router API routes - Fix migration 0071 to use IF NOT EXISTS for idempotency - Add public/openapi.json to .gitignore (generated file) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
633c789338
commit
31b9f9dac1
|
|
@ -51,6 +51,7 @@ styled-system
|
|||
|
||||
# generated
|
||||
src/generated/build-info.json
|
||||
public/openapi.json
|
||||
|
||||
# biome
|
||||
.biome
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
-- MCP API Keys for external tool access (Claude Code, etc.)
|
||||
CREATE TABLE `mcp_api_keys` (
|
||||
CREATE TABLE IF NOT EXISTS `mcp_api_keys` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`user_id` text NOT NULL,
|
||||
`key` text NOT NULL,
|
||||
|
|
@ -9,5 +9,5 @@ CREATE TABLE `mcp_api_keys` (
|
|||
`created_at` integer NOT NULL,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);--> statement-breakpoint
|
||||
CREATE INDEX `mcp_api_keys_user_id_idx` ON `mcp_api_keys` (`user_id`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX `mcp_api_keys_key_idx` ON `mcp_api_keys` (`key`);
|
||||
CREATE INDEX IF NOT EXISTS `mcp_api_keys_user_id_idx` ON `mcp_api_keys` (`user_id`);--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS `mcp_api_keys_key_idx` ON `mcp_api_keys` (`key`);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,114 @@
|
|||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"title": "Abaci One API",
|
||||
"version": "1.0.0",
|
||||
"description": "API for Abaci One - Soroban abacus learning platform"
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://abaci.one/api",
|
||||
"description": "Production server"
|
||||
},
|
||||
{
|
||||
"url": "http://localhost:3000/api",
|
||||
"description": "Local development server"
|
||||
}
|
||||
],
|
||||
"components": {
|
||||
"securitySchemes": {
|
||||
"BearerAuth": {
|
||||
"type": "http",
|
||||
"scheme": "bearer",
|
||||
"bearerFormat": "JWT"
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultResponseSet": "common",
|
||||
"responseSets": {
|
||||
"common": [
|
||||
"400",
|
||||
"500"
|
||||
],
|
||||
"auth": [
|
||||
"400",
|
||||
"401",
|
||||
"403",
|
||||
"500"
|
||||
],
|
||||
"public": [
|
||||
"400",
|
||||
"500"
|
||||
]
|
||||
},
|
||||
"errorConfig": {
|
||||
"template": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"error": {
|
||||
"type": "string",
|
||||
"example": "{{ERROR_MESSAGE}}"
|
||||
},
|
||||
"code": {
|
||||
"type": "string",
|
||||
"example": "{{ERROR_CODE}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codes": {
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"variables": {
|
||||
"ERROR_MESSAGE": "Invalid request parameters",
|
||||
"ERROR_CODE": "BAD_REQUEST"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"variables": {
|
||||
"ERROR_MESSAGE": "Authentication required",
|
||||
"ERROR_CODE": "UNAUTHORIZED"
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"variables": {
|
||||
"ERROR_MESSAGE": "Access denied",
|
||||
"ERROR_CODE": "FORBIDDEN"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"variables": {
|
||||
"ERROR_MESSAGE": "Resource not found",
|
||||
"ERROR_CODE": "NOT_FOUND"
|
||||
}
|
||||
},
|
||||
"409": {
|
||||
"description": "Conflict",
|
||||
"variables": {
|
||||
"ERROR_MESSAGE": "Resource already exists",
|
||||
"ERROR_CODE": "CONFLICT"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"variables": {
|
||||
"ERROR_MESSAGE": "An unexpected error occurred",
|
||||
"ERROR_CODE": "INTERNAL_ERROR"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"apiDir": "./src/app/api",
|
||||
"schemaDir": "./src",
|
||||
"schemaType": "zod",
|
||||
"schemaFiles": [],
|
||||
"docsUrl": "api-docs",
|
||||
"ui": "scalar",
|
||||
"outputFile": "openapi.json",
|
||||
"outputDir": "./public",
|
||||
"includeOpenApiRoutes": false,
|
||||
"ignoreRoutes": [],
|
||||
"debug": false
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npx @pandacss/dev --clean && tsc -p tsconfig.server.json && tsc-alias -p tsconfig.server.json && concurrently \"node server.js\" \"npx @pandacss/dev --watch --poll\"",
|
||||
"build": "node scripts/generate-build-info.js && npx tsx scripts/generateAllDayIcons.tsx && npx @pandacss/dev && tsc -p tsconfig.server.json && tsc-alias -p tsconfig.server.json && npm run build:seed-script && next build",
|
||||
"build": "node scripts/generate-build-info.js && npx next-openapi-gen generate && npx tsx scripts/generateAllDayIcons.tsx && npx @pandacss/dev && tsc -p tsconfig.server.json && tsc-alias -p tsconfig.server.json && npm run build:seed-script && next build",
|
||||
"start": "NODE_ENV=production node server.js",
|
||||
"lint": "npx @biomejs/biome lint . && npx eslint .",
|
||||
"lint:fix": "npx @biomejs/biome lint . --write && npx eslint . --fix",
|
||||
|
|
@ -51,6 +51,7 @@
|
|||
"@react-spring/web": "^10.0.3",
|
||||
"@react-three/drei": "^9.117.0",
|
||||
"@react-three/fiber": "^8.17.0",
|
||||
"@scalar/api-reference-react": "^0.8.24",
|
||||
"@socket.io/redis-adapter": "^8.3.0",
|
||||
"@soroban/abacus-react": "workspace:*",
|
||||
"@soroban/core": "workspace:*",
|
||||
|
|
@ -69,6 +70,7 @@
|
|||
"@types/jsdom": "^21.1.7",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"@use-gesture/react": "^10.3.1",
|
||||
"ajv": "^8.17.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"canvas-confetti": "^1.9.4",
|
||||
|
|
@ -150,6 +152,7 @@
|
|||
"eslint-plugin-storybook": "^9.1.7",
|
||||
"happy-dom": "^18.0.1",
|
||||
"jsdom": "^27.0.0",
|
||||
"next-openapi-gen": "^0.9.4",
|
||||
"sharp": "^0.34.5",
|
||||
"storybook": "^9.1.7",
|
||||
"tsc-alias": "^1.8.16",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
"use client";
|
||||
|
||||
import { ApiReferenceReact } from "@scalar/api-reference-react";
|
||||
|
||||
import "@scalar/api-reference-react/style.css";
|
||||
|
||||
export default function ApiDocsPage() {
|
||||
return (
|
||||
<ApiReferenceReact
|
||||
configuration={{
|
||||
_integration: "nextjs",
|
||||
url: "/openapi.json",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
1833
pnpm-lock.yaml
1833
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue