Initial Commit
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
# `routes/build-routes` — auth-gated import routes
|
||||
|
||||
## Purpose
|
||||
|
||||
Constructs the `OpenAPIHono` router mounted by `createImportPlugin` at
|
||||
the configured `routePrefix`. Five routes are registered — `POST
|
||||
/preview`, `POST /`, `GET /`, `GET /{id}`, `DELETE /{id}` — each gated
|
||||
behind `requireAuth()` from `@suaveplan/api-plugin-auth` so anonymous
|
||||
requests return 401 problem+json before they ever reach a handler.
|
||||
Owner-scoped routes read `c.get("user").id` and pass it through to the
|
||||
canonical `ImportService`, which raises `IMPORT.NOT_FOUND` (mapped to
|
||||
404 problem+json) on foreign-owner lookups so the surface is
|
||||
anti-enumeration-safe.
|
||||
|
||||
## Features
|
||||
|
||||
- Five OpenAPI routes with full Zod request / response schemas.
|
||||
- Per-route `app.use("/*", requireAuth())` guard.
|
||||
- `progressToResponse()` shape mapper so the response body always
|
||||
satisfies `ImportJobResponseSchema`.
|
||||
- Every handler funnels error paths through `respondProblemJson` so
|
||||
the response shape is uniform across success and failure.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { createImportRoutes, ImportService } from "@suaveplan/api-plugin-import";
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
|
||||
const service = new ImportService();
|
||||
const app = new OpenAPIHono();
|
||||
app.route("/imports", createImportRoutes({ service }));
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { createImportRoutes, ImportService, MemoryImportStore } from "@suaveplan/api-plugin-import";
|
||||
import { OpenAPIHono } from "@hono/zod-openapi";
|
||||
|
||||
const store = new MemoryImportStore();
|
||||
const service = new ImportService({ store });
|
||||
service.bus.on("import.progress", (snap) => sse.broadcast(snap));
|
||||
|
||||
const app = new OpenAPIHono();
|
||||
app.use("/imports/*", customRateLimit({ window: "1m", max: 10 }));
|
||||
app.route("/imports", createImportRoutes({ service }));
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `createImportRoutes(deps: ImportRoutesDeps): OpenAPIHono`
|
||||
- `interface ImportRoutesDeps { service: ImportService }`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `requireAuth()` middleware is mounted via `app.use("/*", ...)` so
|
||||
every route inherits the guard. Mounting per-route would duplicate
|
||||
the wiring and miss the OpenAPI catch-all.
|
||||
- `ownerOf(c)` returns the empty string when `c.get("user")` is null;
|
||||
the auth middleware throws BEFORE the handler runs, so the empty
|
||||
string is unreachable from production traffic — but it remains a
|
||||
safe default if a caller somehow bypasses the middleware in a test
|
||||
harness.
|
||||
- The list endpoint returns `{ items, total }` rather than a raw array
|
||||
so clients can paginate; the schema enforces `total >= 0`.
|
||||
- The delete endpoint surfaces a `{ deleted: boolean }` payload — a
|
||||
successful delete returns `{ deleted: true }`; a missing job throws
|
||||
`IMPORT.NOT_FOUND` from the service layer and is rendered as 404.
|
||||
Reference in New Issue
Block a user