feat: support custom OPENAI_BASE_URL for self-hosted OpenAI-compatible LLM endpoints (#1996) - #2090
Conversation
…ack endpoints and add contract test (CapSoftware#2039)
|
🚨 Contributor flagged. Click here for more info: Superagent Dashboard |
| if (!video.public && video.ownerId !== user?.id) { | ||
| return Response.json({ error: "Unauthorized" }, { status: 401 }); | ||
| } |
There was a problem hiding this comment.
Password policy bypassed for metadata
When a public video is password-protected, this check authorizes unauthenticated and non-owner requests solely because video.public is true, exposing its title, AI summary, chapters, and generation status without the required password.
How this was verified: The video schema permits public videos with passwords, while the established public-view policy verifies password candidates before granting access.
Knowledge Base Used: Web App (apps/web)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/app/api/video/metadata/route.ts
Line: 60-62
Comment:
**Password policy bypassed for metadata**
When a public video is password-protected, this check authorizes unauthenticated and non-owner requests solely because `video.public` is true, exposing its title, AI summary, chapters, and generation status without the required password.
**How this was verified:** The video schema permits public videos with passwords, while the established public-view policy verifies password candidates before granting access.
**Knowledge Base Used:** [Web App (apps/web)](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/web-app.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| pub async fn info(url_or_id: String, format: OutputFormat) -> Result<(), String> { | ||
| let video_id = if url_or_id.contains('/') { | ||
| url_or_id | ||
| .rsplit('/') | ||
| .next() | ||
| .unwrap_or(&url_or_id) | ||
| .to_string() | ||
| } else { | ||
| url_or_id |
There was a problem hiding this comment.
Share URL suffix becomes video ID
When a valid share URL contains a query parameter or trailing slash, rsplit('/') includes the query suffix or returns an empty segment, causing cap recordings info to send the wrong videoId and receive a 400 or 404 instead of metadata.
Knowledge Base Used: Cap CLI (apps/cli)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/cli/src/recordings.rs
Line: 110-118
Comment:
**Share URL suffix becomes video ID**
When a valid share URL contains a query parameter or trailing slash, `rsplit('/')` includes the query suffix or returns an empty segment, causing `cap recordings info` to send the wrong `videoId` and receive a 400 or 404 instead of metadata.
**Knowledge Base Used:** [Cap CLI (`apps/cli`)](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/cli.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| return Response.json({ error: "Unauthorized" }, { status: 401 }); | ||
| } | ||
|
|
||
| const meta = (video.metadata as Record<string, any>) ?? {}; |
There was a problem hiding this comment.
Explicit any breaks lint contract
The new Record<string, any> cast violates the repository's enabled noExplicitAny rule, causing the changed route to fail the required Biome check; use a defined metadata type or unknown with narrowing.
Context Used: AGENTS.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/app/api/video/metadata/route.ts
Line: 64
Comment:
**Explicit any breaks lint contract**
The new `Record<string, any>` cast violates the repository's enabled `noExplicitAny` rule, causing the changed route to fail the required Biome check; use a defined metadata type or `unknown` with narrowing.
**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| return Response.json(true, { status: 200 }); | ||
| } | ||
|
|
||
| export async function GET(request: NextRequest) { |
There was a problem hiding this comment.
Metadata route bypasses API architecture
This new operation uses an ad-hoc Next.js handler and direct database query instead of the required HttpApi builder and backend service pattern, bypassing shared policy, typed errors, and request-context wiring.
Context Used: AGENTS.md (source)
Knowledge Base Used: Web App (apps/web)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/app/api/video/metadata/route.ts
Line: 43
Comment:
**Metadata route bypasses API architecture**
This new operation uses an ad-hoc Next.js handler and direct database query instead of the required `HttpApi` builder and backend service pattern, bypassing shared policy, typed errors, and request-context wiring.
**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))
**Knowledge Base Used:** [Web App (apps/web)](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/web-app.md)
---
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!
| // Rate limit IDs declared in advance for firewall rules or separate app packages | ||
| // that are intentionally not yet wired in apps/web endpoints. |
There was a problem hiding this comment.
Comments duplicate constant purpose
These comments only narrate the self-descriptive UNWIRED_RATE_LIMIT_IDS constant rather than preserving a non-obvious invariant or workaround, adding redundant text that must be maintained.
| // Rate limit IDs declared in advance for firewall rules or separate app packages | |
| // that are intentionally not yet wired in apps/web endpoints. |
Context Used: AGENTS.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/__tests__/unit/rate-limit-ids.test.ts
Line: 6-7
Comment:
**Comments duplicate constant purpose**
These comments only narrate the self-descriptive `UNWIRED_RATE_LIMIT_IDS` constant rather than preserving a non-obvious invariant or workaround, adding redundant text that must be maintained.
```suggestion
```
**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))
---
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!
Fixes #1996
Summary
Add optional environment variable support so self-hosted Cap instances can direct AI summaries, title generation, chapter extraction, and support chat to local or custom OpenAI-compatible endpoints (such as vLLM, LocalAI, or Ollama) instead of forcing cloud .
Greptile Summary
The PR adds configurable OpenAI-compatible endpoint support and also introduces CLI recording metadata lookup, a corresponding web metadata endpoint, rate limiting, and teleprompter adjustments.
OPENAI_BASE_URLto the server environment and uses it for AI generation and messenger chat.cap recordings infoand a GET video-metadata endpoint.Confidence Score: 1/5
This PR should not merge until the password-protected metadata disclosure, malformed CLI share-URL handling, and strict TypeScript lint failure are fixed.
The new metadata route exposes AI metadata without the established password check, the CLI rejects valid share URLs with query parameters or trailing slashes, and the route introduces an explicit-any violation that breaks repository checks.
Files Needing Attention: apps/web/app/api/video/metadata/route.ts, apps/cli/src/recordings.rs, apps/web/tests/unit/rate-limit-ids.test.ts
Security Review
The new metadata endpoint bypasses the established password policy for public password-protected videos, allowing unauthenticated disclosure of titles, summaries, and chapters.
Important Files Changed
Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "feat: support custom OPENAI_BASE_URL for..." | Re-trigger Greptile
Context used (6)
apps/cli)