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:
Rotimi Best
2026-04-12 08:58:41 +01:00
parent 5a8f50f67b
commit d6e812d08b
573 changed files with 31964 additions and 8708 deletions
+6 -5
View File
@@ -1,6 +1,6 @@
# OpenAPI Spec Generator Script
This script generates the OpenAPI specification from your Hono application and optionally uploads it to a Cloudflare R2 bucket.
This script generates the OpenAPI specification for the public API routes only and optionally uploads it to a Cloudflare R2 bucket.
## Usage
@@ -38,10 +38,11 @@ CLOUDFLARE_ACCOUNT_ID=your_account_id
The script will:
1. **Generate** the OpenAPI specification from your Hono app routes
2. **Save locally** to `dist/openapi/openapi.json`
3. **Upload to R2** (if credentials provided):
- `openapi/openapi-YYYY-MM-DD.json` (dated version)
- `openapi/openapi-latest.json` (latest version)
2. **Filter** the output to `/public-api/v1/*`
3. **Save locally** to `dist/openapi/public-api/openapi.json`
4. **Upload to R2** (if credentials provided):
- `openapi/public-api/openapi-YYYY-MM-DD.json` (dated version)
- `openapi/public-api/openapi-latest.json` (latest version)
## GitHub Action Integration
+35 -5
View File
@@ -16,6 +16,35 @@ interface UploadOptions {
contentType?: string;
}
function filterPublicApiSpec(spec: Record<string, unknown>) {
const paths = (spec.paths ?? {}) as Record<string, unknown>;
const publicApiPaths = Object.fromEntries(
Object.entries(paths).filter(([path]) => path.startsWith('/public-api/v1/'))
);
const tags = Array.isArray(spec.tags)
? spec.tags.filter((tag) => {
if (!tag || typeof tag !== 'object' || !('name' in tag)) {
return false;
}
return typeof tag.name === 'string' && tag.name.startsWith('Public API');
})
: undefined;
return {
...spec,
openapi: spec.openapi ?? '3.1.0',
info: {
title: 'ClassroomIO Public API',
version: '1.0.0',
description: 'Public API for managing organizations, audience members, and courses in ClassroomIO.'
},
paths: publicApiPaths,
tags
};
}
class OpenAPISpecGenerator {
private s3Client: S3Client | null = null;
@@ -68,7 +97,8 @@ class OpenAPISpecGenerator {
}
});
const specString = JSON.stringify(spec, null, 2);
const publicApiSpec = filterPublicApiSpec(spec as Record<string, unknown>);
const specString = JSON.stringify(publicApiSpec, null, 2);
console.log('✅ OpenAPI specification generated successfully');
return specString;
@@ -80,7 +110,7 @@ class OpenAPISpecGenerator {
async saveLocalSpec(spec: string): Promise<string> {
try {
const outputDir = join(process.cwd(), 'dist', 'openapi');
const outputDir = join(process.cwd(), 'dist', 'openapi', 'public-api');
mkdirSync(outputDir, { recursive: true });
const filePath = join(outputDir, 'openapi.json');
@@ -122,10 +152,10 @@ class OpenAPISpecGenerator {
try {
const spec = await this.generateSpec();
// await this.saveLocalSpec(spec);
await this.saveLocalSpec(spec);
if (this.s3Client) {
const uploadKey = `openapi/openapi-${new Date().toISOString().split('T')[0]}.json`;
const uploadKey = `openapi/public-api/openapi-${new Date().toISOString().split('T')[0]}.json`;
await this.uploadToR2({
key: uploadKey,
content: spec,
@@ -134,7 +164,7 @@ class OpenAPISpecGenerator {
// Also upload as latest version
await this.uploadToR2({
key: 'openapi/openapi-latest.json',
key: 'openapi/public-api/openapi-latest.json',
content: spec,
contentType: 'application/json'
});