feat: enhance API and middleware functionality: add public API routes, implement program and course member checks, and improve rate limiting for invite acceptance. Update OpenAPI spec generation to focus on public API endpoints and add new middleware for automation key scopes. Include new documentation for lesson AI features and public API tests.
This commit is contained in:
+290
-276
@@ -1,365 +1,379 @@
|
||||
---
|
||||
name: Public API with Keys
|
||||
overview: Build a versioned public API (v1) authenticated via shared organization-scoped automation keys reused by MCP and Public API, with course CRUD, course export, student/audience CRUD endpoints, plus a dashboard Automation page with MCP, Zapier, and API tabs.
|
||||
name: Public API with Organization-Scoped Automation Keys
|
||||
overview: Build the public API on top of the shipped organization automation foundation: the `/org/[slug]/automation` section already exists, MCP is live, `/automation/api` and `/automation/zapier` exist as placeholder pages, and org-scoped automation keys already exist in the backend. This PRD updates the remaining work to ship the actual public API experience and docs.
|
||||
todos:
|
||||
- id: db-schema
|
||||
content: Add `api_key` table to schema.ts, update relations, generate migration
|
||||
- id: confirm-existing-foundation
|
||||
content: Confirm and preserve the shipped automation foundation (`Automation` nav, `/org/[slug]/automation` redirect, MCP page, organization automation key backend)
|
||||
status: completed
|
||||
- id: org-automation-foundation
|
||||
content: Keep using `organization_api_key` and `/organization/automation/*` as the shared automation-key foundation for MCP, API, and Zapier
|
||||
status: completed
|
||||
- id: mcp-page-reference
|
||||
content: Use the existing MCP implementation as the structural reference for the API page (route shell, server load, feature API class, feature page component, translations)
|
||||
status: completed
|
||||
- id: agents-guidance
|
||||
content: Ensure all implementation in this initiative follows `AGENTS.md`
|
||||
status: completed
|
||||
- id: api-tab-page
|
||||
content: Replace the placeholder `/org/[slug]/automation/api` page with a real API page that follows the MCP page pattern
|
||||
status: pending
|
||||
- id: db-queries
|
||||
content: Create api-key query functions (create, getByHash, getByOrg, revoke, updateLastUsed)
|
||||
- id: api-tab-server-load
|
||||
content: Add `/org/[slug]/automation/api/+page.server.ts` to load API key and usage data using the existing organization automation endpoints
|
||||
status: pending
|
||||
- id: api-key-middleware
|
||||
content: Build apiKeyMiddleware that authenticates via Bearer token and sets orgId in context
|
||||
- id: api-tab-copy
|
||||
content: Add API-specific translations and content for docs links, usage messaging, and key management states
|
||||
status: pending
|
||||
- id: validation-schemas
|
||||
content: Add Zod schemas for shared automation key management and public API input validation
|
||||
- id: v1-validation-schemas
|
||||
content: Add public API input/output validation schemas on top of the existing automation-key schemas
|
||||
status: pending
|
||||
- id: api-key-service
|
||||
content: Create api-key service (key generation, hashing, CRUD)
|
||||
status: pending
|
||||
- id: api-key-routes
|
||||
content: Create session-authenticated automation key management routes reused by MCP and Public API
|
||||
status: pending
|
||||
- id: v1-course-service
|
||||
content: Create v1 course service (list, get, create, update, delete, export, students)
|
||||
status: pending
|
||||
- id: v1-audience-service
|
||||
content: Create v1 audience service (list, get, add, update, remove)
|
||||
- id: v1-services
|
||||
content: Create versioned public API services for courses and audience/students
|
||||
status: pending
|
||||
- id: v1-routes
|
||||
content: Create versioned v1 router with course and audience sub-routers, register in app.ts
|
||||
content: Create and register `/v1` public API routes authenticated by organization automation keys
|
||||
status: pending
|
||||
- id: public-api-scopes
|
||||
content: Finalize the public API scope model and middleware requirements for `api` and `zapier` automation keys
|
||||
status: pending
|
||||
- id: tests
|
||||
content: Write tests for api-key, v1/courses, v1/audience endpoints using Hono app.request()
|
||||
status: pending
|
||||
- id: translations
|
||||
content: Add automation-related translation keys to en.json
|
||||
status: pending
|
||||
- id: sidebar-nav
|
||||
content: Add Automation nav item to org-navigation.ts
|
||||
status: pending
|
||||
- id: automation-page
|
||||
content: Create automation page with Tabs (MCP/Zapier/API), admin-only controls, and key setup flows
|
||||
status: pending
|
||||
- id: automation-api-class
|
||||
content: Create frontend API class for api-key management
|
||||
status: pending
|
||||
- id: build-verify
|
||||
content: Build API package, verify no lint errors, run tests
|
||||
content: Add API tests for automation auth, `/v1` routes, and contract coverage
|
||||
status: pending
|
||||
- id: openapi-contracts
|
||||
content: Ensure all new public API routes are included in OpenAPI generation (route metadata + schemas)
|
||||
content: Ensure all public API routes are represented in generated OpenAPI output
|
||||
status: pending
|
||||
- id: openapi-publish
|
||||
content: Regenerate and upload latest OpenAPI spec via `upload-openapi-spec.ts`
|
||||
content: Regenerate and upload the latest OpenAPI spec via `upload-openapi-spec.ts`
|
||||
status: pending
|
||||
- id: docs-openapi-integration
|
||||
content: Integrate OpenAPI docs page in `apps/docs` using Fumadocs OpenAPI integration
|
||||
content: Add the public API docs experience in `apps/docs`
|
||||
status: pending
|
||||
- id: docs-openapi-nav
|
||||
content: Add `API` docs navigation entry/page in `apps/docs/content/docs/meta.json`
|
||||
- id: build-verify
|
||||
content: Build and verify API and docs packages after implementation
|
||||
status: pending
|
||||
isProject: false
|
||||
---
|
||||
|
||||
# Public API with Organization-Scoped API Keys
|
||||
# Public API with Organization-Scoped Automation Keys
|
||||
|
||||
## Architecture Overview
|
||||
## Goal
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph client [External Client]
|
||||
APIKey["API Key in Authorization header"]
|
||||
end
|
||||
subgraph api [API Server]
|
||||
V1Router["/v1 Router"]
|
||||
APIKeyMiddleware["apiKeyMiddleware"]
|
||||
CourseRoutes["Course Routes"]
|
||||
AudienceRoutes["Audience Routes"]
|
||||
APIKeyRoutes["API Key Management"]
|
||||
end
|
||||
subgraph db [Database]
|
||||
APIKeyTable["api_key table"]
|
||||
OrgTable["organization"]
|
||||
CourseTable["course / group"]
|
||||
AudienceTable["groupmember / profile"]
|
||||
end
|
||||
Ship a versioned public API on top of the existing organization automation platform. The current codebase already supports shared org-scoped automation keys and a dedicated Automation section in `/org`; this PRD now focuses on the remaining API product work rather than re-specifying the already-shipped foundation.
|
||||
|
||||
client -->|"Bearer cio_..."| V1Router
|
||||
V1Router --> APIKeyMiddleware
|
||||
APIKeyMiddleware -->|"validates + resolves orgId"| APIKeyTable
|
||||
APIKeyMiddleware --> CourseRoutes
|
||||
APIKeyMiddleware --> AudienceRoutes
|
||||
APIKeyRoutes -->|"session auth"| APIKeyTable
|
||||
```
|
||||
## Implementation Guardrails
|
||||
|
||||
- All implementation in this initiative must follow [AGENTS.md](../../AGENTS.md).
|
||||
- Do not design this as a brand-new standalone automation surface. Follow the existing patterns already used by the MCP implementation.
|
||||
- For dashboard work, the API page must mirror the MCP structure:
|
||||
- route shell under `apps/dashboard/src/routes/org/[slug]/automation/*`
|
||||
- server-side data loading in `+page.server.ts`
|
||||
- state and mutations in `apps/dashboard/src/lib/features/automation/api/automation.svelte.ts`
|
||||
- request/response types in `apps/dashboard/src/lib/features/automation/utils/types.ts`
|
||||
- page composition in `apps/dashboard/src/lib/features/automation/pages/api.svelte`
|
||||
- Do not define new dashboard types inside `.svelte.ts` files. Keep feature types in `utils/types.ts` and infer them from the API client.
|
||||
- Keep user-facing copy in `apps/dashboard/src/lib/utils/translations/en.json`.
|
||||
|
||||
## Current State Confirmed in Code
|
||||
|
||||
## 1. Database: `api_key` Table
|
||||
### Org navigation and routes
|
||||
|
||||
Add a new table in [packages/db/src/schema.ts](packages/db/src/schema.ts):
|
||||
These pieces already exist and should be treated as the current baseline:
|
||||
|
||||
```typescript
|
||||
export const apiKey = pgTable('api_key', {
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
organizationId: uuid('organization_id').notNull().references(() => organization.id, { onDelete: 'cascade' }),
|
||||
keyHash: text('key_hash').notNull(), // SHA-256 hash of the key
|
||||
keyPrefix: varchar('key_prefix', { length: 12 }).notNull(), // "cio_live_xxxx" for display
|
||||
name: varchar('name', { length: 255 }).notNull().default('Default'),
|
||||
lastUsedAt: timestamp('last_used_at'),
|
||||
expiresAt: timestamp('expires_at'),
|
||||
isRevoked: boolean('is_revoked').notNull().default(false),
|
||||
createdAt: timestamp('created_at').defaultNow().notNull(),
|
||||
createdBy: uuid('created_by').notNull().references(() => profile.id),
|
||||
});
|
||||
```
|
||||
- `apps/dashboard/src/lib/features/ui/navigation/org-navigation.ts`
|
||||
- contains an admin-only `Automation` section under `/org/[slug]/automation`
|
||||
- exposes nested entries for `MCP`, `Zapier`, and `API`
|
||||
- `apps/dashboard/src/routes/org/[slug]/automation/+page.ts`
|
||||
- redirects `/org/[slug]/automation` to `/org/[slug]/automation/mcp`
|
||||
- `apps/dashboard/src/routes/org/[slug]/automation/mcp/+page.svelte`
|
||||
- shipped MCP route shell using `Page.Root`, `Page.Header`, and `Page.Body`
|
||||
- `apps/dashboard/src/routes/org/[slug]/automation/mcp/+page.server.ts`
|
||||
- already loads automation keys and usage for `type=mcp`
|
||||
- `apps/dashboard/src/routes/org/[slug]/automation/api/+page.svelte`
|
||||
- already exists, but is currently a placeholder
|
||||
- `apps/dashboard/src/routes/org/[slug]/automation/zapier/+page.svelte`
|
||||
- already exists, but is currently a placeholder
|
||||
|
||||
- The raw key is only shown once on creation; we store a SHA-256 hash.
|
||||
- `keyPrefix` (e.g. `cio_live_a1b2`) lets users identify keys in the UI without exposing the secret.
|
||||
- DON'T GENERATE MIGRATION, when we run `pnpm db push` it will automatically apply the changes.
|
||||
### Frontend automation feature foundation
|
||||
|
||||
## 2. API Key Queries
|
||||
These pieces already exist and should be reused:
|
||||
|
||||
New file: [packages/db/src/queries/api-key/api-key.ts](packages/db/src/queries/api-key/api-key.ts)
|
||||
- `apps/dashboard/src/lib/features/automation/api/automation.svelte.ts`
|
||||
- shared automation API class for list/create/revoke/rotate
|
||||
- `apps/dashboard/src/lib/features/automation/utils/types.ts`
|
||||
- shared request and response inference types
|
||||
- `apps/dashboard/src/lib/features/automation/pages/mcp.svelte`
|
||||
- the current reference implementation for page structure and composition
|
||||
- `apps/dashboard/src/lib/features/automation/pages/api.svelte`
|
||||
- currently renders only a coming-soon callout
|
||||
- `apps/dashboard/src/lib/utils/translations/en.json`
|
||||
- already contains automation navigation and MCP copy
|
||||
|
||||
Functions:
|
||||
### Backend automation-key foundation
|
||||
|
||||
- `createApiKey(orgId, createdBy, name?)` -- inserts row, returns id
|
||||
- `getApiKeyByHash(hash)` -- lookup for auth middleware
|
||||
- `getApiKeysByOrganization(orgId)` -- list keys (prefix, name, createdAt, lastUsedAt, isRevoked)
|
||||
- `revokeApiKey(keyId, orgId)` -- sets `isRevoked = true`
|
||||
- `updateApiKeyLastUsed(keyId)` -- stamps `lastUsedAt`
|
||||
This work is already implemented and should not be re-invented as a separate `/api-key` system:
|
||||
|
||||
## 3. API Key Auth Middleware
|
||||
- `packages/db/src/schema.ts`
|
||||
- already defines `organization_api_key`
|
||||
- already defines `organization_automation_usage`
|
||||
- `packages/utils/src/validation/organization/automation-key.ts`
|
||||
- already defines key types: `mcp`, `api`, `zapier`
|
||||
- already defines scopes, query params, creation payload, and param validation
|
||||
- `packages/db/src/queries/organization/automation-key.ts`
|
||||
- already handles persistence for organization automation keys
|
||||
- `apps/api/src/services/organization/automation-key.ts`
|
||||
- already handles secret generation, hashing, creation, revoke, rotate, and auth
|
||||
- `apps/api/src/routes/organization/automation.ts`
|
||||
- already exposes:
|
||||
- `GET /organization/automation/usage`
|
||||
- `GET /organization/automation/keys`
|
||||
- `POST /organization/automation/keys`
|
||||
- `DELETE /organization/automation/keys/:keyId`
|
||||
- `POST /organization/automation/keys/:keyId/rotate`
|
||||
|
||||
New file: [apps/api/src/middlewares/api-key.ts](apps/api/src/middlewares/api-key.ts)
|
||||
### Important correction to the old PRD
|
||||
|
||||
- Reads `Authorization: Bearer cio_live_...` header
|
||||
- SHA-256 hashes the key, looks up via `getApiKeyByHash`
|
||||
- Checks `isRevoked`, `expiresAt`
|
||||
- Sets `c.set('orgId', key.organizationId)` on context (same pattern as `orgMemberMiddleware`)
|
||||
- Returns 401 on invalid/expired/revoked key
|
||||
The old PRD assumed:
|
||||
|
||||
## 4. Validation Schemas
|
||||
- a brand-new Automation page still needed to be created
|
||||
- Automation should be one page with internal tabs
|
||||
- a standalone `/api-key` backend should be introduced
|
||||
- `Webhooks` was one of the primary tabs
|
||||
|
||||
New file: [packages/utils/src/validation/api-key/api-key.ts](packages/utils/src/validation/api-key/api-key.ts)
|
||||
That is no longer accurate.
|
||||
|
||||
Schemas for API key management and public API input validation:
|
||||
The current product structure is:
|
||||
|
||||
- `ZCreateApiKey` -- `{ name?: string }`
|
||||
- `ZRevokeApiKeyParam` -- `{ keyId: string (uuid) }`
|
||||
- Public API schemas for course CRUD, audience CRUD, pagination, etc.
|
||||
- org navigation owns the Automation section
|
||||
- Automation is split into nested routes: `mcp`, `zapier`, and `api`
|
||||
- the shared backend lives under `/organization/automation/*`
|
||||
- MCP is the shipped reference page
|
||||
- `api` and `zapier` are present as placeholder pages
|
||||
|
||||
## 5. Public API Routes (Versioned)
|
||||
## Existing Foundation to Build On
|
||||
|
||||
### 1. Shared Automation Keys
|
||||
|
||||
The public API must reuse the existing organization-scoped automation-key model.
|
||||
|
||||
Current model:
|
||||
|
||||
- table: `organization_api_key`
|
||||
- key types: `mcp`, `api`, `zapier`
|
||||
- key prefixes: generated from the actual secret prefix
|
||||
- auth: bearer token via the existing automation-key service/middleware path
|
||||
- rotation and revocation: already implemented
|
||||
- usage tracking: already implemented in `organization_automation_usage`
|
||||
|
||||
Design requirement:
|
||||
|
||||
- do not introduce a second key table or a second dashboard key-management surface
|
||||
- do not reintroduce the older `/api-key` route design from the original PRD
|
||||
- build the public API on the existing `organization/automation` contracts unless there is a deliberate migration plan
|
||||
|
||||
### 2. Dashboard Information Architecture
|
||||
|
||||
The Automation product is already organized as nested org routes, not a tabbed single-page shell.
|
||||
|
||||
Required route structure:
|
||||
|
||||
- `/org/[slug]/automation`
|
||||
- redirect only
|
||||
- `/org/[slug]/automation/mcp`
|
||||
- existing reference implementation
|
||||
- `/org/[slug]/automation/api`
|
||||
- target page for public API setup and management
|
||||
- `/org/[slug]/automation/zapier`
|
||||
- separate placeholder or future implementation
|
||||
|
||||
Design requirement:
|
||||
|
||||
- keep the org sidebar/navigation pattern unchanged
|
||||
- do not replace nested routes with a new internal tabs container for top-level Automation navigation
|
||||
- if tabs are used inside a page, use them only for local subcontent, the way MCP uses setup tabs for client snippets
|
||||
|
||||
## Remaining Product Scope
|
||||
|
||||
### 3. API Page in Dashboard
|
||||
|
||||
The next dashboard milestone is to replace the placeholder API page with a real implementation.
|
||||
|
||||
### Target route files
|
||||
|
||||
- `apps/dashboard/src/routes/org/[slug]/automation/api/+page.svelte`
|
||||
- `apps/dashboard/src/routes/org/[slug]/automation/api/+page.server.ts`
|
||||
- `apps/dashboard/src/lib/features/automation/pages/api.svelte`
|
||||
- `apps/dashboard/src/lib/features/automation/api/automation.svelte.ts`
|
||||
- `apps/dashboard/src/lib/features/automation/utils/types.ts`
|
||||
- `apps/dashboard/src/lib/utils/translations/en.json`
|
||||
|
||||
### Required structure
|
||||
|
||||
The API page must follow the same structural pattern already used by MCP:
|
||||
|
||||
1. Route shell:
|
||||
- use `Page.Root`, `Page.Header`, `Page.HeaderContent`, `Page.Title`, and `Page.Body`
|
||||
- set the page title from `automation.tabs.api`
|
||||
- use an API-specific subtitle, not the generic coming-soon copy
|
||||
|
||||
2. Server load:
|
||||
- add `+page.server.ts`
|
||||
- load key data with `classroomio.organization.automation.keys.$get({ query: { type: 'api' } })`
|
||||
- load usage data with `classroomio.organization.automation.usage.$get({ query: { type: 'api' } })`
|
||||
- hydrate the shared `automationApi` store the same way MCP currently does
|
||||
|
||||
3. Feature page:
|
||||
- keep composition in `apps/dashboard/src/lib/features/automation/pages/api.svelte`
|
||||
- mirror the MCP page layout style and sectioning
|
||||
- do not leave this page as a single callout
|
||||
|
||||
### Required API page sections
|
||||
|
||||
The real API page should include the following, using the MCP page as the UX baseline:
|
||||
|
||||
- admin permission callout for non-admin users
|
||||
- usage cards or equivalent summary block for API-specific limits
|
||||
- API key table filtered to `type === 'api'`
|
||||
- create key flow
|
||||
- rotate key flow
|
||||
- revoke key flow
|
||||
- one-time secret reveal after creation or rotation
|
||||
- docs/discovery section with a link to the public API documentation
|
||||
- API-specific setup guidance
|
||||
|
||||
### Reuse requirements
|
||||
|
||||
- reuse `automationApi` instead of introducing a separate API-only dashboard client
|
||||
- reuse shared automation types in `utils/types.ts`
|
||||
- add new helper utilities only if the API page needs API-specific setup snippets or docs helpers
|
||||
|
||||
### 4. Public API Routes
|
||||
|
||||
The actual public API is still pending.
|
||||
|
||||
### Router structure
|
||||
|
||||
New directory: `apps/api/src/routes/v1/`
|
||||
Create:
|
||||
|
||||
Files:
|
||||
- `apps/api/src/routes/v1/index.ts`
|
||||
- `apps/api/src/routes/v1/courses.ts`
|
||||
- `apps/api/src/routes/v1/audience.ts`
|
||||
|
||||
- [apps/api/src/routes/v1/courses.ts](apps/api/src/routes/v1/courses.ts) -- Course endpoints
|
||||
- [apps/api/src/routes/v1/audience.ts](apps/api/src/routes/v1/audience.ts) -- Audience endpoints
|
||||
- [apps/api/src/routes/v1/index.ts](apps/api/src/routes/v1/index.ts) -- Combines sub-routers
|
||||
|
||||
Register in [apps/api/src/app.ts](apps/api/src/app.ts):
|
||||
Register in `apps/api/src/app.ts` as:
|
||||
|
||||
```typescript
|
||||
.route('/v1', v1Router)
|
||||
.route('/public-api/v1', v1Router)
|
||||
```
|
||||
|
||||
All v1 routes use `apiKeyMiddleware` (not session auth).
|
||||
### Authentication model
|
||||
|
||||
### Course Endpoints (`/v1/courses`)
|
||||
All `/public-api/v1` routes must authenticate with organization automation keys.
|
||||
|
||||
Requirements:
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | -------------------------------- | -------------------------------------------------- |
|
||||
| GET | `/v1/courses` | List all org courses (paginated) |
|
||||
| POST | `/v1/courses` | Create a course |
|
||||
| GET | `/v1/courses/:courseId` | Get course details |
|
||||
| PUT | `/v1/courses/:courseId` | Update a course |
|
||||
| DELETE | `/v1/courses/:courseId` | Soft-delete a course |
|
||||
| GET | `/v1/courses/:courseId/export` | Export course content (lessons, exercises) as JSON |
|
||||
| GET | `/v1/courses/:courseId/students` | List students enrolled in a course |
|
||||
- use the existing automation-key authentication path
|
||||
- require `type: 'api'` or an equivalent scope model that intentionally permits public API access
|
||||
- document and enforce scopes clearly
|
||||
- do not rely on session auth for `/public-api/v1`
|
||||
|
||||
### Planned endpoint groups
|
||||
|
||||
### Audience Endpoints (`/v1/audience`)
|
||||
Initial public API surface:
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | ---- | ----------- |
|
||||
| GET | `/public-api/v1/courses` | List organization courses |
|
||||
| POST | `/public-api/v1/courses` | Create a course |
|
||||
| GET | `/public-api/v1/courses/:courseId` | Get course details |
|
||||
| PUT | `/public-api/v1/courses/:courseId` | Update a course |
|
||||
| DELETE | `/public-api/v1/courses/:courseId` | Soft-delete a course |
|
||||
| GET | `/public-api/v1/courses/:courseId/export` | Export course content as JSON |
|
||||
| GET | `/public-api/v1/courses/:courseId/students` | List enrolled students |
|
||||
| GET | `/public-api/v1/audience` | List organization audience members |
|
||||
| POST | `/public-api/v1/audience` | Add a student to the organization |
|
||||
| POST | `/public-api/v1/audience/assign-courses` | Assign one or more audience members to one or more courses |
|
||||
| GET | `/public-api/v1/audience/:studentId` | Get student details |
|
||||
| PUT | `/public-api/v1/audience/:studentId` | Update student details |
|
||||
| DELETE | `/public-api/v1/audience/:studentId` | Remove a student from the organization |
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | ------------------------- | --------------------------------- |
|
||||
| GET | `/v1/audience` | List all org students (paginated) |
|
||||
| POST | `/v1/audience` | Add a student to the org |
|
||||
| GET | `/v1/audience/:studentId` | Get student details |
|
||||
| PUT | `/v1/audience/:studentId` | Update student info |
|
||||
| DELETE | `/v1/audience/:studentId` | Remove student from org |
|
||||
Enrollment decision:
|
||||
|
||||
- keep only the audience assignment endpoint for enrollment
|
||||
- do not add a separate course-centric enrollment route such as `POST /public-api/v1/courses/:courseId/students`
|
||||
- a single audience member plus a single course in `POST /public-api/v1/audience/assign-courses` covers the single-enrollment case
|
||||
- multiple audience members and multiple courses in the same payload cover the bulk-assignment case
|
||||
|
||||
### API Key Management (session-authenticated, not public API)
|
||||
### 5. Validation and Services
|
||||
|
||||
New file: [apps/api/src/routes/api-key/api-key.ts](apps/api/src/routes/api-key/api-key.ts)
|
||||
Add public API schemas and services on top of the existing automation-key foundation.
|
||||
|
||||
Target files:
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | ----------------- | ------------------------------------------- |
|
||||
| POST | `/api-key` | Generate new API key (returns raw key once) |
|
||||
| GET | `/api-key` | List API keys for the org |
|
||||
| DELETE | `/api-key/:keyId` | Revoke an API key |
|
||||
- `packages/utils/src/validation/...`
|
||||
- `apps/api/src/services/v1/course.ts`
|
||||
- `apps/api/src/services/v1/audience.ts`
|
||||
|
||||
Requirements:
|
||||
|
||||
Registered in `app.ts` as `.route('/api-key', apiKeyRouter)`. Protected by `authMiddleware` + `orgAdminMiddleware`.
|
||||
- follow the route/service/validation split defined in `AGENTS.md`
|
||||
- each route must return a single response type shape
|
||||
- keep business logic in services, not routes
|
||||
- reuse existing DB queries where possible
|
||||
|
||||
## 6. Services
|
||||
### 6. OpenAPI and Docs
|
||||
|
||||
- [apps/api/src/services/api-key.ts](apps/api/src/services/api-key.ts) -- Key generation (crypto.randomBytes), hashing, CRUD logic
|
||||
- [apps/api/src/services/v1/course.ts](apps/api/src/services/v1/course.ts) -- Public API course business logic (reuses existing queries from `@cio/db/queries/course`)
|
||||
- [apps/api/src/services/v1/audience.ts](apps/api/src/services/v1/audience.ts) -- Public API audience business logic (reuses `getOrganizationAudience` and related queries)
|
||||
The backend already has OpenAPI infrastructure. The remaining work is to ensure the new public API is included and discoverable.
|
||||
|
||||
## 7. Tests
|
||||
### OpenAPI requirements
|
||||
|
||||
New test files under `apps/api/src/__tests__/`:
|
||||
- all new `/public-api/v1/*` routes must be documented with the existing Hono OpenAPI patterns
|
||||
- request params, query, body, success, and error responses must be declared
|
||||
- avoid undocumented public API routes
|
||||
|
||||
- `api-key.test.ts` -- API key creation, listing, revocation, auth flow
|
||||
- `v1/courses.test.ts` -- All course CRUD + export + students endpoints
|
||||
- `v1/audience.test.ts` -- All audience CRUD endpoints
|
||||
### Spec publishing
|
||||
|
||||
Tests will use Hono's `app.request()` test helper (no HTTP server needed). Mock the DB layer with `vi.mock()`.
|
||||
Use the existing script:
|
||||
|
||||
Additionally, include OpenAPI contract coverage:
|
||||
- verify all `/v1/*` and `/api-key/*` public-facing endpoints are represented in generated spec.
|
||||
- ensure schema components are generated for request/response payloads.
|
||||
- `apps/api/scripts/upload-openapi-spec.ts`
|
||||
|
||||
## 8. Dashboard UI: Automation Page
|
||||
Required flow after API changes:
|
||||
|
||||
### Sidebar Navigation
|
||||
1. Implement or update routes and schemas.
|
||||
2. Run `pnpm --filter @cio/api upload:openapi`.
|
||||
3. Verify the latest spec includes the new routes.
|
||||
|
||||
Add an "Automation" nav item in [apps/dashboard/src/lib/features/ui/navigation/org-navigation.ts](apps/dashboard/src/lib/features/ui/navigation/org-navigation.ts), placed before Settings, with `requiresAdmin: true`:
|
||||
### Docs integration
|
||||
|
||||
```typescript
|
||||
{
|
||||
titleKey: 'org_navigation.automation',
|
||||
path: '/automation',
|
||||
icon: AutomationIcon, // use Zap or Workflow icon from lucide
|
||||
requiresAdmin: true,
|
||||
matchPattern: '^/org/[^/]+/automation(/.*)?$'
|
||||
}
|
||||
```
|
||||
Add the public API docs experience in `apps/docs`.
|
||||
|
||||
### Route
|
||||
Target tasks:
|
||||
|
||||
New file: [apps/dashboard/src/routes/org/[slug]/automation/+page.svelte](apps/dashboard/src/routes/org/[slug]/automation/+page.svelte)
|
||||
1. Add the API docs page or route.
|
||||
2. Wire it to the published OpenAPI source.
|
||||
3. Add docs navigation for API.
|
||||
4. Verify the docs build still passes.
|
||||
|
||||
### Page Structure
|
||||
### 7. Testing
|
||||
|
||||
```
|
||||
+----------------------------------------------+
|
||||
| Automation |
|
||||
| Connect ClassroomIO with your tools and |
|
||||
| workflows. |
|
||||
+----------------------------------------------+
|
||||
| [ API ] [ Zapier ] [ Webhooks ] |
|
||||
+----------------------------------------------+
|
||||
| |
|
||||
| (Tab content based on selection) |
|
||||
| |
|
||||
+----------------------------------------------+
|
||||
```
|
||||
Add tests for:
|
||||
|
||||
- Title: "Automation" with description text
|
||||
- Uses `Tabs` from `@cio/ui/base/tabs`
|
||||
- Three tabs: **API**, **Zapier**, **Webhooks**
|
||||
- organization automation key auth behavior relevant to public API access
|
||||
- `/v1/courses`
|
||||
- `/v1/audience`
|
||||
- OpenAPI contract inclusion for public API routes
|
||||
|
||||
### API Tab Content
|
||||
Use Hono app-level testing where possible.
|
||||
|
||||
**Free plan users:** Show `UpgradeBanner` component (same pattern as other pages). Disable all controls.
|
||||
### 8. Build and Verification
|
||||
|
||||
**Paid plan users:**
|
||||
Required verification commands after implementation:
|
||||
|
||||
- API key display field (masked by default, e.g. `cio_live_••••••••••••a1b2`)
|
||||
- "Show" / "Hide" toggle button to reveal/mask the key
|
||||
- "Copy" button to copy to clipboard
|
||||
- "Regenerate" button (with confirmation) to create a new key and revoke the old one
|
||||
- "Read the Docs" button/link pointing to the API documentation URL
|
||||
|
||||
### API Class
|
||||
|
||||
New file: [apps/dashboard/src/lib/features/automation/api/automation.svelte.ts](apps/dashboard/src/lib/features/automation/api/automation.svelte.ts)
|
||||
|
||||
Extends `BaseApi`, methods:
|
||||
|
||||
- `fetchApiKeys()` -- GET `/api-key`
|
||||
- `createApiKey()` -- POST `/api-key`
|
||||
- `revokeApiKey(keyId)` -- DELETE `/api-key/:keyId`
|
||||
|
||||
### Frontend Types
|
||||
|
||||
New file: [apps/dashboard/src/lib/features/automation/utils/types.ts](apps/dashboard/src/lib/features/automation/utils/types.ts)
|
||||
|
||||
### Translations
|
||||
|
||||
Add keys to [apps/dashboard/src/lib/utils/translations/en.json](apps/dashboard/src/lib/utils/translations/en.json):
|
||||
|
||||
- `org_navigation.automation`
|
||||
- `automation.title`, `automation.description`
|
||||
- `automation.tabs.api`, `automation.tabs.zapier`, `automation.tabs.webhooks`
|
||||
- `automation.api.key_label`, `automation.api.copy`, `automation.api.regenerate`, `automation.api.read_docs`
|
||||
- `automation.api.upgrade_prompt`
|
||||
- `automation.zapier.coming_soon`, `automation.webhooks.coming_soon`
|
||||
|
||||
## 9. Versioning Strategy
|
||||
|
||||
- All public API routes live under `/v1/`. When v2 is needed, a new `apps/api/src/routes/v2/` directory is created with its own router, registered as `.route('/v2', v2Router)`.
|
||||
- The v1 router remains untouched, ensuring backward compatibility.
|
||||
- The API key middleware is version-agnostic and shared across all versions.
|
||||
|
||||
## 10. OpenAPI Spec Requirements
|
||||
|
||||
### Route Annotation Requirements
|
||||
All new public API routes in this PRD must be OpenAPI-documented:
|
||||
- add route metadata using existing Hono OpenAPI patterns (`describeRoute`, validators/schemas).
|
||||
- ensure request params/query/body and success/error response shapes are declared.
|
||||
- avoid undocumented routes under `/v1/*` and `/api-key/*`.
|
||||
|
||||
### Spec Generation and Publishing
|
||||
Use existing script in `apps/api/scripts/upload-openapi-spec.ts`:
|
||||
- generate spec from app routes.
|
||||
- save local output to `apps/api/dist/openapi/openapi.json`.
|
||||
- upload dated + latest specs to R2 (`openapi/openapi-YYYY-MM-DD.json`, `openapi/openapi-latest.json`).
|
||||
|
||||
Required update flow after public API changes:
|
||||
1. implement/adjust routes and schemas.
|
||||
2. run `pnpm --filter @cio/api upload:openapi`.
|
||||
3. verify `openapi-latest.json` contains new endpoints.
|
||||
|
||||
### CI/Verification Gate
|
||||
Add a verification step in this initiative to prevent stale API docs:
|
||||
- PR checklist requires successful OpenAPI generation for API changes.
|
||||
- include generated-spec validation in API CI for public API route changes.
|
||||
|
||||
## 11. Docs Integration (Fumadocs OpenAPI)
|
||||
|
||||
Integrate API docs in `apps/docs` following Fumadocs OpenAPI integration guidance:
|
||||
- reference: `https://www.fumadocs.dev/docs/integrations/openapi`
|
||||
- wire docs app to render OpenAPI spec from latest published source.
|
||||
|
||||
Target implementation tasks:
|
||||
1. Add required Fumadocs OpenAPI integration package(s) to `apps/docs/package.json`.
|
||||
2. Add a docs route/page named `API` at `/api` in `apps/docs/src/routes` (or equivalent docs content route).
|
||||
3. Configure the page to load `openapi-latest.json` URL (env-driven preferred, with production fallback).
|
||||
4. Add an `API` entry to docs navigation in `apps/docs/content/docs/meta.json`.
|
||||
5. Validate local docs build still passes with the OpenAPI page.
|
||||
|
||||
Suggested file touchpoints:
|
||||
- `apps/docs/package.json`
|
||||
- `apps/docs/src/routes/*` (new `/api` route) or `apps/docs/content/docs/api.mdx`
|
||||
- `apps/docs/content/docs/meta.json`
|
||||
- optional docs config/util files for OpenAPI URL env handling
|
||||
|
||||
## 12. Build and Verification Commands
|
||||
1. `pnpm --filter @cio/api build`
|
||||
2. `pnpm --filter @cio/api upload:openapi`
|
||||
3. `pnpm --filter @cio/docs build`
|
||||
4. Manual check: docs navigation shows `API`, open `/api`, and verify new endpoints appear.
|
||||
|
||||
Note: Follow AGENTS.md for code practices.
|
||||
Manual verification:
|
||||
|
||||
1. `/org/[slug]/automation` still redirects to `/org/[slug]/automation/mcp`
|
||||
2. `/org/[slug]/automation/api` now renders the real API page rather than the placeholder
|
||||
3. API key creation, rotation, and revoke flows work for `type=api`
|
||||
4. `/public-api/v1/*` routes respond under the new base path
|
||||
5. docs navigation exposes the public API docs entry
|
||||
|
||||
Reference in New Issue
Block a user