aboutsummaryrefslogtreecommitdiffstats
path: root/packages/shared
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2025-11-29 14:53:31 +0000
committerGitHub <noreply@github.com>2025-11-29 14:53:31 +0000
commit86a4b3966504507afd6c3adbb6a1246cafd39d83 (patch)
tree66208555ef2720799d7196d777172b390eaf6d8f /packages/shared
parente67c33e46626258b748eb492d124f263fb427d0d (diff)
downloadkarakeep-86a4b3966504507afd6c3adbb6a1246cafd39d83.tar.zst
feat: Add automated bookmark backup feature (#2182)
* feat: Add automated bookmark backup system Implements a comprehensive automated backup feature for user bookmarks with the following capabilities: Database Schema: - Add backupSettings table to store user backup preferences (enabled, frequency, retention) - Add backups table to track backup records with status and metadata - Add BACKUP asset type for storing compressed backup files - Add migration 0066_add_backup_tables.sql Background Workers: - Implement BackupSchedulingWorker cron job (runs daily at midnight UTC) - Create BackupWorker to process individual backup jobs - Deterministic scheduling spreads backup jobs across 24 hours based on user ID hash - Support for daily and weekly backup frequencies - Automated retention cleanup to delete old backups based on user settings Export & Compression: - Reuse existing export functionality for bookmark data - Compress exports using Node.js built-in zlib (gzip level 9) - Store compressed backups as assets with proper metadata - Track backup size and bookmark count for statistics tRPC API: - backups.getSettings - Retrieve user backup configuration - backups.updateSettings - Update backup preferences - backups.list - List all user backups with metadata - backups.get - Get specific backup details - backups.delete - Delete a backup - backups.download - Download backup file (base64 encoded) - backups.triggerBackup - Manually trigger backup creation UI Components: - BackupSettings component with configuration form - Enable/disable automatic backups toggle - Frequency selection (daily/weekly) - Retention period configuration (1-365 days) - Backup list table with download and delete actions - Manual backup trigger button - Display backup stats (size, bookmark count, status) - Added backups page to settings navigation Technical Details: - Uses Restate queue system for distributed job processing - Implements idempotency keys to prevent duplicate backups - Background worker concurrency: 2 jobs at a time - 10-minute timeout for large backup exports - Proper error handling and logging throughout - Type-safe implementation with Zod schemas * refactor: simplify backup settings and asset handling - Move backup settings from separate table to user table columns - Update BackupSettings model to use static methods with users table - Remove download mutation in favor of direct asset links - Implement proper quota checks using QuotaService.checkStorageQuota - Update UI to use new property names and direct asset downloads - Update shared types to match new schema Key changes: - backupSettingsTable removed, settings now in users table - Backup downloads use direct /api/assets/{id} links - Quota properly validated before creating backup assets - Cleaner separation of concerns in tRPC models * migration * use zip instead of gzip * fix drizzle * fix settings * streaming json * remove more dead code * add e2e tests * return backup * poll for backups * more fixes * more fixes * fix test * fix UI * fix delete asset * fix ui * redirect for backup download * cleanups * fix idempotency * fix tests * add ratelimit * add error handling for background backups * i18n * model changes --------- Co-authored-by: Claude <noreply@anthropic.com>
Diffstat (limited to 'packages/shared')
-rw-r--r--packages/shared/assetdb.ts2
-rw-r--r--packages/shared/types/backups.ts14
-rw-r--r--packages/shared/types/users.ts12
3 files changed, 28 insertions, 0 deletions
diff --git a/packages/shared/assetdb.ts b/packages/shared/assetdb.ts
index ff87e847..2e22faf7 100644
--- a/packages/shared/assetdb.ts
+++ b/packages/shared/assetdb.ts
@@ -26,6 +26,7 @@ export const enum ASSET_TYPES {
IMAGE_PNG = "image/png",
IMAGE_WEBP = "image/webp",
APPLICATION_PDF = "application/pdf",
+ APPLICATION_ZIP = "application/zip",
TEXT_HTML = "text/html",
VIDEO_MP4 = "video/mp4",
@@ -65,6 +66,7 @@ export const SUPPORTED_ASSET_TYPES: Set<string> = new Set<string>([
...SUPPORTED_UPLOAD_ASSET_TYPES,
ASSET_TYPES.TEXT_HTML,
ASSET_TYPES.VIDEO_MP4,
+ ASSET_TYPES.APPLICATION_ZIP,
]);
export const zAssetMetadataSchema = z.object({
diff --git a/packages/shared/types/backups.ts b/packages/shared/types/backups.ts
new file mode 100644
index 00000000..f54d4824
--- /dev/null
+++ b/packages/shared/types/backups.ts
@@ -0,0 +1,14 @@
+import { z } from "zod";
+
+export const zBackupSchema = z.object({
+ id: z.string(),
+ userId: z.string(),
+ assetId: z.string().nullable(),
+ createdAt: z.date(),
+ size: z.number(),
+ bookmarkCount: z.number(),
+ status: z.enum(["pending", "success", "failure"]),
+ errorMessage: z.string().nullable().optional(),
+});
+
+export type ZBackup = z.infer<typeof zBackupSchema>;
diff --git a/packages/shared/types/users.ts b/packages/shared/types/users.ts
index 830fe87b..2fad4f83 100644
--- a/packages/shared/types/users.ts
+++ b/packages/shared/types/users.ts
@@ -108,6 +108,9 @@ export const zUserSettingsSchema = z.object({
]),
archiveDisplayBehaviour: z.enum(["show", "hide"]),
timezone: z.string(),
+ backupsEnabled: z.boolean(),
+ backupsFrequency: z.enum(["daily", "weekly"]),
+ backupsRetentionDays: z.number().int().min(1).max(365),
});
export type ZUserSettings = z.infer<typeof zUserSettingsSchema>;
@@ -116,4 +119,13 @@ export const zUpdateUserSettingsSchema = zUserSettingsSchema.partial().pick({
bookmarkClickAction: true,
archiveDisplayBehaviour: true,
timezone: true,
+ backupsEnabled: true,
+ backupsFrequency: true,
+ backupsRetentionDays: true,
+});
+
+export const zUpdateBackupSettingsSchema = zUpdateUserSettingsSchema.pick({
+ backupsEnabled: true,
+ backupsFrequency: true,
+ backupsRetentionDays: true,
});