Skip to content

Conversation

@Brendonovich
Copy link
Contributor

@Brendonovich Brendonovich commented Oct 15, 2025

For multipart uploads: Delete as part of completion

  • Identified as part of multipart/initiate
    For singlepart uploads: Delete in progress handler
  • Identified in upload/signed if client supports it

Summary by CodeRabbit

  • New Features

    • Explicit single-part and multipart upload modes for improved progress tracking.
    • Multipart initiation now pre-validates and sets upload state before starting.
    • Signed upload flow updates video metadata and marks single-part uploads for progress monitoring.
  • Bug Fixes

    • Prevents false or stale completion when total size is zero.
    • Returns clear 404 when a referenced video is missing.
    • More robust error handling during upload setup and URL signing.
  • UX

    • Improved video player loading logic to reduce incorrect early initialization.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 15, 2025

Walkthrough

Centralizes desktop semver checks, adds a mode enum to video_uploads, and updates upload-related API routes to join and maintain video_uploads rows (insert/update/delete) for singlepart/multipart flows; signed and multipart routes set modes and validate videos before S3 operations. Also adjusts frontend progress and player effect guards.

Changes

Cohort / File(s) Summary
Desktop utils (semver/version)
apps/web/utils/desktop.ts
New utilities: isAtLeastSemver, isFromDesktopSemver, and UPLOAD_PROGRESS_VERSION for reading X-Cap-Desktop-Version and gating upload-progress behavior.
DB schema: videoUploads mode
packages/database/schema.ts
Adds videoUploads.mode enum field (`"singlepart"
Desktop video API: progress handling
apps/web/app/api/desktop/[...route]/video.ts
Replaces manual header parsing with isFromDesktopSemver(UPLOAD_PROGRESS_VERSION); left-joins video_uploads; inserts/updates/deletes video_uploads rows based on relation presence, mode, and completion; removes legacy header/semver code; adjusts control flow for absent/present upload relations.
Signed upload route: metadata and singlepart mode
apps/web/app/api/upload/[...route]/signed.ts
Uses Db table aliases, derives/validates videoId from key/body, updates videos metadata when videoId exists, and sets video_uploads.mode = "singlepart" for result.mp4 when client supports desktop upload progress.
Multipart initiate route: early init and errors
apps/web/app/api/upload/[...route]/multipart.ts
Adds early pre-check to derive/validate videoId, upsert video_uploads.mode = "multipart", maps VideoNotFoundError to 404 JSON and returns early before S3 multipart initiation; preserves S3 try/catch flow.
Frontend progress UI tweak
apps/web/app/s/[videoId]/_components/ProgressCircle.tsx
Changes finish condition to require total > 0 && uploaded >= total to avoid treating zero-total as immediate completion.
Frontend player effect guard
apps/web/app/s/[videoId]/_components/CapVideoPlayer.tsx
Effect guard changed from checking resolvedSrc.data?.url to resolvedSrc.isPending; dependency updated from [hasPlayedOnce, resolvedSrc.data?.url] to [hasPlayedOnce, resolvedSrc.isPending].

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant D as Desktop Client
  participant API as /api/desktop/.../video
  participant DB as Database

  D->>API: POST create/progress (X-Cap-Desktop-Version)
  API->>API: isFromDesktopSemver(UPLOAD_PROGRESS_VERSION)?
  API->>DB: SELECT video LEFT JOIN video_uploads
  alt No upload row
    API->>DB: INSERT video_uploads (init state)
  else Existing row
    alt singlepart complete
      API->>DB: DELETE video_uploads row
    else update in-progress
      API->>DB: UPDATE uploaded/total/updatedAt
    end
  end
  API-->>D: JSON response
Loading
sequenceDiagram
  autonumber
  participant C as Client
  participant S as /api/upload/.../signed
  participant DB as Database
  participant S3 as S3

  C->>S: GET presigned URL (fileKey)
  S->>S: Parse videoId, validate presence
  alt valid videoId
    S->>DB: UPDATE videos (duration,width,height,fps)
    opt fileKey == "result.mp4" AND isFromDesktopSemver(UPLOAD_PROGRESS_VERSION)
      S->>DB: UPDATE/UPSERT video_uploads.mode = "singlepart"
    end
  end
  S->>S3: Create presigned URL
  S3-->>S: URL
  S-->>C: URL
Loading
sequenceDiagram
  autonumber
  participant C as Client
  participant M as /api/upload/.../multipart:initiate
  participant DB as Database
  participant S3 as S3

  C->>M: POST initiate multipart (fileKey/body)
  M->>M: Derive videoId, validate
  alt Video not found
    M-->>C: 404 JSON (Video not found)
  else Found
    M->>DB: UPSERT video_uploads.mode = "multipart"
    M->>S3: Create multipart upload
    S3-->>M: UploadId, parts info
    M-->>C: JSON (UploadId, parts)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I thump a paw, commit in flight,
Modes hop in — single, multi — bright.
Semvers checked with whisker pride,
Upload rows tended, side by side.
Carrots cheered, progress now in sight. 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly captures the main change of introducing and distinguishing videoUpload modes to improve progress reporting accuracy, reflecting the core schema and logic updates made across the pull request.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch more-accurate-progress-reporting

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f5ea990 and f8ee04a.

📒 Files selected for processing (5)
  • apps/web/app/api/desktop/[...route]/video.ts (3 hunks)
  • apps/web/app/api/upload/[...route]/multipart.ts (1 hunks)
  • apps/web/app/api/upload/[...route]/signed.ts (3 hunks)
  • apps/web/utils/desktop.ts (1 hunks)
  • packages/database/schema.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Use strict TypeScript and avoid any; leverage shared types

Files:

  • packages/database/schema.ts
  • apps/web/utils/desktop.ts
  • apps/web/app/api/desktop/[...route]/video.ts
  • apps/web/app/api/upload/[...route]/multipart.ts
  • apps/web/app/api/upload/[...route]/signed.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • packages/database/schema.ts
  • apps/web/utils/desktop.ts
  • apps/web/app/api/desktop/[...route]/video.ts
  • apps/web/app/api/upload/[...route]/multipart.ts
  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/utils/desktop.ts
  • apps/web/app/api/desktop/[...route]/video.ts
  • apps/web/app/api/upload/[...route]/multipart.ts
  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and fetching in the web app
Mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData
Run server-side effects via the ManagedRuntime from apps/web/lib/server.ts using EffectRuntime.runPromise/runPromiseExit; do not create runtimes ad hoc
Client code should use helpers from apps/web/lib/EffectRuntime.ts (useEffectQuery, useEffectMutation, useRpcClient); never call ManagedRuntime.make inside components

Files:

  • apps/web/utils/desktop.ts
  • apps/web/app/api/desktop/[...route]/video.ts
  • apps/web/app/api/upload/[...route]/multipart.ts
  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/app/api/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/app/api/**/*.{ts,tsx}: Prefer Server Actions for API surface; when routes are necessary, implement under app/api and export only the handler from apiToHandler(ApiLive)
Construct API routes with @effect/platform HttpApi/HttpApiBuilder, declare contracts with Schema, and only export the handler
Use HttpAuthMiddleware for required auth and provideOptionalAuth for guests; avoid duplicating session lookups
Map domain errors to transport with HttpApiError.* and keep translation exhaustive (catchTags/tapErrorCause)
Inside HttpApiBuilder.group, acquire services with Effect.gen and provide dependencies via Layer.provide instead of manual provideService

Files:

  • apps/web/app/api/desktop/[...route]/video.ts
  • apps/web/app/api/upload/[...route]/multipart.ts
  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Server components needing Effect services must call EffectRuntime.runPromise(effect.pipe(provideOptionalAuth))

Files:

  • apps/web/app/api/desktop/[...route]/video.ts
  • apps/web/app/api/upload/[...route]/multipart.ts
  • apps/web/app/api/upload/[...route]/signed.ts
🧬 Code graph analysis (4)
apps/web/utils/desktop.ts (1)
apps/desktop/scripts/prepare.js (1)
  • major (30-30)
apps/web/app/api/desktop/[...route]/video.ts (2)
apps/web/utils/desktop.ts (2)
  • isFromDesktopSemver (3-10)
  • UPLOAD_PROGRESS_VERSION (12-12)
packages/database/schema.ts (2)
  • videos (277-332)
  • videoUploads (710-717)
apps/web/app/api/upload/[...route]/multipart.ts (4)
packages/web-backend/src/Videos/index.ts (1)
  • Videos (12-230)
packages/web-backend/src/Database.ts (1)
  • Database (5-15)
packages/web-backend/src/Auth.ts (1)
  • provideOptionalAuth (91-113)
apps/web/lib/server.ts (1)
  • runPromise (124-136)
apps/web/app/api/upload/[...route]/signed.ts (2)
packages/database/index.ts (2)
  • db (18-25)
  • updateIfDefined (28-29)
apps/web/utils/desktop.ts (2)
  • isFromDesktopSemver (3-10)
  • UPLOAD_PROGRESS_VERSION (12-12)
⏰ 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). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/web/app/api/upload/[...route]/signed.ts (1)

183-183: The fileKey comparison never matches.

The condition fileKey === "result.mp4" will never be true because fileKey is a full path (e.g., userId/videoId/result.mp4), as confirmed by the comment on line 161 and the split logic. The singlepart mode update never runs.

Apply the suggested fix from the previous review:

-				if (fileKey === "result.mp4" && clientSupportsUploadProgress)
+				if (
+					fileKey.split("/").at(-1) === "result.mp4" &&
+					clientSupportsUploadProgress
+				)
 					await db()
 						.update(Db.videoUploads)
 						.set({ mode: "singlepart" })
 						.where(eq(Db.videoUploads.videoId, videoId));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8ee04a and f172676.

📒 Files selected for processing (1)
  • apps/web/app/api/upload/[...route]/signed.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Use strict TypeScript and avoid any; leverage shared types

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/app/api/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/app/api/**/*.{ts,tsx}: Prefer Server Actions for API surface; when routes are necessary, implement under app/api and export only the handler from apiToHandler(ApiLive)
Construct API routes with @effect/platform HttpApi/HttpApiBuilder, declare contracts with Schema, and only export the handler
Use HttpAuthMiddleware for required auth and provideOptionalAuth for guests; avoid duplicating session lookups
Map domain errors to transport with HttpApiError.* and keep translation exhaustive (catchTags/tapErrorCause)
Inside HttpApiBuilder.group, acquire services with Effect.gen and provide dependencies via Layer.provide instead of manual provideService

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and fetching in the web app
Mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData
Run server-side effects via the ManagedRuntime from apps/web/lib/server.ts using EffectRuntime.runPromise/runPromiseExit; do not create runtimes ad hoc
Client code should use helpers from apps/web/lib/EffectRuntime.ts (useEffectQuery, useEffectMutation, useRpcClient); never call ManagedRuntime.make inside components

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Server components needing Effect services must call EffectRuntime.runPromise(effect.pipe(provideOptionalAuth))

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
🧬 Code graph analysis (1)
apps/web/app/api/upload/[...route]/signed.ts (2)
packages/database/index.ts (2)
  • db (18-25)
  • updateIfDefined (28-29)
apps/web/utils/desktop.ts (2)
  • isFromDesktopSemver (3-10)
  • UPLOAD_PROGRESS_VERSION (12-12)
⏰ 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). (3)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (2)
apps/web/app/api/upload/[...route]/signed.ts (2)

7-7: LGTM: Clean namespace migration and utility imports.

The migration to the Db namespace for schema references improves clarity, and the desktop utility imports enable proper client version checking for upload progress features.

Also applies to: 18-22, 59-60, 167-167, 169-172, 175-175


164-176: LGTM: Robust video metadata update logic.

The guard on line 164 prevents errors when videoId is unavailable, and the use of updateIfDefined correctly preserves existing values for optional fields. The ownerId constraint ensures updates are properly scoped to the authenticated user.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/web/app/api/upload/[...route]/multipart.ts (2)

49-51: Extract duplicated videoId derivation logic.

The past review comment has been correctly addressed—the code now derives the actual videoId from the request instead of using user.id. However, this exact logic is duplicated in the /complete endpoint (lines 245-247).

Extract this into a shared helper function to maintain DRY:

// In a shared utility file or at the top of this file
function deriveVideoId(body: { videoId?: string; fileKey?: string }, fileKey: string): string {
  const videoIdFromFileKey = fileKey.split("/")[1];
  const videoId = "videoId" in body && body.videoId ? body.videoId : videoIdFromFileKey;
  if (!videoId) throw new Error("Video ID is required");
  return videoId;
}

Then use it in both endpoints:

-const videoIdFromFileKey = fileKey.split("/")[1];
-const videoId = "videoId" in body ? body.videoId : videoIdFromFileKey;
-if (!videoId) throw new Error("Video ID is required");
+const videoId = deriveVideoId(body, fileKey);

51-51: Consider Effect error handling for consistency.

Throwing a plain Error here is inconsistent with the Effect-based error handling used throughout the preprocessing block. While this works as an early validation, moving it inside the Effect.gen would maintain a uniform error handling pattern.

Refactor to handle the validation inside Effect.gen:

 		const fileKey = parseVideoIdOrFileKey(user.id, {
 			...body,
 			subpath: "result.mp4",
 		});
 
 		const videoIdFromFileKey = fileKey.split("/")[1];
 		const videoId = "videoId" in body ? body.videoId : videoIdFromFileKey;
-		if (!videoId) throw new Error("Video ID is required");
 
 		const resp = await Effect.gen(function* () {
+			if (!videoId) return yield* Effect.fail(new Error("Video ID is required"));
+			
 			const videos = yield* Videos;
 			const db = yield* Database;
 
 			const video = yield* videos.getById(Video.VideoId.make(videoId));
 			if (Option.isNone(video)) return yield* new Video.NotFoundError();
 
 			yield* db.use((db) =>
 				db
 					.update(Db.videoUploads)
 					.set({ mode: "multipart" })
 					.where(eq(Db.videoUploads.videoId, video.value[0].id)),
 			);
 		}).pipe(
 			provideOptionalAuth,
 			Effect.tapError(Effect.logError),
 			Effect.catchAll((e) => {
 				if (e._tag === "VideoNotFoundError")
 					return Effect.succeed<Response>(c.text("Video not found", 404));
 
 				return Effect.succeed<Response>(
 					c.json({ error: "Error initiating multipart upload" }, 500),
 				);
 			}),
 			runPromise,
 		);
 		if (resp) return resp;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d061707 and bfd47a7.

📒 Files selected for processing (1)
  • apps/web/app/api/upload/[...route]/multipart.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Use strict TypeScript and avoid any; leverage shared types

Files:

  • apps/web/app/api/upload/[...route]/multipart.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/app/api/upload/[...route]/multipart.ts
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/app/api/upload/[...route]/multipart.ts
apps/web/app/api/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/app/api/**/*.{ts,tsx}: Prefer Server Actions for API surface; when routes are necessary, implement under app/api and export only the handler from apiToHandler(ApiLive)
Construct API routes with @effect/platform HttpApi/HttpApiBuilder, declare contracts with Schema, and only export the handler
Use HttpAuthMiddleware for required auth and provideOptionalAuth for guests; avoid duplicating session lookups
Map domain errors to transport with HttpApiError.* and keep translation exhaustive (catchTags/tapErrorCause)
Inside HttpApiBuilder.group, acquire services with Effect.gen and provide dependencies via Layer.provide instead of manual provideService

Files:

  • apps/web/app/api/upload/[...route]/multipart.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and fetching in the web app
Mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData
Run server-side effects via the ManagedRuntime from apps/web/lib/server.ts using EffectRuntime.runPromise/runPromiseExit; do not create runtimes ad hoc
Client code should use helpers from apps/web/lib/EffectRuntime.ts (useEffectQuery, useEffectMutation, useRpcClient); never call ManagedRuntime.make inside components

Files:

  • apps/web/app/api/upload/[...route]/multipart.ts
apps/web/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Server components needing Effect services must call EffectRuntime.runPromise(effect.pipe(provideOptionalAuth))

Files:

  • apps/web/app/api/upload/[...route]/multipart.ts
🧬 Code graph analysis (1)
apps/web/app/api/upload/[...route]/multipart.ts (5)
packages/web-backend/src/Videos/index.ts (1)
  • Videos (12-230)
packages/web-backend/src/Database.ts (1)
  • Database (5-15)
packages/web-domain/src/Video.ts (1)
  • Video (16-59)
packages/web-backend/src/Auth.ts (1)
  • provideOptionalAuth (91-113)
apps/web/lib/server.ts (1)
  • runPromise (124-136)
⏰ 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). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (1)
apps/web/app/api/upload/[...route]/multipart.ts (1)

359-386: LGTM: Multipart completion correctly deletes videoUploads row.

The transaction properly updates video metadata and deletes the corresponding videoUploads row upon completion, which aligns with the PR objective: "For multipart uploads: delete as part of completion."

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/web/app/api/upload/[...route]/signed.ts (1)

183-187: Still need to ensure the video_uploads row exists before updating the mode

This update continues to assume a video_uploads record already exists, so single-part flows that never called multipart/initiate still end up touching 0 rows and we never mark them as "singlepart". Please insert (or upsert) the row before updating, mirroring the multipart path—e.g. insert(...).onDuplicateKeyUpdate({ set: { mode: "singlepart" } }).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2faa7d9 and dd48f37.

📒 Files selected for processing (1)
  • apps/web/app/api/upload/[...route]/signed.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Use strict TypeScript and avoid any; leverage shared types

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/app/api/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/app/api/**/*.{ts,tsx}: Prefer Server Actions for API surface; when routes are necessary, implement under app/api and export only the handler from apiToHandler(ApiLive)
Construct API routes with @effect/platform HttpApi/HttpApiBuilder, declare contracts with Schema, and only export the handler
Use HttpAuthMiddleware for required auth and provideOptionalAuth for guests; avoid duplicating session lookups
Map domain errors to transport with HttpApiError.* and keep translation exhaustive (catchTags/tapErrorCause)
Inside HttpApiBuilder.group, acquire services with Effect.gen and provide dependencies via Layer.provide instead of manual provideService

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and fetching in the web app
Mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData
Run server-side effects via the ManagedRuntime from apps/web/lib/server.ts using EffectRuntime.runPromise/runPromiseExit; do not create runtimes ad hoc
Client code should use helpers from apps/web/lib/EffectRuntime.ts (useEffectQuery, useEffectMutation, useRpcClient); never call ManagedRuntime.make inside components

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
apps/web/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Server components needing Effect services must call EffectRuntime.runPromise(effect.pipe(provideOptionalAuth))

Files:

  • apps/web/app/api/upload/[...route]/signed.ts
🧬 Code graph analysis (1)
apps/web/app/api/upload/[...route]/signed.ts (2)
packages/database/index.ts (2)
  • db (18-25)
  • updateIfDefined (28-29)
apps/web/utils/desktop.ts (2)
  • isFromDesktopSemver (3-10)
  • UPLOAD_PROGRESS_VERSION (12-12)
⏰ 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). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)

Comment on lines +18 to +22
import {
isAtLeastSemver,
isFromDesktopSemver,
UPLOAD_PROGRESS_VERSION,
} from "@/utils/desktop";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove the unused isAtLeastSemver import

isAtLeastSemver isn’t referenced anywhere in this file, so TypeScript (with noUnusedLocals) or Biome will fail the build. Please drop the unused symbol from the import.

-import {
-	isAtLeastSemver,
-	isFromDesktopSemver,
-	UPLOAD_PROGRESS_VERSION,
-} from "@/utils/desktop";
+import {
+	isFromDesktopSemver,
+	UPLOAD_PROGRESS_VERSION,
+} from "@/utils/desktop";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import {
isAtLeastSemver,
isFromDesktopSemver,
UPLOAD_PROGRESS_VERSION,
} from "@/utils/desktop";
import {
isFromDesktopSemver,
UPLOAD_PROGRESS_VERSION,
} from "@/utils/desktop";
🤖 Prompt for AI Agents
In apps/web/app/api/upload/[...route]/signed.ts around lines 18 to 22, the
import list includes isAtLeastSemver which is not used in this file; remove
isAtLeastSemver from the import statement so only isFromDesktopSemver and
UPLOAD_PROGRESS_VERSION are imported to satisfy TypeScript/Biome noUnusedLocals
rules and allow the build to pass.

@Brendonovich Brendonovich merged commit b99e6b3 into main Oct 15, 2025
15 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants