diff options
| author | Mohamed Bassem <me@mbassem.com> | 2025-12-08 10:35:17 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-12-08 10:35:17 +0000 |
| commit | 20d3761c89d566cf28ef1a22db14ad4f6eef2f17 (patch) | |
| tree | 3a665014c098603f5e631970afc66cb17586d054 /apps/web/lib/hooks | |
| parent | b6c2dadd88540eb7181b1af157ea4577157763a5 (diff) | |
| download | karakeep-20d3761c89d566cf28ef1a22db14ad4f6eef2f17.tar.zst | |
fix: check import quota before importing bookmarks (#2232)
* feat: check import quota before importing bookmarks
Add quota validation before bookmark import to prevent users from
exceeding their bookmark limits. The implementation includes:
- New QuotaService.canImportBookmarks() method to check if user can import N bookmarks
- New tRPC checkImportQuota procedure for client-side quota validation
- Updated useBookmarkImport hook to parse files and check quota before import
- Added error banner in ImportExport component to display quota errors
- Optimized file parsing to avoid reading the file twice
The quota check displays remaining bookmarks and provides clear error
messages when the import would exceed the user's quota.
* fix
* some fixes
---------
Co-authored-by: Claude <noreply@anthropic.com>
Diffstat (limited to 'apps/web/lib/hooks')
| -rw-r--r-- | apps/web/lib/hooks/useBookmarkImport.ts | 43 |
1 files changed, 42 insertions, 1 deletions
diff --git a/apps/web/lib/hooks/useBookmarkImport.ts b/apps/web/lib/hooks/useBookmarkImport.ts index d4ffda4e..0d9bbaaf 100644 --- a/apps/web/lib/hooks/useBookmarkImport.ts +++ b/apps/web/lib/hooks/useBookmarkImport.ts @@ -13,10 +13,12 @@ import { useAddBookmarkToList, useCreateBookmarkList, } from "@karakeep/shared-react/hooks/lists"; +import { api } from "@karakeep/shared-react/trpc"; import { importBookmarksFromFile, ImportSource, ParsedBookmark, + parseImportFile, } from "@karakeep/shared/import-export"; import { BookmarkTypes, @@ -36,7 +38,9 @@ export function useBookmarkImport() { const [importProgress, setImportProgress] = useState<ImportProgress | null>( null, ); + const [quotaError, setQuotaError] = useState<string | null>(null); + const apiUtils = api.useUtils(); const { mutateAsync: createImportSession } = useCreateImportSession(); const { mutateAsync: createBookmark } = useCreateBookmarkWithPostHook(); const { mutateAsync: createList } = useCreateBookmarkList(); @@ -51,6 +55,36 @@ export function useBookmarkImport() { file: File; source: ImportSource; }) => { + // Clear any previous quota error + setQuotaError(null); + + // First, parse the file to count bookmarks + const textContent = await file.text(); + const parsedBookmarks = parseImportFile(source, textContent); + const bookmarkCount = parsedBookmarks.length; + + // Check quota before proceeding + if (bookmarkCount > 0) { + const quotaUsage = + await apiUtils.client.subscriptions.getQuotaUsage.query(); + + if ( + !quotaUsage.bookmarks.unlimited && + quotaUsage.bookmarks.quota !== null + ) { + const remaining = + quotaUsage.bookmarks.quota - quotaUsage.bookmarks.used; + + if (remaining < bookmarkCount) { + const errorMsg = `Cannot import ${bookmarkCount} bookmarks. You have ${remaining} bookmark${remaining === 1 ? "" : "s"} remaining in your quota of ${quotaUsage.bookmarks.quota}.`; + setQuotaError(errorMsg); + throw new Error(errorMsg); + } + } + } + + // Proceed with import if quota check passes + // Use a custom parser to avoid re-parsing the file const result = await importBookmarksFromFile( { file, @@ -122,7 +156,12 @@ export function useBookmarkImport() { }, onProgress: (done, total) => setImportProgress({ done, total }), }, - {}, + { + // Use a custom parser to avoid re-parsing the file + parsers: { + [source]: () => parsedBookmarks, + }, + }, ); return result; }, @@ -158,6 +197,8 @@ export function useBookmarkImport() { return { importProgress, + quotaError, + clearQuotaError: () => setQuotaError(null), runUploadBookmarkFile: uploadBookmarkFileMutation.mutateAsync, isImporting: uploadBookmarkFileMutation.isPending, }; |
