diff --git a/api/tinyhumans.backend.json b/api/tinyhumans.backend.json index ff28b2e..ddb280c 100644 --- a/api/tinyhumans.backend.json +++ b/api/tinyhumans.backend.json @@ -8,11 +8,11 @@ "url": "https://api.tinyhumans.ai/swagger.json", "title": "TinyHumans API", "version": "1.0.0", - "pathCount": 233, - "totalOperationCount": 258, + "pathCount": 235, + "totalOperationCount": 260, "operationCount": 206, "supplementalOperationCount": 13, - "excludedAdminOperationCount": 44, + "excludedAdminOperationCount": 46, "excludedWebhookOperationCount": 12, "servers": [ "https://api.tinyhumans.ai/", @@ -148,7 +148,7 @@ "name": "auth", "basePath": "/auth", "auth": "mixed", - "operationCount": 15, + "operationCount": 16, "tags": [ "Auth" ], @@ -164,6 +164,7 @@ "GET /auth/{provider}/login", "POST /auth/channels/{channel}/link-token", "POST /auth/email/send-link", + "POST /auth/guild/link-token", "POST /auth/integrations/{integrationId}/tokens", "POST /auth/key/grant/{code}/issue", "POST /auth/keys", @@ -311,7 +312,7 @@ "name": "openCompany", "basePath": "/opencompany", "auth": "bearer", - "operationCount": 10, + "operationCount": 9, "tags": [ "OpenCompany" ], @@ -324,7 +325,6 @@ "POST /opencompany/instances/{slug}/custom-domain/verify", "POST /opencompany/instances/{slug}/resume", "POST /opencompany/instances/{slug}/suspend", - "POST /opencompany/instances/{slug}/update", "PUT /opencompany/instances/{slug}/custom-domain" ] }, diff --git a/scripts/sync-openapi.mjs b/scripts/sync-openapi.mjs index 50ef3f9..036fe4a 100644 --- a/scripts/sync-openapi.mjs +++ b/scripts/sync-openapi.mjs @@ -60,6 +60,9 @@ const RETAINED_UNEXPOSED_ROUTES = [ ["POST", "/opencompany/instances/{slug}/inference-key"], ["DELETE", "/opencompany/instances/{slug}/inference-key"], ["POST", "/opencompany/instances/{slug}/usage"], + // Guild (teeny Discord service) callbacks, gated by GUILD_SERVICE_TOKEN. + ["POST", "/internal/discord/link"], + ["DELETE", "/internal/discord/link/{userId}"], ["POST", "/admin/announcements"], ["DELETE", "/admin/announcements/{announcementId}"], ["PATCH", "/admin/announcements/{announcementId}"], diff --git a/src/generated_public_routes.rs b/src/generated_public_routes.rs index f9d5ed0..3be152b 100644 --- a/src/generated_public_routes.rs +++ b/src/generated_public_routes.rs @@ -89,6 +89,7 @@ pub const PUBLIC_ROUTES: &[(&str, &str)] = &[ ("POST", "/auth/channels/{channel}/link-token"), ("POST", "/auth/email/send-link"), ("GET", "/auth/email/verify"), + ("POST", "/auth/guild/link-token"), ("GET", "/auth/integrations"), ("DELETE", "/auth/integrations/{integrationId}"), ("POST", "/auth/integrations/{integrationId}/tokens"), @@ -143,7 +144,6 @@ pub const PUBLIC_ROUTES: &[(&str, &str)] = &[ ("POST", "/opencompany/instances/{slug}/custom-domain/verify"), ("POST", "/opencompany/instances/{slug}/resume"), ("POST", "/opencompany/instances/{slug}/suspend"), - ("POST", "/opencompany/instances/{slug}/update"), ("GET", "/opencompany/instances/usage"), ("POST", "/payments/coinbase/charge"), ("GET", "/payments/coinbase/charge/{gatewayTransactionId}"), @@ -252,6 +252,8 @@ pub(crate) const UNEXPOSED_ROUTES: &[(&str, &str)] = &[ ("POST", "/feedback/admin/triage/{id}/merge"), ("POST", "/feedback/admin/triage/{id}/reject"), ("POST", "/feedback/admin/triage/{id}/reprocess"), + ("POST", "/internal/discord/link"), + ("DELETE", "/internal/discord/link/{userId}"), ("GET", "/invite/campaign"), ("POST", "/invite/campaign"), ("DELETE", "/invite/campaign/{codeId}"), diff --git a/src/lib.rs b/src/lib.rs index 9f046e8..5172a27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -548,7 +548,13 @@ mod exclusion_tests { // 55 -> 56: `POST /admin/blog-images`, the multipart upload the // dashboard uses for a post's cover and body figures. Same service // token as the other blog writes, so it is blocked alongside them. - assert_eq!(UNEXPOSED_ROUTES.len(), 56); + // + // 56 -> 58: the teeny Discord service (the guild) calls back into the + // backend on `POST /internal/discord/link` and + // `DELETE /internal/discord/link/{userId}`, both gated by a shared + // service token rather than a user bearer, so they are unexposed like + // the orchestrator's inference-key callbacks. + assert_eq!(UNEXPOSED_ROUTES.len(), 58); for (method, template) in UNEXPOSED_ROUTES { let concrete_path = template .split('/') diff --git a/tests/openapi_sync.rs b/tests/openapi_sync.rs index 06d0b26..b0990cb 100644 --- a/tests/openapi_sync.rs +++ b/tests/openapi_sync.rs @@ -180,7 +180,13 @@ fn generated_rust_routes_match_the_public_manifest() { // // 43 -> 44: `POST /admin/blog-images`, the multipart upload behind a // post's cover and body figures. Same token as the other blog writes. - assert_eq!(manifest["source"]["excludedAdminOperationCount"], 44); + // + // 44 -> 46: `POST /internal/discord/link` and + // `DELETE /internal/discord/link/{userId}`, the teeny Discord service's + // account-link callbacks, gated by GUILD_SERVICE_TOKEN. Service-token + // routes, so they land here and never in the public surface; the + // user-facing half of that flow is `POST /auth/guild/link-token`. + assert_eq!(manifest["source"]["excludedAdminOperationCount"], 46); assert_eq!(manifest["source"]["excludedWebhookOperationCount"], 12); assert_eq!(rust_routes.len(), 206); assert_eq!(rust_routes, manifest_routes);