-
Notifications
You must be signed in to change notification settings - Fork 945
google drive integration #1606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
google drive integration #1606
Conversation
@AshishViradiya153 is attempting to deploy a commit to the mftsio Team on Vercel. A member of the Team first needs to authorize it. |
@coderabbitai full review |
✅ Actions performedFull review triggered. |
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis update introduces comprehensive Google Drive integration into the application. It adds OAuth-based connection management, file/folder selection via Google Picker, recursive folder/file import and upload, and batch upload progress tracking. The backend supports Google Drive file/folder metadata, upload sessions, folder structure creation, and document association. UI components and database schema are extended to accommodate these new features, including integration management in settings. Changes
Sequence Diagram(s)Google Drive File Import and Upload FlowsequenceDiagram
participant User
participant AddDocumentModal
participant GoogleDrivePicker
participant BackendAPI
participant UploadContext
participant UploadNotificationDrawer
User->>AddDocumentModal: Opens modal
AddDocumentModal->>User: Shows tabs (Document, Google Drive, Notion)
User->>AddDocumentModal: Selects "Google Drive" tab
AddDocumentModal->>GoogleDrivePicker: Initiates file/folder picker
GoogleDrivePicker->>User: User selects files/folders
GoogleDrivePicker->>AddDocumentModal: Returns selected items
AddDocumentModal->>BackendAPI: POST /api/integrations/google-drive/list-files
BackendAPI->>AddDocumentModal: Returns folder tree & file metadata
AddDocumentModal->>User: Shows confirmation dialog (file/folder count)
User->>AddDocumentModal: Confirms upload
AddDocumentModal->>UploadContext: startUpload(treeFiles, filesList, etc.)
UploadContext->>BackendAPI: Triggers batch upload (Trigger.dev)
UploadContext->>UploadNotificationDrawer: Updates upload progress
UploadNotificationDrawer->>User: Displays upload status
UploadContext->>User: Notifies on completion/errors
Google Drive Integration Management FlowsequenceDiagram
participant User
participant GoogleDriveIntegration
participant BackendAPI
User->>GoogleDriveIntegration: Clicks "Connect"
GoogleDriveIntegration->>BackendAPI: GET /api/integrations/google-drive/generate-state
GoogleDriveIntegration->>User: Redirects to Google OAuth
User->>BackendAPI: Google OAuth callback (/api/integrations/google-drive/callback)
BackendAPI->>GoogleDriveIntegration: Stores tokens, updates status
User->>GoogleDriveIntegration: Sees connected status
User->>GoogleDriveIntegration: Clicks "Disconnect"
GoogleDriveIntegration->>BackendAPI: POST /api/integrations/google-drive/disconnect
BackendAPI->>GoogleDriveIntegration: Revokes tokens, removes integration
GoogleDriveIntegration->>User: Updates to disconnected status
Poem
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 29
🧹 Nitpick comments (26)
lib/utils/node-pdf-page-counter.ts (2)
1-39
: Strong implementation with multiple fallback methods for PDF page counting.The function employs a multi-method approach to determine PDF page count, gracefully falling back to alternative methods when preferred ones fail. This is robust for handling different PDF structures.
Two minor improvements using optional chaining:
- if (countMatch && countMatch[1]) { + if (countMatch?.[1]) {- if (nMatch && nMatch[1]) { + if (nMatch?.[1]) {🧰 Tools
🪛 Biome (1.9.4)
[error] 9-9: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 26-26: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
35-38
: Consider more specific error handling.The current implementation catches all errors and defaults to returning 1 page. Consider adding more specific error types or logging additional details to help with debugging in production.
} catch (error) { - console.error('Error counting PDF pages:', error); + console.error('Error counting PDF pages:', + error instanceof Error ? `${error.name}: ${error.message}` : String(error) + ); return 1; }lib/documents/create-document.ts (1)
21-22
: Potential duplication in Google Drive file ID handling.The function accepts a separate
googleDriveFileId
parameter despitedocumentData
already containing this property. This could lead to confusion if the values differ.Consider one of these approaches:
- Remove the separate parameter and use only
documentData.googleDriveFileId
- Document which parameter takes precedence if both are provided
export const createDocument = async ({ documentData, teamId, numPages, folderPathName, createLink = false, token, - googleDriveFileId }: { documentData: DocumentData; teamId: string; numPages?: number; folderPathName?: string; createLink?: boolean; token?: string; - googleDriveFileId?: string; }) => { // ... body: JSON.stringify({ // ... - googleDriveFileId: googleDriveFileId, + googleDriveFileId: documentData.googleDriveFileId, }), // ... };Also applies to: 29-30
prisma/schema/schema.prisma (2)
190-203
: Index (or unique)googleDriveFileId
for fast look-ups & deduping
googleDriveFileId
will be queried frequently during upload → document sync and must be unique within a workspace. Consider:@@ model Document googleDriveFileId String? + + @@index([googleDriveFileId]) + // or @@unique([teamId, googleDriveFileId]) if duplicates must be preventedWithout an index each sync iteration results in full-table scans on
Document
, hampering large tenants.
414-417
: Add an index forgoogleDriveFolderId
similar to documentsFolder synchronisation will need quick mapping
driveFolderId → Folder
. Add an index / uniqueness constraint analogous to the document suggestion to avoid O(n) scans.components/minimized-upload-progress.tsx (3)
41-45
:latestActiveUpload
picks the first active upload, not the latest
Array.find
stops at the first match. If older uploads remain “active”, the newest one won’t show. Sort bytimestamp
(or iterate in reverse) to display the most recent:-const latestActiveUpload = uploads.find((u) => +const latestActiveUpload = [...uploads] + .filter((u) => ["uploading", "pending", "initializing", "processing"].includes(u.status)) + .sort((a, b) => b.timestamp - a.timestamp)[0];
47-52
: Average progress should weight by file sizeA 1 GB file at 10 % and a 1 KB file at 100 % currently report 55 % average, misleading users. If
bytesTotal
is available, compute a weighted mean:-const averageProgress = uploads.length > 0 - ? uploads.reduce((sum, upload) => sum + upload.progress, 0) / uploads.length - : 0; +const totalBytes = uploads.reduce((s, u) => s + (u.bytesTotal ?? 0), 0); +const averageProgress = + totalBytes > 0 + ? uploads.reduce((s, u) => s + (u.progress * (u.bytesTotal ?? 0)), 0) / + totalBytes + : 0;
78-116
: DRY: wrap both icon buttons in a singleTooltipProvider
Two nested
TooltipProvider
s are unnecessary; place one higher to avoid duplicate context providers:- <div className="flex items-center space-x-1"> - <TooltipProvider> + <TooltipProvider> + <div className="flex items-center space-x-1"> ... - </TooltipProvider> - <TooltipProvider> ... - </TooltipProvider> - </div> + </div> + </TooltipProvider>pages/api/integrations/google-drive/callback.ts (1)
67-69
: Avoid cross-layer imports (scope
from a React component)
callback.ts
pullsscope
fromcomponents/integrations/google-drive/google-drive
, coupling API code to a frontend file. This risks cyclic imports and bloats the server bundle. Export the scope list from a shared util (e.g.,lib/google-drive/constants.ts
) and import it in both places.lib/context/upload-context.tsx (2)
37-47
: Remove debug code before production deployment.The console debug statements and TODO comment should be removed before pushing to production. These logs expose internal state information that should not be present in production code.
- // TODO : REMOVE THIS - if (uploads.length > 0) { - console.debug("Upload state updated:", { - count: uploads.length, - statuses: uploads.map((u) => ({ - id: u.fileId, - status: u.status, - progress: u.progress, - })), - }); - }
30-31
: Consider documenting the purpose of local upload state.The component maintains a separate
uploadState
instead of directly usinguploads
from the hook. Adding a comment explaining why this local state is necessary would improve code maintainability.- // Force re-render when uploads change + // Maintain local state copy to ensure component re-renders when upload progress updates + // This is needed because the uploads array reference from the hook might not change + // even when the contents (progress values) change const [uploadState, setUploadState] = useState<UploadProgress[]>([]);pages/api/integrations/google-drive/batch-upload.ts (2)
63-63
: Use optional chaining for cleaner conditional checks.The static analysis tool correctly suggests using optional chaining here for better code readability.
- if (handle && handle.id && handle.publicAccessToken) { + if (handle?.id && handle?.publicAccessToken) {🧰 Tools
🪛 Biome (1.9.4)
[error] 63-63: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
36-36
: Remove unnecessary empty line.For code consistency, remove this empty line.
const { treeFiles, filesList, path, dataroomId, teamId } = req.body as { treeFiles: ProcessedFile, filesList: Files[], path: string, dataroomId: string, teamId: string }; - if (filesList.length === 0) {
pages/api/integrations/google-drive/list-files.ts (4)
46-48
: Remove console.log statements before production.Debug logging should be removed before deploying to production environments.
- console.log("----------------folderPathName ----------------"); - console.log('folderPathName', folderPathName); - console.log("----------------folderPathName ----------------");
69-71
: Remove console.log statements before production.Debug logging should be removed before deploying to production environments.
- console.log("----------------files----------------"); - console.log('files', { files, fileArray }); - console.log("----------------files----------------");
75-77
: Remove console.log statements before production.Debug logging should be removed before deploying to production environments.
- console.log("----------------tree----------------"); - console.log('tree', tree); - console.log("----------------tree----------------");
57-66
: Consider collecting errors instead of failing on the first error.The current implementation returns an error response if any single folder fails to list files. Consider collecting errors and continuing with successfully processed folders.
+ const errors: Record<string, string> = {}; for (const folderId of folderIds) { try { const file = await googleDrive.listFiles(driveClient, folderId); files.push(...file); } catch (error) { console.error(`Error listing files for folder ${folderId}:`, error); - return res.status(500).json({ - error: 'Failed to list files', - message: error instanceof Error ? error.message : 'Unknown error' - }); + errors[folderId] = error instanceof Error ? error.message : 'Unknown error'; } } + + // If all folders failed, return an error + if (Object.keys(errors).length === folderIds.length) { + return res.status(500).json({ + error: 'Failed to list files from all folders', + errors + }); + }prisma/migrations/20250424171651_google_drive/migration.sql (1)
2-12
: Missing indexes on the new googleDriveFileId / googleDriveFolderId columnsThese IDs will be used in look-ups during document/folder sync. Add non-unique B-tree indexes to avoid full-table scans:
CREATE INDEX "Document_googleDriveFileId_idx" ON "Document"("googleDriveFileId"); CREATE INDEX "DataroomDocument_googleDriveFileId_idx" ON "DataroomDocument"("googleDriveFileId"); CREATE INDEX "Folder_googleDriveFolderId_idx" ON "Folder"("googleDriveFolderId"); CREATE INDEX "DataroomFolder_googleDriveFolderId_idx" ON "DataroomFolder"("googleDriveFolderId");lib/files/tus-upload.ts (1)
56-63
: Metadata values must be ASCII strings
tus
spec limits metadata values to ASCII.googleDriveFileId
can contain non-ASCII characters if users create folders with Unicode; encode withencodeURIComponent
to stay spec-compliant.- googleDriveFileId: googleDriveFileId || "", + googleDriveFileId: googleDriveFileId ? encodeURIComponent(googleDriveFileId) : "",components/integrations/google-drive/google-drive-picker.tsx (2)
129-137
:MULTISELECT_ENABLED
should depend on themultiple
propThe picker is always created with multiselect enabled, even when
multiple === false
, which is confusing from a UX standpoint and can break downstream logic.- .enableFeature(google.picker.Feature.MULTISELECT_ENABLED) + {multiple && builder.enableFeature(google.picker.Feature.MULTISELECT_ENABLED)}(or conditionally call
enableFeature
before.build()
).
152-170
: Treating a plain cancel as an errorInside the Picker callback,
Action.CANCEL
is routed throughhandleError
, showing a toast for what is actually a normal user action.
Only invokehandleError
ifdata[google.picker.Response.ERROR]
exists; otherwise just silently reset the state.lib/hooks/use-google-drive-upload.ts (1)
11-20
: Duplicate / unused status values
UploadStatus
already contains"failed"
and"error"
, yetmapStatusFromServer
never returns"failed"
– the server’s"failed"
string is mapped to"error"
. This makes"failed"
impossible at runtime and complicates consumers that have to handle both.Consider unifying:
export type UploadStatus = | "pending" | "initializing" | "uploading" | "processing" | "completed" - | "failed" - | "error" + | "failed" // single failure state | "unknown"; … case "failed": return "failed";That simplification propagates through reducers & UI components.
Also applies to: 33-41
components/documents/add-document-modal.tsx (2)
94-100
:selectedGoogleDriveFile
is never read – remove dead state to keep component leanThe local state
selectedGoogleDriveFile
is created but never used anywhere in the component.
Keeping unused state hurts readability and triggers unnecessary re-renders when it’s updated.- const [selectedGoogleDriveFile, setSelectedGoogleDriveFile] = useState<{ - id: string; - name: string; - mimeType: string; - size: number; - } | null>(null);If you plan to leverage it later, please add the corresponding logic; otherwise, delete it together with the
setSelectedGoogleDriveFile
call inclearModelStates
.
744-748
: UI message can mislead when Drive is disconnected but finished loading
isConnected || !isLoading
will display “Select a file…” even if the user is not connected (isConnected === false
) but the fetch finished (isLoading === false
). This is the typical state right after a failed/expired connection.- {isConnected || !isLoading + {isConnectedor explicitly
{isConnected ? "Select a file ..." : "Sync your Google Drive account ..."}This avoids confusing users with an unavailable action.
lib/files/process-and-upload.ts (2)
160-166
:folderMap
is declared but never populated – dead code / potential logic driftA
folderMap
is initialised but noset()
calls follow. Either remove it or populate it (e.g., while walkingfolderTree
). Keeping unused maps adds cognitive noise and hints at an incomplete feature.
86-115
: Possible race when two workers create the same folder concurrentlyThe uniqueness check loops with
findFolderRecord
/createFolderRecord
. Without a DB-side unique index(parentId, name)
two parallel uploads might still create duplicate folders between thefind
andcreate
.If not already present, add a DB unique constraint and be prepared to catch
PrismaClientKnownRequestError
P2002
to retry with the incremented suffix.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (39)
components/documents/add-document-modal.tsx
(4 hunks)components/documents/upload-notification-drawer.tsx
(1 hunks)components/google-drive-upload-progress.tsx
(1 hunks)components/integrations/google-drive/google-drive-picker.tsx
(1 hunks)components/integrations/google-drive/google-drive.tsx
(1 hunks)components/minimized-upload-progress.tsx
(1 hunks)components/settings/settings-header.tsx
(1 hunks)components/shared/icons/google-drive.tsx
(1 hunks)components/ui/badge.tsx
(1 hunks)components/ui/confirmation-dialog.tsx
(1 hunks)lib/api/documents/process-document.ts
(3 hunks)lib/auth/gdrive-upload-session.ts
(1 hunks)lib/context/upload-context.tsx
(1 hunks)lib/documents/create-document.ts
(3 hunks)lib/files/file-utils.ts
(1 hunks)lib/files/process-and-upload.ts
(1 hunks)lib/files/tus-upload.ts
(4 hunks)lib/google-drive.ts
(1 hunks)lib/hooks/use-google-drive-token.ts
(1 hunks)lib/hooks/use-google-drive-upload.ts
(1 hunks)lib/swr/use-google-drive-integration.ts
(1 hunks)lib/trigger/batch-file-upload.ts
(1 hunks)lib/utils/node-pdf-page-counter.ts
(1 hunks)package.json
(1 hunks)pages/_app.tsx
(3 hunks)pages/api/file/browser-upload.ts
(1 hunks)pages/api/integrations/google-drive/batch-upload.ts
(1 hunks)pages/api/integrations/google-drive/callback.ts
(1 hunks)pages/api/integrations/google-drive/disconnect.ts
(1 hunks)pages/api/integrations/google-drive/generate-state.ts
(1 hunks)pages/api/integrations/google-drive/index.tsx
(1 hunks)pages/api/integrations/google-drive/list-files.ts
(1 hunks)pages/api/integrations/google-drive/upload-session.ts
(1 hunks)pages/api/teams/[teamId]/datarooms/[id]/documents/index.ts
(5 hunks)pages/api/teams/[teamId]/documents/index.ts
(3 hunks)pages/settings/integrations.tsx
(1 hunks)prisma/migrations/20250424171651_google_drive/migration.sql
(1 hunks)prisma/schema/dataroom.prisma
(2 hunks)prisma/schema/schema.prisma
(4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (12)
lib/swr/use-google-drive-integration.ts (2)
components/integrations/google-drive/google-drive.tsx (1)
GoogleDriveIntegration
(110-262)lib/utils.ts (1)
fetcher
(34-48)
pages/_app.tsx (5)
components/ui/tooltip.tsx (1)
TooltipProvider
(144-144)lib/constants.ts (1)
EXCLUDED_PATHS
(56-56)lib/context/upload-context.tsx (1)
UploadProvider
(27-61)components/documents/upload-notification-drawer.tsx (1)
UploadNotificationDrawer
(9-60)components/theme-provider.tsx (1)
ThemeProvider
(10-12)
pages/settings/integrations.tsx (3)
components/layouts/app.tsx (1)
AppLayout
(9-29)components/settings/settings-header.tsx (1)
SettingsHeader
(8-88)components/integrations/google-drive/google-drive.tsx (1)
GoogleDriveIntegration
(110-262)
pages/api/integrations/google-drive/disconnect.ts (4)
pages/api/integrations/google-drive/index.tsx (1)
handler
(10-70)pages/api/auth/[...nextauth].ts (1)
authOptions
(25-189)lib/types.ts (1)
CustomUser
(18-18)lib/google-drive.ts (1)
GoogleDriveClient
(10-238)
lib/context/upload-context.tsx (2)
lib/hooks/use-google-drive-upload.ts (2)
UploadProgress
(49-65)useGoogleDriveUpload
(67-533)components/documents/add-document-modal.tsx (2)
ProcessedFile
(50-62)Files
(64-69)
lib/auth/gdrive-upload-session.ts (1)
lib/redis.ts (1)
redis
(4-7)
components/minimized-upload-progress.tsx (4)
lib/hooks/use-google-drive-upload.ts (1)
UploadProgress
(49-65)components/ui/tooltip.tsx (4)
TooltipProvider
(144-144)Tooltip
(139-139)TooltipTrigger
(140-140)TooltipContent
(143-143)components/ui/button.tsx (1)
Button
(71-71)components/ui/progress.tsx (1)
Progress
(61-61)
lib/google-drive.ts (1)
lib/constants.ts (1)
SUPPORTED_DOCUMENT_MIME_TYPES
(63-90)
lib/hooks/use-google-drive-upload.ts (1)
components/documents/add-document-modal.tsx (2)
ProcessedFile
(50-62)Files
(64-69)
components/integrations/google-drive/google-drive-picker.tsx (1)
components/ui/button.tsx (1)
Button
(71-71)
components/documents/add-document-modal.tsx (9)
lib/context/upload-context.tsx (1)
useUpload
(63-69)ee/limits/swr-handler.ts (1)
useLimits
(18-44)lib/swr/use-billing.ts (1)
usePlan
(72-104)components/integrations/google-drive/google-drive.tsx (2)
useGoogleDriveStatus
(30-52)GoogleDriveIntegration
(110-262)components/integrations/google-drive/google-drive-picker.tsx (1)
GoogleDrivePicker
(40-217)components/ui/card.tsx (5)
Card
(80-80)CardHeader
(81-81)CardTitle
(83-83)CardDescription
(84-84)CardContent
(85-85)components/ui/button.tsx (1)
Button
(71-71)lib/constants.ts (1)
SUPPORTED_DOCUMENT_MIME_TYPES
(63-90)components/ui/confirmation-dialog.tsx (1)
ConfirmationDialog
(27-83)
lib/files/process-and-upload.ts (5)
components/documents/add-document-modal.tsx (1)
ProcessedFile
(50-62)lib/files/file-utils.ts (2)
findFolderRecord
(33-54)createFolderRecord
(4-29)lib/google-drive.ts (1)
GoogleDriveClient
(10-238)lib/utils/node-pdf-page-counter.ts (1)
countPdfPages
(1-39)lib/files/tus-upload.ts (1)
resumableUpload
(30-121)
🪛 Biome (1.9.4)
lib/utils/node-pdf-page-counter.ts
[error] 9-9: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
[error] 26-26: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
pages/_app.tsx
[error] 13-13: Shouldn't redeclare 'EXCLUDED_PATHS'. Consider to delete it or rename it.
'EXCLUDED_PATHS' is defined here:
(lint/suspicious/noRedeclare)
pages/api/integrations/google-drive/batch-upload.ts
[error] 63-63: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
components/documents/add-document-modal.tsx
[error] 574-574: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
lib/files/process-and-upload.ts
[error] 263-263: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🔇 Additional comments (70)
components/ui/badge.tsx (1)
18-19
: Good addition of a success variant for badges.The new success variant with green styling will help provide clear visual feedback to users during Google Drive integration operations. This aligns well with the upload progress components mentioned in the summary.
package.json (1)
88-88
:✅ Verification successful
Verify googleapis version compatibility.
The addition of googleapis (v148.0.0) is appropriate for the Google Drive integration. Let's verify this is a stable version without known vulnerabilities.
🌐 Web query:
Are there any known issues or vulnerabilities with googleapis version 148.0.0?
💡 Result:
As of my knowledge cutoff in June 2024, there is no specific information regarding vulnerabilities in googleapis version 148.0.0. The latest available release at that time was version 144.0.0, which included breaking changes and updates to various APIs. (googleapis.dev)
It's important to note that vulnerabilities can be discovered after my last update. To ensure the security of your application, I recommend:
Reviewing the Changelog: Check the official googleapis changelog for any updates or security advisories related to version 148.0.0.
Consulting Security Databases: Utilize platforms like Snyk or GitHub Security Advisories to search for known vulnerabilities in the googleapis package.
Testing Your Application: Regularly run security scans and tests on your application to identify and mitigate potential vulnerabilities.
By staying informed and proactive, you can help maintain the security and integrity of your application.
Citations:
No known vulnerabilities or compatibility issues with googleapis@148.0.0
As of May 2025, there are no publicly reported security advisories for version 148.0.0 and it aligns with the latest stable release. To finalize:
- Review the official changelog for any breaking changes:
https://googleapis.dev/nodejs/googleapis/latest/docs/changelog.html- Run a security scan (e.g.,
npm audit
, Snyk or GitHub Security Advisories) to catch any newly discovered issues.This dependency is safe to merge.
pages/api/file/browser-upload.ts (1)
15-15
: Simple descriptive comment added.The added comment helps identify the API route, which is good for codebase documentation.
components/settings/settings-header.tsx (1)
61-65
: Navigation item for Google Drive integration looks good!The new "Integrations" menu item is properly positioned between "Billing" and "Tokens" in the settings navigation. This addition follows the existing pattern and provides access to the new Google Drive integration features.
prisma/schema/dataroom.prisma (2)
39-39
: Schema extension for Google Drive file ID looks good.The optional
googleDriveFileId
field appropriately allows for linking DataroomDocument objects to their corresponding Google Drive files.
65-65
: Schema extension for Google Drive folder ID looks good.The optional
googleDriveFolderId
field appropriately allows for linking DataroomFolder objects to their corresponding Google Drive folders.pages/api/teams/[teamId]/documents/index.ts (2)
254-255
: API handler now accepts Google Drive file ID.The document creation API properly accepts the optional
googleDriveFileId
parameter in the request body. This aligns with the database schema changes.Also applies to: 265-266
289-290
: Google Drive file ID is correctly passed to the document processor.The
googleDriveFileId
parameter is correctly forwarded to theprocessDocument
function.lib/documents/create-document.ts (4)
3-12
: DocumentData interface properly extended for Google Drive.Converting from a type alias to an interface and adding the optional
googleDriveFileId
property correctly supports the Google Drive integration.
50-51
: Google Drive file ID included in document creation payload.The
googleDriveFileId
is correctly included in the JSON body sent to the backend API.
62-96
:❓ Verification inconclusive
Consider adding Google Drive support to createAgreementDocument.
The
createAgreementDocument
function hasn't been updated to handle Google Drive files. If agreement documents can come from Google Drive, consider adding similar support to this function.
🏁 Script executed:
#!/bin/bash # Check if agreement documents can come from Google Drive rg -A 3 "createAgreementDocument.*googleDrive" --type tsLength of output: 58
Verify Google Drive support in createAgreementDocument
I searched the codebase and found no references to Google Drive in
createAgreementDocument
. If your app should accept agreement files sourced from Google Drive, you’ll need to extend this function to:
- Detect Google Drive inputs (e.g. a Drive file ID or URL)
- Fetch the file metadata/content via the Drive API
- Generate or store a shareable link or blob URL
- Populate the POST body (
url
,storageType
, etc.) accordinglyPlease confirm whether agreement documents may originate from Google Drive and, if so, implement these steps in
lib/documents/create-document.ts
(lines 62–96).
98-133
:❌ Incorrect review comment
Consider adding Google Drive support to createNewDocumentVersion.
The
createNewDocumentVersion
function hasn't been updated to handle Google Drive files. If document versions can be updated from Google Drive, consider adding similar support to this function.
🏁 Script executed:
#!/bin/bash # Check if document versions can come from Google Drive rg -A 3 "createNewDocumentVersion.*googleDrive" --type tsLength of output: 59
🏁 Script executed:
#!/bin/bash # Search for DocumentData type or interface definitions rg -A 5 -B 2 "type DocumentData" --type ts rg -A 5 -B 2 "interface DocumentData" --type ts # Check possible values for storageType within DocumentData rg -A 2 "storageType" --type tsLength of output: 18973
Ignore Google Drive support suggestion
There’s no existing Google Drive integration in theDocumentStorageType
enum or elsewhere in the codebase. SincecreateNewDocumentVersion
only handles storage types defined in Prisma (e.g., S3_PATH, VERCEL_BLOB), adding “Google Drive” isn’t applicable unless you introduce it as a new storage option.Likely an incorrect or invalid review comment.
components/shared/icons/google-drive.tsx (1)
1-39
: Well-implemented Google Drive icon component.The SVG icon implementation looks good with appropriate sizing, viewBox, and color specifications that accurately represent the Google Drive logo. The component accepts an optional className prop for styling flexibility.
lib/api/documents/process-document.ts (3)
23-23
: Google Drive integration parameter properly documented.Good addition of the Google Drive file ID parameter with clear documentation in the comment.
34-34
: Parameter default value appropriately set.The default value of
null
for the optional Google Drive file ID parameter is appropriate.
113-113
: Conditional inclusion of Google Drive file ID is well implemented.The use of a conditional spread operator is an elegant approach to include the Google Drive file ID only when it exists.
pages/_app.tsx (2)
77-77
: Good addition of viewport meta tag.Adding the viewport meta tag is a good practice for responsive design.
94-97
: Well-integrated UploadProvider and notification drawer.The UploadProvider is properly nested within the TeamProvider and wraps both the main component and the notification drawer. The UploadNotificationDrawer component is correctly positioned as a sibling to maintain a consistent UI hierarchy.
lib/swr/use-google-drive-integration.ts (2)
6-9
: Well-typed integration response interface.The GoogleDriveIntegrationResponse type properly defines the structure of the integration data and connection status.
11-17
: Well-implemented SWR hook for Google Drive integration.The hook follows best practices by using SWR with proper typing and configuration. Disabling revalidation on focus is appropriate for this type of integration data. The hook cleanly exposes all necessary properties (data, error, isLoading, mutate) for consumers.
pages/settings/integrations.tsx (3)
1-6
: Import statements look good.The imports are well organized, separating the hook from the components with a blank line. Imports from the project are properly prefixed with
@/
.
7-9
: Component structure is well-defined.The component is correctly exported as default and uses the
useTeam
hook to access team context.
10-32
: Well-structured layout following application patterns.The component follows the existing application layout patterns, using
AppLayout
as a wrapper and properly structuring the content with appropriate spacing and typography classes. The page is well-organized with a clear hierarchy and consistent styling with other settings pages.The grid layout at line 25 is prepared for multiple integrations, though currently only contains the Google Drive integration.
pages/api/integrations/google-drive/generate-state.ts (4)
1-8
: Imports are properly organized.The imports are well-structured, grouped logically with a blank line separating different types of imports.
9-12
: Handler function signature is correct.The function is properly exported as default with the correct Next.js API handler typing.
21-24
: Secure state generation for OAuth flow.The state parameter is securely generated using a combination of user email, timestamp, and a checksum. This follows security best practices for OAuth state parameters to prevent CSRF attacks.
26-30
: Method validation is correctly implemented.The endpoint properly handles method validation, returning a 405 status and setting the Allow header for unsupported methods.
pages/api/integrations/google-drive/disconnect.ts (5)
1-7
: Imports are properly organized.The necessary dependencies for Next.js API, authentication, and Google Drive integration are correctly imported.
8-14
: API method validation is correctly implemented.The handler correctly validates that only POST requests are allowed for this endpoint.
16-22
: Authentication verification is properly implemented.The code correctly retrieves the user session and handles unauthorized access appropriately.
24-28
: Google Drive disconnection implementation is clean.The handler uses the singleton GoogleDriveClient to handle the disconnection logic and properly checks for success.
29-33
: Error handling is comprehensive.The try-catch block properly captures and logs errors, returning appropriate error responses to the client.
pages/api/integrations/google-drive/upload-session.ts (5)
1-6
: Imports are properly organized.The necessary dependencies for API routing, authentication, and session management are correctly imported.
7-18
: Authentication verification is properly implemented.The authentication check is correctly placed at the beginning of the handler to ensure all routes are protected.
19-59
: GET endpoint implementation is secure and thorough.The code properly:
- Retrieves the session token from cookies
- Validates the session exists and is valid
- Verifies the session belongs to the current user
- Clears invalid cookies when necessary
- Returns appropriate error messages and status codes
This is a robust implementation for session retrieval.
60-90
: DELETE endpoint implementation follows best practices.The session deletion logic is well-implemented:
- Verifies ownership before deleting
- Always clears the cookie regardless of session existence
- Provides appropriate success and error responses
- Handles exceptions properly
This ensures clean session cleanup.
92-95
: Method validation is correctly implemented.The handler properly returns a 405 status for unsupported HTTP methods.
pages/api/teams/[teamId]/datarooms/[id]/documents/index.ts (5)
4-4
: Proper import added for logger libraryThe addition of
logger
import from the trigger.dev SDK enhances error reporting capabilities.
105-138
: Good enhancement of authentication flowThe dual authentication method implementation is well-structured and secure:
- First tries to authenticate via API token (Bearer token)
- Falls back to session-based authentication if no token is provided
- Properly verifies that the token belongs to the correct team
This improvement enables programmatic access while maintaining session-based security.
141-147
: Enhanced document creation with Google Drive integrationThe addition of
googleDriveFileId
and configurabledelay
parameters provides excellent flexibility:
- Allows linking documents to Google Drive files
- Enables customizable notification delay with sensible default (10 minutes)
182-182
: Google Drive file ID association enabledThe document creation data now includes the Google Drive file ID, properly implementing the Google Drive integration.
227-227
: Dynamic delay configuration for notificationsReplaced hardcoded delay with configurable
delayMs
parameter, improving flexibility.components/documents/upload-notification-drawer.tsx (4)
1-14
: Well-structured component setup with proper imports and state managementThe component correctly:
- Imports necessary dependencies
- Uses the upload context to access uploads state
- Initializes local state for uploads, drawer open state, and minimized state
15-20
: Effective synchronization with upload contextThe useEffect hook properly:
- Updates local state when uploads change
- Automatically opens the drawer when uploads are added
- Prevents unnecessary re-renders with appropriate dependency array
22-35
: Proper conditional rendering and controlled closingThe component:
- Returns null when there are no uploads or when drawer is closed
- Prevents closing the drawer while uploads are in progress
- Cleans up local state when closing
37-59
: Well-implemented UI with dual display modesThe component offers:
- A minimized view for less intrusive display
- A full-featured view with detailed upload progress
- Proper positioning and animations
- Controls for minimizing, maximizing, and closing
components/ui/confirmation-dialog.tsx (4)
1-14
: Proper imports for dialog componentThe necessary imports for React, UI components, and icons are correctly included.
15-25
: Well-defined props interface with optional parametersThe component's interface:
- Provides clear required props (isOpen, onClose, onConfirm, title)
- Includes optional props with appropriate types (confirmText, cancelText, file/folder counts, loading)
- Follows TypeScript best practices
27-37
: Good component setup with default valuesThe component correctly:
- Destructures props
- Provides sensible defaults for button text
- Accepts optional file and folder counts
- Supports loading state
38-83
: Well-implemented dialog with conditional renderingThe dialog UI:
- Responds to open/close state
- Displays a header with title
- Shows conditional description based on file/folder counts with proper pluralization
- Includes footer with cancel and confirm buttons
- Handles loading state with spinner and text change
- Disables confirm button during loading
lib/hooks/use-google-drive-token.ts (3)
1-7
: Proper hook setup with state initializationThe hook correctly:
- Imports necessary React hooks
- Initializes token state with the provided initial value
- Tracks refresh operation state
8-29
: Robust token refresh implementationThe refresh function:
- Uses useCallback for performance optimization
- Properly manages loading state
- Makes an API call to refresh the token
- Handles success by updating state
- Provides comprehensive error handling with logging and user feedback
- Ensures loading state is reset even if errors occur
31-36
: Clean hook interfaceThe hook returns a well-structured object with:
- Current token value
- Token setter function
- Token refresh function
- Loading state indicator
This provides all necessary functionality for components to manage Google Drive tokens.
lib/context/upload-context.tsx (2)
13-23
: LGTM: Well-defined context type with proper TypeScript typing.The context type definition is well-structured with clear interface for managing uploads, tracking upload status, and initiating new uploads.
63-69
: LGTM: Good error handling in the custom hook.The
useUpload
hook properly throws an informative error when used outside the provider context, which will help developers identify integration issues.pages/api/integrations/google-drive/batch-upload.ts (3)
13-19
: LGTM: Well-configured secure cookie options.The cookie configuration follows security best practices with HTTP-only flag, secure in production, strict same-site policy, and reasonable expiration time.
72-81
: LGTM: Properly formatted cookie with security attributes.The Set-Cookie header is correctly constructed with all security attributes and expiration time.
49-55
:❓ Verification inconclusive
Verify file size value in production.
The file size is hardcoded to 0 and URL to an empty string, which seems like placeholders. Ensure this is intentional and works correctly with the batch upload process.
🏁 Script executed:
#!/bin/bash # Check how the batch upload process handles these placeholder values rg -A 10 "size: 0" --glob "*.ts" --glob "*.tsx"Length of output: 885
🏁 Script executed:
#!/bin/bash # Show context around the filesList.map mapping rg -A 15 -B 15 "filesList.map" pages/api/integrations/google-drive/batch-upload.tsLength of output: 1244
Ensure actual file size and URL values are provided in batch‐upload payload
We’re currently mapping each file to
files: filesList.map(file => ({ id: file.fileId, name: file.fileName, type: file.fileType, size: 0, url: "" })),– both
size
andurl
are hardcoded placeholders. Please confirm that these fields are populated downstream (e.g., in the batch‐file‐upload job or the upload session) before they’re used or returned to the client. If they aren’t, either:
- Pass the real
fileSize
(e.g.file.fileSize
) and construct the actualurl
here,- Or add a clear
// TODO
comment explaining why they remain empty and where they’ll be set.• File: pages/api/integrations/google-drive/batch-upload.ts at the
filesList.map(...)
call.pages/api/integrations/google-drive/list-files.ts (1)
83-97
: LGTM: Good error handling for Google Drive specific errors.The error handling for Google Drive connection errors is well-implemented, providing clear messages to guide the user on reconnecting or setting up their Google Drive integration.
lib/auth/gdrive-upload-session.ts (5)
9-16
: LGTM: Well-defined session data schema.The Zod schema provides strong validation for session data structure with appropriate field types.
29-30
: LGTM: Secure session token generation.Using
crypto.randomBytes(32)
creates a cryptographically secure random token with sufficient entropy.
67-69
: LGTM: Defensive programming with explicit type checking.The code safely handles parsing the session data by checking if it's a string before attempting to parse it as JSON.
72-75
: LGTM: Proper session expiration handling.The code correctly checks for expired sessions and removes them from Redis, which prevents access to expired sessions and cleans up the storage.
42-49
: LGTM: Validation before storage and proper Redis configuration.The code validates session data with the Zod schema before storing it, and correctly sets expiration in Redis using the
pxat
option.prisma/migrations/20250424171651_google_drive/migration.sql (1)
31-34
: Unique constraint limits users to a single Google account
CREATE UNIQUE INDEX "GoogleDriveIntegration_userId_key"
enforces a one-to-one mapping between User and Integration.
Is multi-account linking out of scope? If not, drop the uniqueness constraint and use(userId, id)
PK.components/google-drive-upload-progress.tsx (1)
285-303
:"success"
variant might not exist in<Badge>
components/ui/badge.tsx
usually exposesdefault | secondary | destructive | outline
. Passing an unknown variant will drop styling or throw a type error. Confirm the design-system API or replace with an existing variant (e.g.default
+ green classes).lib/files/tus-upload.ts (1)
5-16
:❌ Incorrect review comment
Buffer
uploads require a Node environment – clarify execution context
Buffer
is undefined in browsers;tus-js-client
expectsBlob
/File
on the client side. If this function is imported by both SSR and client bundles, a runtime reference toBuffer
will break the browser build.Options:
- Split the implementation (
resumableUpload.browser.ts
/.node.ts
) and let your bundler conditionally resolve the correct file.- Convert
Buffer
→Blob
before instantiatingtus.Upload
.Please verify where this helper runs and adjust typings or bundling targets accordingly.
I’d like to inspect the implementation to see if
Buffer
is ever used at runtime or only in TS types:#!/bin/bash set -e # Locate the file fd tus-upload.ts # Show the first 200 lines to check for any runtime references to `Buffer` sed -n '1,200p' lib/files/tus-upload.ts
To confirm whether
Buffer
is only in the TS type or actually used at runtime, let’s search for anyBuffer
references and inspect the upload logic:
🏁 Script executed:
#!/bin/bash set -e # 1. Check for any Buffer usage in tus-upload.ts grep -R "Buffer" -n lib/files/tus-upload.ts || echo "No Buffer occurrences found" # 2. Locate the tus.Upload instantiation to see how `file` is handled grep -R -n "new tus.Upload" -n lib/files/tus-upload.ts || echo "No tus.Upload instantiation found" # 3. (Optional) Show the full file for context if needed # sed -n '1,200p' lib/files/tus-upload.tsLength of output: 181
Let’s inspect the upload logic around line 50 to see if
Buffer
is ever referenced or handled at runtime:#!/bin/bash set -e # Show lines 30–100 around the tus.Upload instantiation sed -n '30,100p' lib/files/tus-upload.ts
Let’s inspect the upload logic around the
new tus.Upload
call:
🏁 Script executed:
#!/bin/bash set -e # Display lines 30–100 around the tus.Upload instantiation sed -n '30,100p' lib/files/tus-upload.tsLength of output: 2616
Type-only Buffer usage – no runtime break in browsers
The
Buffer
inResumableUploadParams
is purely a TypeScript annotation. After compilation, there’s noBuffer
identifier in the emitted JavaScript, and the code never calls or importsBuffer
at runtime. Thenew tus.Upload(file, …)
call simply passes thefile
argument (whether aFile
orBuffer
) totus-js-client
, which handles supported data types in each environment. You can safely ignore the browser-build concern.Likely an incorrect or invalid review comment.
lib/hooks/use-google-drive-upload.ts (2)
241-249
: Throttling may hide the final metadata updateThe
now - lastMetadataUpdate.current < 200
guard skips processing when the job is still running, but the final COMPLETED event also passes through this block. If the final update happens ≤200 ms after a previous one, UI may never receive the 100 %/completed progress.Add an unconditional branch for terminal states:
if (run.status === "COMPLETED" || metadata.status === "completed") { lastMetadataUpdate.current = now; } else if (now - lastMetadataUpdate.current < 200) { return; }This guarantees the last snapshot reaches the UI.
418-441
:⚠️ Potential issueEarly-return paths leave
isLoading
true
startUpload
setsisUploading
only after all guard clauses. When a guard aborts (no files, duplicate upload, missing teamId) the flag never resets, blocking future uploads for this session.if (isUploading || uploadInProgress.current) { - toast.error("An upload is already in progress"); - return () => { }; + toast.error("An upload is already in progress"); + setIsUploading(false); + uploadInProgress.current = false; + return () => {}; } … if (!filesList.length) { - toast.error("No files selected"); - return () => { }; + toast.error("No files selected"); + setIsUploading(false); + uploadInProgress.current = false; + return () => {}; }Repeat for the other early exits to avoid a stuck “uploading” state.
Likely an incorrect or invalid review comment.
lib/trigger/batch-file-upload.ts (1)
223-232
: Retry counter without actual retry logicThe error message
"(Try ${retryCount}/3)"
suggests automatic retries, but the code never dispatches another upload attempt. Either implement a retry (e.g.,while (retryCount<3)
loop aroundprocessAndUploadFiles
) or remove the misleading messaging to avoid false expectations.
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Database & Schema
Documentation