-
Notifications
You must be signed in to change notification settings - Fork 71
feat: add OpenAPI spec and generate TS types from it #251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { NextResponse } from "next/server"; | ||
| import { readFileSync } from "node:fs"; | ||
| import { join } from "node:path"; | ||
|
|
||
| // Serve the raw OpenAPI spec as JSON (for tooling consumption). | ||
| export async function GET() { | ||
| const specPath = join(process.cwd(), "..", "docs", "openapi.yaml"); | ||
| let specYaml: string; | ||
| try { | ||
| specYaml = readFileSync(specPath, "utf-8"); | ||
| } catch { | ||
| return NextResponse.json({ error: "OpenAPI spec not found" }, { status: 404 }); | ||
| } | ||
|
|
||
| // Serve the Redoc HTML page — loads spec from /api/docs/spec.yaml | ||
| const html = `<!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="utf-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
| <title>StellarCred API Reference</title> | ||
| <!-- Redoc standalone bundle (no React dependency needed) --> | ||
| <script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The Redoc bundle is fetched from Prompt To Fix With AIThis is a comment left during a code review.
Path: frontend/app/api/docs/route.ts
Line: 23
Comment:
**Unpinned CDN script without subresource integrity**
The Redoc bundle is fetched from `cdn.redoc.ly/redoc/latest` with no `integrity=` attribute. Two problems compound here: (1) `latest` is a moving pointer — a breaking release silently breaks the docs UI; (2) without SRI the browser will execute whatever the CDN serves, so a compromised or hijacked CDN delivers arbitrary JavaScript in your app's origin. Because `/api/docs` is served from the same origin as the API, any injected script can read `document.cookie`, `localStorage`, and make credentialed same-origin requests. Pin to a specific version and add a `crossorigin="anonymous" integrity="sha384-..."` attribute.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly. |
||
| <style> | ||
| body { margin: 0; } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <redoc spec-url="/api/docs/spec"></redoc> | ||
| <script> | ||
| Redoc.init('/api/docs/spec', { | ||
| theme: { | ||
| colors: { primary: { main: '#4f46e5' } }, | ||
| typography: { fontFamily: 'Inter, system-ui, sans-serif' } | ||
| } | ||
| }); | ||
| </script> | ||
| </body> | ||
| </html>`; | ||
|
|
||
| return new NextResponse(html, { | ||
| headers: { "Content-Type": "text/html; charset=utf-8" }, | ||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { NextResponse } from "next/server"; | ||
| import { readFileSync } from "node:fs"; | ||
| import { join } from "node:path"; | ||
|
|
||
| /** | ||
| * Serves the raw OpenAPI spec as YAML at GET /api/docs/spec. | ||
| * Used by the Redoc page at /api/docs to load the spec. | ||
| * Also useful for tooling (e.g. `openapi-typescript`, Postman import). | ||
| */ | ||
| export async function GET() { | ||
| const specPath = join(process.cwd(), "..", "docs", "openapi.yaml"); | ||
| let specYaml: string; | ||
| try { | ||
| specYaml = readFileSync(specPath, "utf-8"); | ||
| } catch { | ||
| return NextResponse.json({ error: "OpenAPI spec not found" }, { status: 404 }); | ||
| } | ||
|
|
||
| return new NextResponse(specYaml, { | ||
| headers: { | ||
| "Content-Type": "application/yaml; charset=utf-8", | ||
| // Allow cross-origin fetch so browser-based tooling can consume it. | ||
| "Access-Control-Allow-Origin": "*", | ||
| }, | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,7 @@ import { sha256 } from "@noble/hashes/sha2.js"; | |||||
| import { IssuerClient, CREDENTIAL_TYPES, type CredentialType, type ClaimParams } from "@stellarcred/issuer"; | ||||||
| import { fetchIssuerPubkey } from "@/lib/issuer-registry"; | ||||||
| import { logger, stripSensitiveFields, resolveRequestId } from "../../../lib/logger"; | ||||||
| import type { IssueRequest, IssueResponse200, IssueResponse202 } from "../../../types/index.js"; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: frontend/app/api/issue/route.ts
Line: 6
Comment:
**Unused type imports**
`IssueResponse200` and `IssueResponse202` are imported but not used anywhere in the file — no function return type is annotated with them. They were likely intended to type the `NextResponse.json(...)` call sites, but that wiring was not completed.
```suggestion
import type { IssueRequest } from "../../../types/index.js";
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
||||||
|
|
||||||
| // Server-side only — never shipped to the browser. | ||||||
| // Set ISSUER_PRIVATE_KEY in .env.local to the 64-char hex secp256k1 private | ||||||
|
|
@@ -222,21 +223,7 @@ export async function POST(req: NextRequest) { | |||||
| return response; | ||||||
| }; | ||||||
|
|
||||||
| let body: { | ||||||
| credential_types?: string[]; | ||||||
| // Legacy single-type shape — still accepted for backward compatibility. | ||||||
| type?: string; | ||||||
| holder?: string; | ||||||
| issuerId?: string; | ||||||
| issuerName?: string; | ||||||
| expiry?: string; | ||||||
| attributes?: Record<string, string>; | ||||||
| attribute?: string; | ||||||
| claimParams?: ClaimParams; | ||||||
| // Set by the frontend after the user returns from Persona's hosted flow. | ||||||
| persona_inquiry_id?: string; | ||||||
| returnUrl?: string; | ||||||
| }; | ||||||
| let body: IssueRequest; | ||||||
|
|
||||||
| try { | ||||||
| body = await req.json(); | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
specYamlholds the parsed YAML but is never referenced again; the HTML response hard-codesspec-url="/api/docs/spec"and fetches it from the sibling endpoint. The read serves only as a file-existence guard, meaning the entire YAML (~858 lines) is loaded into memory and discarded on every Redoc page load. A stat check (fs.existsSyncorstatSync) would achieve the same guard without reading the file contents.Prompt To Fix With AI