Conversation
- Add lib/transcribe/ module with core transcription logic - Add MCP tool 'transcribe_audio' for chat integration - Add POST /api/transcribe endpoint - Saves both original audio and transcript to customer files
- DRY: Extract shared FileRecord type and STORAGE_BUCKET constant - DRY: Centralize error formatting in formatTranscriptionError() - Remove console.error statements (production code) - Remove redundant 'what' comments, keep 'why' context - Extract getExtensionFromContentType helper function
- Add lib/consts.ts with SUPABASE_STORAGE_BUCKET - Add lib/supabase/storage/uploadFileByKey.ts (mirrors Chat) - Add lib/supabase/files/createFileRecord.ts (mirrors Chat) - Refactor transcribe files to use shared utilities - Enables future extraction to shared package
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds a new audio transcription feature: POST API route at /api/transcribe, MCP tool registration for transcribe_audio, OpenAI Whisper transcription integration, Supabase storage + DB persistence for audio and markdown transcripts, utility functions/types/constants, and a design doc for an inbound email client. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant APIRoute as /api/transcribe
participant Processor as TranscriptionProcessor
participant Fetcher as AudioFetcher
participant OpenAI as OpenAI_Whisper
participant Storage as Supabase_Storage
participant DB as Supabase_DB
Client->>APIRoute: POST { audio_url, account_id, artist_account_id, title?, include_timestamps? }
APIRoute->>APIRoute: validate input
APIRoute->>Processor: processAudioTranscription(params)
rect rgb(230,240,255)
Processor->>Fetcher: fetch audio from URL
Fetcher-->>Processor: audio blob / headers
Processor->>Storage: uploadFileByKey(audio blob)
Storage-->>Processor: storageKey
Processor->>DB: createFileRecord(audio metadata)
DB-->>Processor: audio FileRecord
end
rect rgb(240,255,230)
Processor->>OpenAI: POST /v1/audio/transcriptions (file)
OpenAI-->>Processor: transcription response (text, segments, language)
Processor->>Processor: formatTranscriptMd(transcription)
end
rect rgb(255,245,220)
Processor->>Storage: uploadFileByKey(markdown)
Storage-->>Processor: storageKey
Processor->>DB: createFileRecord(transcript metadata)
DB-->>Processor: transcript FileRecord
end
Processor-->>APIRoute: { success, audioFile, transcriptFile, text, language }
APIRoute-->>Client: HTTP 200 JSON (or formatted error)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Fix all issues with AI Agents
In @lib/consts.ts:
- Line 5: Replace the hardcoded SUPABASE_STORAGE_BUCKET constant with a value
read from the environment so the bucket can be configured per deployment; update
the symbol SUPABASE_STORAGE_BUCKET in lib/consts.ts to derive from
process.env.SUPABASE_STORAGE_BUCKET (providing a sensible default like
"user-files" if desired) and ensure any code importing SUPABASE_STORAGE_BUCKET
continues to work; also update setup/docs to describe creating the bucket and
required access policies for the configured SUPABASE_STORAGE_BUCKET environment
variable.
In @lib/supabase/files/createFileRecord.ts:
- Around line 3-13: The local FileRecord interface in createFileRecord.ts
conflicts with the exported FileRecord in lib/transcribe/types.ts causing type
mismatches for callers like saveAudioToFiles() and saveTranscriptToFiles(); fix
by consolidating the definitions—either export the full 13-field interface from
createFileRecord.ts and update lib/transcribe/types.ts to import/use that single
definition, or change the return type annotation of createFileRecord() to the
exported 3-field FileRecord from lib/transcribe/types.ts so callers and
implementation agree (ensure symbols: createFileRecord(), FileRecord,
saveAudioToFiles(), saveTranscriptToFiles() are updated accordingly).
In @lib/transcribe/saveAudioToFiles.ts:
- Around line 9-10: The current storageKey built in saveAudioToFiles uses
safeFileName and can collide if the same fileName is uploaded for the same
ownerAccountId/artistAccountId; modify saveAudioToFiles (and mirror the same
change in saveTranscriptToFiles) to append a stable unique suffix (e.g., ISO
timestamp or UUID) to safeFileName or include it in storageKey so each uploaded
file gets a unique key; update the variables safeFileName and storageKey
creation accordingly and ensure the same uniqueness strategy is used in both
functions for consistency.
In @lib/transcribe/saveTranscriptToFiles.ts:
- Around line 8-10: The current logic builds storageKey from safeTitle/fileName
(safeTitle, fileName, storageKey in saveTranscriptToFiles.ts) but uses upsert:
false which causes uploads to fail on duplicate titles; modify the
fileName/storageKey generation to append a unique suffix (e.g., timestamp or
short UUID) to fileName (e.g., `${safeTitle}-${Date.now()}-transcript.md`) or
alternatively catch the conflict error from the upload call and retry with a
unique suffix; ensure changes are applied where fileName and storageKey are
constructed and where the upload is invoked so collisions are avoided or handled
gracefully.
In @lib/transcribe/types.ts:
- Around line 64-81: The fallback path in formatTranscriptionError currently
returns rawMessage (which may leak internal details); change it to return a
generic client-safe message like "Transcription failed" with status 500 instead
of rawMessage, and if you need the original error for diagnostics, log the error
internally (e.g., via your logger or console.error) before returning the generic
message; update formatTranscriptionError to never expose rawMessage to callers.
🧹 Nitpick comments (9)
lib/supabase/storage/uploadFileByKey.ts (1)
7-25: Implementation is correct, but consider returning upload metadata.The function correctly handles the upload with appropriate error handling. However, returning
voidmeans callers cannot access useful information like the public URL, storage path confirmation, or upload metadata.Optional enhancement: Return upload metadata
-): Promise<void> { - const { error } = await supabase.storage +): Promise<{ path: string }> { + const { data, error } = await supabase.storage .from(SUPABASE_STORAGE_BUCKET) .upload(key, file, { contentType: options.contentType || "application/octet-stream", upsert: options.upsert ?? false, }); if (error) { throw new Error(`Failed to upload file: ${error.message}`); } + + return { path: data.path }; }lib/supabase/files/createFileRecord.ts (1)
53-53: Defensive array check may be unnecessary.The
Array.isArray(tags)check is defensive, but according to theCreateFileRecordParamsinterface (line 23),tagsis already typed asstring[] | undefined. TypeScript should ensure that whentagsis provided, it's always an array.The check adds safety but could be simplified:
- tags: Array.isArray(tags) ? tags : [], + tags: tags ?? [],app/api/transcribe/route.ts (2)
7-21: Consider adding URL and UUID format validation for consistency with MCP tool schema.The MCP tool in
registerTranscribeAudioTool.tsvalidatesaudio_urlas a URL andaccount_id/artist_account_idas UUIDs using Zod. This route only checks for presence. Consider adding similar validation for consistency and to fail fast with clear errors.🔎 Proposed enhancement
+import { z } from "zod"; + +const transcribeBodySchema = z.object({ + audio_url: z.string().url(), + account_id: z.string().uuid(), + artist_account_id: z.string().uuid(), + title: z.string().optional(), + include_timestamps: z.boolean().optional(), +}); + export async function POST(req: NextRequest) { try { const body = await req.json(); - const { audio_url, account_id, artist_account_id, title, include_timestamps } = body; - - if (!audio_url) { - return NextResponse.json({ error: "Missing required field: audio_url" }, { status: 400 }); - } - if (!account_id) { - return NextResponse.json({ error: "Missing required field: account_id" }, { status: 400 }); - } - if (!artist_account_id) { - return NextResponse.json( - { error: "Missing required field: artist_account_id" }, - { status: 400 }, - ); - } + const parsed = transcribeBodySchema.safeParse(body); + if (!parsed.success) { + return NextResponse.json( + { error: parsed.error.issues[0]?.message || "Invalid request body" }, + { status: 400 }, + ); + } + const { audio_url, account_id, artist_account_id, title, include_timestamps } = parsed.data;
6-7: Handle JSON parse errors explicitly for clearer error messages.If the request body is not valid JSON,
req.json()throws aSyntaxError. The current catch block will pass this toformatTranscriptionError, which will return the raw error message (e.g., "Unexpected token..."). Consider catching this case explicitly for a cleaner user experience.🔎 Proposed enhancement
export async function POST(req: NextRequest) { try { - const body = await req.json(); + let body; + try { + body = await req.json(); + } catch { + return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); + }lib/transcribe/formatTranscriptMd.ts (1)
14-17: Consider escaping markdown special characters in title.If the
titlecontains Markdown special characters (e.g.,#,*,[,]), they could affect the document formatting. This may be an edge case depending on how titles are sourced.🔎 Proposed fix
+function escapeMarkdown(text: string): string { + return text.replace(/([#*_\[\]\\`])/g, "\\$1"); +} + export function formatTranscriptMd( transcription: TranscriptionResult, options: TranscriptMdOptions = {}, ): string { const { title = "Transcription", includeTimestamps = false } = options; - let md = `# ${title}\n\n`; + let md = `# ${escapeMarkdown(title)}\n\n`;lib/transcribe/transcribeAudio.ts (1)
41-47: Consider adding a timeout for the OpenAI API call.Audio transcription can take a while for long files. Without a timeout, the request could hang indefinitely if there's a network issue or the API becomes unresponsive. Consider using
AbortControllerwith a reasonable timeout.🔎 Proposed enhancement
+ const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 120_000); // 2 minute timeout + const response = await fetch("https://api.openai.com/v1/audio/transcriptions", { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, }, body: formData, + signal: controller.signal, }); + + clearTimeout(timeoutId);lib/transcribe/processAudioTranscription.ts (2)
16-19: Consider adding a timeout and URL validation for the external fetch.Fetching from an arbitrary URL without a timeout could cause the request to hang indefinitely. Additionally, consider validating the URL scheme to prevent SSRF attacks (e.g., only allow
https://).🔎 Suggested improvement
+ // Validate URL scheme to prevent SSRF + const url = new URL(audioUrl); + if (!["http:", "https:"].includes(url.protocol)) { + throw new Error("Invalid audio URL: only HTTP(S) protocols are allowed"); + } + - const response = await fetch(audioUrl); + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 60_000); // 60s timeout + + const response = await fetch(audioUrl, { signal: controller.signal }); + clearTimeout(timeoutId); if (!response.ok) { throw new Error(`Failed to fetch audio: ${response.statusText}`); }
65-70: Consider extending content-type coverage.The helper handles common types well. You might want to add support for
oggandflacwhich are also commonly used and supported by Whisper.🔎 Extended mapping
function getExtensionFromContentType(contentType: string): string { if (contentType.includes("wav")) return "wav"; if (contentType.includes("m4a") || contentType.includes("mp4")) return "m4a"; if (contentType.includes("webm")) return "webm"; + if (contentType.includes("ogg")) return "ogg"; + if (contentType.includes("flac")) return "flac"; return "mp3"; }features/feature-email-client.md (1)
23-26: Add language specifier to code block.Per the linter, this code block should have a language specifier for proper syntax highlighting.
-``` +```bash RESEND_API_KEY=your_resend_api_key</blockquote></details> </blockquote></details> <details> <summary>📜 Review details</summary> **Configuration used**: defaults **Review profile**: CHILL **Plan**: Pro <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 931cfaa67c1ddab6da4818e54addd5f3c28ce7cf and 0f3e75a671a0491ff46eb8dfee968ba0e6920f11. </details> <details> <summary>📒 Files selected for processing (15)</summary> * `app/api/transcribe/route.ts` * `features/feature-email-client.md` * `lib/consts.ts` * `lib/mcp/tools/index.ts` * `lib/mcp/tools/transcribe/index.ts` * `lib/mcp/tools/transcribe/registerTranscribeAudioTool.ts` * `lib/supabase/files/createFileRecord.ts` * `lib/supabase/storage/uploadFileByKey.ts` * `lib/transcribe/formatTranscriptMd.ts` * `lib/transcribe/index.ts` * `lib/transcribe/processAudioTranscription.ts` * `lib/transcribe/saveAudioToFiles.ts` * `lib/transcribe/saveTranscriptToFiles.ts` * `lib/transcribe/transcribeAudio.ts` * `lib/transcribe/types.ts` </details> <details> <summary>🧰 Additional context used</summary> <details> <summary>🧬 Code graph analysis (10)</summary> <details> <summary>lib/supabase/storage/uploadFileByKey.ts (1)</summary><blockquote> <details> <summary>lib/consts.ts (1)</summary> * `SUPABASE_STORAGE_BUCKET` (5-5) </details> </blockquote></details> <details> <summary>lib/mcp/tools/transcribe/index.ts (1)</summary><blockquote> <details> <summary>lib/mcp/tools/transcribe/registerTranscribeAudioTool.ts (1)</summary> * `registerTranscribeAudioTool` (18-50) </details> </blockquote></details> <details> <summary>app/api/transcribe/route.ts (3)</summary><blockquote> <details> <summary>lib/transcribe/processAudioTranscription.ts (1)</summary> * `processAudioTranscription` (11-63) </details> <details> <summary>lib/transcribe/index.ts (1)</summary> * `processAudioTranscription` (12-12) </details> <details> <summary>lib/transcribe/types.ts (1)</summary> * `formatTranscriptionError` (64-81) </details> </blockquote></details> <details> <summary>lib/transcribe/transcribeAudio.ts (2)</summary><blockquote> <details> <summary>lib/transcribe/index.ts (1)</summary> * `transcribeAudio` (8-8) </details> <details> <summary>lib/transcribe/types.ts (1)</summary> * `TranscriptionResult` (11-15) </details> </blockquote></details> <details> <summary>lib/supabase/files/createFileRecord.ts (1)</summary><blockquote> <details> <summary>lib/transcribe/types.ts (1)</summary> * `FileRecord` (5-9) </details> </blockquote></details> <details> <summary>lib/transcribe/saveAudioToFiles.ts (4)</summary><blockquote> <details> <summary>lib/transcribe/index.ts (1)</summary> * `saveAudioToFiles` (10-10) </details> <details> <summary>lib/transcribe/types.ts (2)</summary> * `SaveAudioParams` (29-33) * `FileRecord` (5-9) </details> <details> <summary>lib/supabase/storage/uploadFileByKey.ts (1)</summary> * `uploadFileByKey` (7-25) </details> <details> <summary>lib/supabase/files/createFileRecord.ts (1)</summary> * `createFileRecord` (29-63) </details> </blockquote></details> <details> <summary>lib/mcp/tools/transcribe/registerTranscribeAudioTool.ts (4)</summary><blockquote> <details> <summary>lib/transcribe/processAudioTranscription.ts (1)</summary> * `processAudioTranscription` (11-63) </details> <details> <summary>lib/mcp/getToolResultSuccess.ts (1)</summary> * `getToolResultSuccess` (9-11) </details> <details> <summary>lib/transcribe/types.ts (1)</summary> * `formatTranscriptionError` (64-81) </details> <details> <summary>lib/mcp/getToolResultError.ts (1)</summary> * `getToolResultError` (9-16) </details> </blockquote></details> <details> <summary>lib/transcribe/saveTranscriptToFiles.ts (3)</summary><blockquote> <details> <summary>lib/transcribe/types.ts (2)</summary> * `SaveTranscriptParams` (35-37) * `FileRecord` (5-9) </details> <details> <summary>lib/supabase/storage/uploadFileByKey.ts (1)</summary> * `uploadFileByKey` (7-25) </details> <details> <summary>lib/supabase/files/createFileRecord.ts (1)</summary> * `createFileRecord` (29-63) </details> </blockquote></details> <details> <summary>lib/transcribe/formatTranscriptMd.ts (2)</summary><blockquote> <details> <summary>lib/transcribe/index.ts (1)</summary> * `formatTranscriptMd` (9-9) </details> <details> <summary>lib/transcribe/types.ts (2)</summary> * `TranscriptionResult` (11-15) * `TranscriptMdOptions` (17-20) </details> </blockquote></details> <details> <summary>lib/mcp/tools/index.ts (1)</summary><blockquote> <details> <summary>lib/mcp/tools/transcribe/index.ts (1)</summary> * `registerTranscribeTools` (9-11) </details> </blockquote></details> </details><details> <summary>🪛 markdownlint-cli2 (0.18.1)</summary> <details> <summary>features/feature-email-client.md</summary> 24-24: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 33-33: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 38-38: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 57-57: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 76-76: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 91-91: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 142-142: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 265-265: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 319-319: Fenced code blocks should have a language specified (MD040, fenced-code-language) --- 342-342: Fenced code blocks should have a language specified (MD040, fenced-code-language) </details> </details> </details> <details> <summary>⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)</summary> * GitHub Check: Vercel Agent Review </details> <details> <summary>🔇 Additional comments (15)</summary><blockquote> <details> <summary>lib/mcp/tools/index.ts (1)</summary><blockquote> `15-15`: **LGTM! Clean integration.** The transcribe tools are properly imported and registered following the established pattern used by other tool categories in this file. Also applies to: 31-31 </blockquote></details> <details> <summary>lib/mcp/tools/transcribe/index.ts (1)</summary><blockquote> `1-11`: **LGTM! Well-structured module.** The delegation pattern provides a clean entry point for transcribe tools and allows easy expansion for additional transcription-related tools in the future. </blockquote></details> <details> <summary>lib/supabase/files/createFileRecord.ts (1)</summary><blockquote> `29-63`: **Implementation is solid with proper error handling.** The function correctly maps camelCase parameters to snake_case database columns and handles optional fields appropriately. Error handling provides clear feedback on failures. </blockquote></details> <details> <summary>lib/transcribe/formatTranscriptMd.ts (1)</summary><blockquote> `19-32`: **LGTM!** The conditional logic correctly handles all combinations: timestamps with chunks, timestamps requested but no chunks available, and plain text mode. The timestamp formatting is clear and consistent. </blockquote></details> <details> <summary>lib/mcp/tools/transcribe/registerTranscribeAudioTool.ts (2)</summary><blockquote> `36-43`: **Review potential redundancy in success flag.** Looking at `getToolResultSuccess` from the relevant snippets, it stringifies the data directly. The `getToolResultError` wrapper adds `success: false` to its output. For consistency, you're adding `success: true` here. This is fine, but verify that the MCP client consumers expect this structure, as `getToolResultSuccess` doesn't automatically add it. --- `8-14`: **LGTM!** The Zod schema provides proper validation for URL format and UUID format on the ID fields, which is more robust than the HTTP route's current validation. Good use of `.describe()` for documentation. </blockquote></details> <details> <summary>lib/transcribe/saveTranscriptToFiles.ts (1)</summary><blockquote> `19-28`: **LGTM!** The file record creation correctly captures all metadata including the accurate byte size via `TextEncoder`, appropriate MIME type, and meaningful description. The tag fallback is sensible. </blockquote></details> <details> <summary>lib/transcribe/transcribeAudio.ts (2)</summary><blockquote> `49-54`: **LGTM on error handling!** Good defensive approach with `.catch(() => ({}))` when parsing the error response, and the fallback message with status code provides useful context. --- `59-68`: **LGTM!** The segment-to-chunk mapping correctly transforms OpenAI's response format to the internal `TranscriptionResult` structure. Using `as [number, number]` appropriately narrows the tuple type. </blockquote></details> <details> <summary>lib/transcribe/saveAudioToFiles.ts (1)</summary><blockquote> `5-33`: **LGTM on overall structure!** The implementation is clean and consistent with the parallel `saveTranscriptToFiles` pattern. Good use of the shared types and proper metadata capture. </blockquote></details> <details> <summary>lib/transcribe/index.ts (1)</summary><blockquote> `1-14`: **LGTM!** Clean barrel file with helpful module documentation. The centralized export surface makes consuming the transcription API straightforward. </blockquote></details> <details> <summary>lib/transcribe/processAudioTranscription.ts (1)</summary><blockquote> `27-47`: **LGTM - Consider documenting partial failure behavior.** The workflow is clean and well-structured. Note that if transcription fails after the audio is saved (line 37), the audio file will remain in storage. If this is intentional for retry scenarios, consider adding a comment to clarify; otherwise, you may want to add cleanup logic. </blockquote></details> <details> <summary>lib/transcribe/types.ts (1)</summary><blockquote> `5-58`: **LGTM - Well-structured type definitions.** The interfaces are cleanly organized with appropriate use of inheritance (`SaveAudioParams extends SaveFileParams`). The separation between `FileRecord` (database shape) and `FileInfo` (API response shape) is a good design pattern. </blockquote></details> <details> <summary>features/feature-email-client.md (2)</summary><blockquote> `1-14`: **This documentation file appears unrelated to the transcription feature.** This PR is titled "Sid/recoup api transcribe" but this file documents the Inbound Email Client feature. Please verify this file was intentionally included in this PR. --- `403-409`: **Add language specifier to HTML code block.** This block contains HTML and would benefit from syntax highlighting. ```diff -```html +```html <hr /> <p>Note: you can reply directly to this email to continue the conversation.</p>Note: The block already has
htmlspecified, so this is already correct - no change needed.
- Export FileRecord from createFileRecord.ts (single source of truth) - Re-export from lib/transcribe/types.ts - Simplify save functions to return full FileRecord directly
- Orchestrator generates timestamp once for both audio and transcript - Ensures matching timestamps between related files - Prevents 'resource already exists' errors on duplicate titles
| const { audio_url, account_id, artist_account_id, title, include_timestamps } = body; | ||
|
|
||
| if (!audio_url) { | ||
| return NextResponse.json({ error: "Missing required field: audio_url" }, { status: 400 }); | ||
| } | ||
| if (!account_id) { | ||
| return NextResponse.json({ error: "Missing required field: account_id" }, { status: 400 }); | ||
| } | ||
| if (!artist_account_id) { | ||
| return NextResponse.json( | ||
| { error: "Missing required field: artist_account_id" }, | ||
| { status: 400 }, | ||
| ); | ||
| } |
There was a problem hiding this comment.
SRP - validate function
- actual: API body validation is handled directly in the API route
- required: API body validation has a standalone lib. See other API routes for an example.
| return getToolResultSuccess({ | ||
| success: true, | ||
| message: `Saved "${result.audioFile.fileName}" and "${result.transcriptFile.fileName}"`, | ||
| audioFile: result.audioFile, | ||
| transcriptFile: result.transcriptFile, | ||
| text: result.text, | ||
| language: result.language, | ||
| }); |
There was a problem hiding this comment.
KISS principle
- actual: manually destructuring result in call to getToolResultSuccess
- required: pass raw response to getToolResult. see other tool files for reference.
| export interface FileRecord { | ||
| id: string; | ||
| owner_account_id: string; | ||
| artist_account_id: string; | ||
| storage_key: string; | ||
| file_name: string; | ||
| mime_type: string | null; | ||
| size_bytes: number | null; | ||
| description: string | null; | ||
| tags: string[]; | ||
| } | ||
|
|
||
| export interface CreateFileRecordParams { | ||
| ownerAccountId: string; | ||
| artistAccountId: string; | ||
| storageKey: string; | ||
| fileName: string; | ||
| mimeType?: string | null; | ||
| sizeBytes?: number | null; | ||
| description?: string | null; | ||
| tags?: string[]; | ||
| } |
There was a problem hiding this comment.
DRY Principle
- actual: redeclaring supabase types.
- required: delete these new types and directly reference the supabase schema.
| function getExtensionFromContentType(contentType: string): string { | ||
| if (contentType.includes("wav")) return "wav"; | ||
| if (contentType.includes("m4a") || contentType.includes("mp4")) return "m4a"; | ||
| if (contentType.includes("webm")) return "webm"; | ||
| return "mp3"; | ||
| } |
There was a problem hiding this comment.
Single Responsibility Principle
- actual: multiple functions defined in one, single, file
- required: new function file for getExtensionFromContentType
| export function formatTranscriptionError(error: unknown): { message: string; status: number } { | ||
| const rawMessage = error instanceof Error ? error.message : "Transcription failed"; | ||
|
|
||
| if (rawMessage.includes("OPENAI_API_KEY")) { | ||
| return { message: "OpenAI API key is not configured", status: 500 }; | ||
| } | ||
| if (rawMessage.includes("fetch audio") || rawMessage.includes("Failed to fetch")) { | ||
| return { message: "Could not fetch the audio file. Please check the URL is accessible.", status: 400 }; | ||
| } | ||
| if (rawMessage.includes("25 MB") || rawMessage.includes("file size")) { | ||
| return { message: "Audio file exceeds the 25MB limit", status: 413 }; | ||
| } | ||
| if (rawMessage.includes("rate limit")) { | ||
| return { message: "Rate limit exceeded. Please try again later.", status: 429 }; | ||
| } | ||
|
|
||
| return { message: rawMessage, status: 500 }; | ||
| } |
There was a problem hiding this comment.
Single Responsibility Principle
- actual: function defined in types file
- required: new lib for formatTranscriptionError
| * Shared constants for Recoup-API | ||
| */ | ||
|
|
||
| export const SUPABASE_STORAGE_BUCKET = "user-files"; |
There was a problem hiding this comment.
DRY principle
- actual: creating new consts file
- required: use the existing recoup-api/lib/const.ts file
Summary by CodeRabbit
New Features
Documentation
Storage & Upload
✏️ Tip: You can customize this high-level summary in your review settings.