diff --git a/.gitignore b/.gitignore
index 60cc568b..50edf5f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ pnpm-debug.log*
lerna-debug.log*
local.pastebar.*
safelist.txt
+pastebar-data-backup*
clipboard-images/**/*
clip-images/**/*
diff --git a/packages/pastebar-app-ui/src/components/ui/use-toast.ts b/packages/pastebar-app-ui/src/components/ui/use-toast.ts
index 607d1870..ce8fa843 100644
--- a/packages/pastebar-app-ui/src/components/ui/use-toast.ts
+++ b/packages/pastebar-app-ui/src/components/ui/use-toast.ts
@@ -11,6 +11,7 @@ export type ToasterToast = {
description?: React.ReactNode
open?: boolean
className?: string
+ variant?: 'default' | 'success' | 'warning' | 'danger' | 'destructive' | 'info' | null
onOpenChange?: (open: boolean) => void
duration?: number
onDismiss?: () => void
diff --git a/packages/pastebar-app-ui/src/layout/NavBar.tsx b/packages/pastebar-app-ui/src/layout/NavBar.tsx
index 9cef6d5b..cd2d2a77 100644
--- a/packages/pastebar-app-ui/src/layout/NavBar.tsx
+++ b/packages/pastebar-app-ui/src/layout/NavBar.tsx
@@ -591,6 +591,13 @@ export function NavBar() {
+ {
+ navigate('/app-settings/backup-restore', { replace: true })
+ }}
+ >
+ {t('Backup and Restore', { ns: 'backuprestore' })}
+ {
navigate('/app-settings/security', { replace: true })
@@ -653,9 +660,7 @@ export function NavBar() {
>
{t('Enable Image Capture', { ns: 'history' })}
diff --git a/packages/pastebar-app-ui/src/locales/lang/en/backuprestore.yaml b/packages/pastebar-app-ui/src/locales/lang/en/backuprestore.yaml
index 1d7a2b27..dbcdb0c8 100644
--- a/packages/pastebar-app-ui/src/locales/lang/en/backuprestore.yaml
+++ b/packages/pastebar-app-ui/src/locales/lang/en/backuprestore.yaml
@@ -1,27 +1,46 @@
-Backup and Restore: Backup and Restore
-Create Backup: Create Backup
-Include images in backup: Include images in backup
-Backup Now: Backup Now
-Restore Data: Restore Data
-Restore from File...: Restore from File...
-Select backup file: Select backup file
+Auto Backup on Restore: Auto Backup on Restore
Available Backups: Available Backups
-Total backup space: "{{size}}": Total backup space: {{size}}
+Backup Files: Backup Files
+Backup Now: Backup Now
+Backup and Restore: Backup and Restore
+Backup created successfully: Backup created successfully
+Backup deleted successfully: Backup deleted successfully
+Backup on Restore: Backup on Restore
+Browse: Browse
+Create Backup: Create Backup
+Create a backup of your data?: Create a backup of your data?
+Created: Created
+Creating backup...: Creating backup...
+Delete: Delete
+Delete this backup? This action cannot be undone.: Delete this backup? This action cannot be undone.
+Failed to create backup: Failed to create backup
+Failed to delete backup: Failed to delete backup
+Failed to load backup list: Failed to load backup list
+Failed to open backup folder: Failed to open backup folder
+Failed to restore backup: Failed to restore backup
+Failed to select backup file: Failed to select backup file
+Include images in backup: Include images in backup
+Invalid backup file: Invalid backup file
+Loading backups...: Loading backups...
+Move to another location?: Move to another location?
No backups found: No backups found
Restore: Restore
-Delete: Delete
-Create a backup of your data?: Create a backup of your data?
-Backup created successfully: Backup created successfully
-Move to another location?: Move to another location?
-This will replace all current data. Are you sure?: This will replace all current data. Are you sure?
-Restore from "{{filename}}"? This will replace all current data.: Restore from {{filename}}? This will replace all current data.
-Delete this backup? This action cannot be undone.: Delete this backup? This action cannot be undone.
+Restore Data: Restore Data
Restore completed. The application will restart.: Restore completed. The application will restart.
-Creating backup...: Creating backup...
+Restore from "{{filename}}"? This will replace all current data.: Restore from {{filename}}? This will replace all current data.
+Restore from File...: Restore from File...
+Restore from {{filename}}? This will replace all current data.: Restore from {{filename}}? This will replace all current data.
+Restored from {{filename}}: Restored from {{filename}}
+Restoring a backup will automatically restart the application.: Restoring a backup will automatically restart the application.
Restoring backup...: Restoring backup...
-Backup deleted successfully: Backup deleted successfully
-Failed to delete backup: Failed to delete backup
-Invalid backup file: Invalid backup file
+Restoring...: Restoring...
+Select backup file: Select backup file
+Size: Size
The selected file is not a valid PasteBar backup: The selected file is not a valid PasteBar backup
-Created: Created
-Size: Size
\ No newline at end of file
+This action cannot be undone. All current data will be replaced with the backup data.: This action cannot be undone. All current data will be replaced with the backup data.
+This will create a backup file containing your database: This will create a backup file containing your database
+This will replace all current data. Are you sure?: This will replace all current data. Are you sure?
+Total backup space {{size}}: 'Total backup space: {{size}}'
+and images: and images
+selected file: selected file
+will be permanently deleted.: will be permanently deleted.
diff --git a/packages/pastebar-app-ui/src/locales/lang/en/common.yaml b/packages/pastebar-app-ui/src/locales/lang/en/common.yaml
index b2559294..141c0558 100644
--- a/packages/pastebar-app-ui/src/locales/lang/en/common.yaml
+++ b/packages/pastebar-app-ui/src/locales/lang/en/common.yaml
@@ -101,6 +101,7 @@ Enter Passcode: Enter Passcode
Enter Password: Enter Password
Enter Recovery Password: Enter Recovery Password
Enter passcode or password to unlock: Enter passcode or password to unlock
+Error: Error
Errors:
Error loading link: Error loading link
Cant save to file: Cant save to file
@@ -237,6 +238,7 @@ Set: Set
Set Password: Set Password
Show Large View: Show Large View
Show all: Show all
+Size: Size
Source: Source
Split History Window: Split History Window
Star: Star
diff --git a/packages/pastebar-app-ui/src/locales/lang/esES/backuprestore.yaml b/packages/pastebar-app-ui/src/locales/lang/esES/backuprestore.yaml
index a9f82d88..fc893f66 100644
--- a/packages/pastebar-app-ui/src/locales/lang/esES/backuprestore.yaml
+++ b/packages/pastebar-app-ui/src/locales/lang/esES/backuprestore.yaml
@@ -1,4 +1,6 @@
Backup and Restore: Copia de Seguridad y Restauración
+Auto Backup on Restore: Copia Automática al Restaurar
+Browse: Explorar
Create Backup: Crear Copia de Seguridad
Include images in backup: Incluir imágenes en la copia de seguridad
Backup Now: Crear Copia Ahora
@@ -6,7 +8,7 @@ Restore Data: Restaurar Datos
Restore from File...: Restaurar desde Archivo...
Select backup file: Seleccionar archivo de copia de seguridad
Available Backups: Copias de Seguridad Disponibles
-Total backup space: "{{size}}": Espacio total de copias: {{size}}
+Total backup space {{size}}: 'Espacio total de copia s: {{size}}'
No backups found: No se encontraron copias de seguridad
Restore: Restaurar
Delete: Eliminar
@@ -24,4 +26,18 @@ Failed to delete backup: Error al eliminar la copia de seguridad
Invalid backup file: Archivo de copia de seguridad inválido
The selected file is not a valid PasteBar backup: El archivo seleccionado no es una copia de seguridad válida de PasteBar
Created: Creado
-Size: Tamaño
\ No newline at end of file
+Size: Tamaño
+Failed to load backup list: Error al cargar la lista de copias de seguridad
+Failed to create backup: Error al crear la copia de seguridad
+Restored from {{filename}}: Restaurado desde {{filename}}
+Failed to restore backup: Error al restaurar la copia de seguridad
+Backup Files: Archivos de Copia de Seguridad
+selected file: archivo seleccionado
+Failed to select backup file: Error al seleccionar el archivo de copia de seguridad
+Failed to open backup folder: Error al abrir la carpeta de copia de seguridad
+This will create a backup file containing your database: Esto creará un archivo de copia de seguridad que contiene tu base de datos
+and images: e imágenes
+Loading backups...: Cargando copias de seguridad...
+will be permanently deleted.: será eliminado permanentemente.
+This action cannot be undone. All current data will be replaced with the backup data.: Esta acción no se puede deshacer. Todos los datos actuales serán reemplazados con los datos de la copia de seguridad.
+Restoring a backup will automatically restart the application.: Restaurar una copia de seguridad reiniciará automáticamente la aplicación.
\ No newline at end of file
diff --git a/packages/pastebar-app-ui/src/locales/lang/fr/backuprestore.yaml b/packages/pastebar-app-ui/src/locales/lang/fr/backuprestore.yaml
index 44f38726..b10fc741 100644
--- a/packages/pastebar-app-ui/src/locales/lang/fr/backuprestore.yaml
+++ b/packages/pastebar-app-ui/src/locales/lang/fr/backuprestore.yaml
@@ -1,4 +1,6 @@
Backup and Restore: Sauvegarde et Restauration
+Auto Backup on Restore: Sauvegarde Automatique à la Restauration
+Browse: Parcourir
Create Backup: Créer une Sauvegarde
Include images in backup: Inclure les images dans la sauvegarde
Backup Now: Sauvegarder Maintenant
@@ -6,7 +8,7 @@ Restore Data: Restaurer les Données
Restore from File...: Restaurer depuis un Fichier...
Select backup file: Sélectionner le fichier de sauvegarde
Available Backups: Sauvegardes Disponibles
-Total backup space: "{{size}}": Espace total des sauvegardes: {{size}}
+Total backup space {{size}}: 'Espace total des sauvegardes: {{size}}'
No backups found: Aucune sauvegarde trouvée
Restore: Restaurer
Delete: Supprimer
@@ -24,4 +26,18 @@ Failed to delete backup: Échec de la suppression de la sauvegarde
Invalid backup file: Fichier de sauvegarde invalide
The selected file is not a valid PasteBar backup: Le fichier sélectionné n'est pas une sauvegarde PasteBar valide
Created: Créé
-Size: Taille
\ No newline at end of file
+Size: Taille
+Failed to load backup list: Échec du chargement de la liste des sauvegardes
+Failed to create backup: Échec de la création de la sauvegarde
+Restored from {{filename}}: Restauré depuis {{filename}}
+Failed to restore backup: Échec de la restauration de la sauvegarde
+Backup Files: Fichiers de Sauvegarde
+selected file: fichier sélectionné
+Failed to select backup file: Échec de la sélection du fichier de sauvegarde
+Failed to open backup folder: Échec de l'ouverture du dossier de sauvegarde
+This will create a backup file containing your database: Ceci créera un fichier de sauvegarde contenant votre base de données
+and images: et les images
+Loading backups...: Chargement des sauvegardes...
+will be permanently deleted.: sera définitivement supprimé.
+This action cannot be undone. All current data will be replaced with the backup data.: Cette action ne peut pas être annulée. Toutes les données actuelles seront remplacées par les données de sauvegarde.
+Restoring a backup will automatically restart the application.: La restauration d'une sauvegarde redémarrera automatiquement l'application.
\ No newline at end of file
diff --git a/packages/pastebar-app-ui/src/locales/lang/it/backuprestore.yaml b/packages/pastebar-app-ui/src/locales/lang/it/backuprestore.yaml
index 4d36a22b..de25259a 100644
--- a/packages/pastebar-app-ui/src/locales/lang/it/backuprestore.yaml
+++ b/packages/pastebar-app-ui/src/locales/lang/it/backuprestore.yaml
@@ -1,4 +1,6 @@
+Auto Backup on Restore: Backup Automatico al Ripristino
Backup and Restore: Backup e Ripristino
+Browse: Sfoglia
Create Backup: Crea Backup
Include images in backup: Includi immagini nel backup
Backup Now: Crea Backup Ora
@@ -6,7 +8,7 @@ Restore Data: Ripristina Dati
Restore from File...: Ripristina da File...
Select backup file: Seleziona file di backup
Available Backups: Backup Disponibili
-Total backup space: "{{size}}": Spazio totale backup: {{size}}
+Total backup space {{size}}: 'Spazio totale di backup: {{size}}'
No backups found: Nessun backup trovato
Restore: Ripristina
Delete: Elimina
@@ -24,4 +26,18 @@ Failed to delete backup: Impossibile eliminare il backup
Invalid backup file: File di backup non valido
The selected file is not a valid PasteBar backup: Il file selezionato non è un backup PasteBar valido
Created: Creato
-Size: Dimensione
\ No newline at end of file
+Size: Dimensione
+Failed to load backup list: Impossibile caricare l'elenco dei backup
+Failed to create backup: Impossibile creare il backup
+Restored from {{filename}}: Ripristinato da {{filename}}
+Failed to restore backup: Impossibile ripristinare il backup
+Backup Files: File di Backup
+selected file: file selezionato
+Failed to select backup file: Impossibile selezionare il file di backup
+Failed to open backup folder: Impossibile aprire la cartella di backup
+This will create a backup file containing your database: Questo creerà un file di backup contenente il tuo database
+and images: e le immagini
+Loading backups...: Caricamento backup...
+will be permanently deleted.: sarà eliminato permanentemente.
+This action cannot be undone. All current data will be replaced with the backup data.: Questa azione non può essere annullata. Tutti i dati attuali saranno sostituiti con i dati del backup.
+Restoring a backup will automatically restart the application.: Il ripristino di un backup riavvierà automaticamente l'applicazione.
\ No newline at end of file
diff --git a/packages/pastebar-app-ui/src/locales/lang/ru/backuprestore.yaml b/packages/pastebar-app-ui/src/locales/lang/ru/backuprestore.yaml
index 7fcc4e84..e90b6bd6 100644
--- a/packages/pastebar-app-ui/src/locales/lang/ru/backuprestore.yaml
+++ b/packages/pastebar-app-ui/src/locales/lang/ru/backuprestore.yaml
@@ -1,4 +1,6 @@
+Auto Backup on Restore: Автобэкап при восстановлении
Backup and Restore: Резервное копирование и восстановление
+Browse: Обзор
Create Backup: Создать резервную копию
Include images in backup: Включить изображения в резервную копию
Backup Now: Создать резервную копию сейчас
@@ -6,7 +8,7 @@ Restore Data: Восстановить данные
Restore from File...: Восстановить из файла...
Select backup file: Выбрать файл резервной копии
Available Backups: Доступные резервные копии
-Total backup space: "{{size}}": Общий размер резервных копий: {{size}}
+Total backup space {{size}}: 'Общий размер резервных копий: {{size}}'
No backups found: Резервные копии не найдены
Restore: Восстановить
Delete: Удалить
@@ -24,4 +26,18 @@ Failed to delete backup: Не удалось удалить резервную
Invalid backup file: Недействительный файл резервной копии
The selected file is not a valid PasteBar backup: Выбранный файл не является действительной резервной копией PasteBar
Created: Создано
-Size: Размер
\ No newline at end of file
+Size: Размер
+Failed to load backup list: Не удалось загрузить список резервных копий
+Failed to create backup: Не удалось создать резервную копию
+Restored from {{filename}}: Восстановлено из {{filename}}
+Failed to restore backup: Не удалось восстановить резервную копию
+Backup Files: Файлы резервных копий
+selected file: выбранный файл
+Failed to select backup file: Не удалось выбрать файл резервной копии
+Failed to open backup folder: Не удалось открыть папку резервных копий
+This will create a backup file containing your database: Это создаст файл резервной копии, содержащий вашу базу данных
+and images: и изображения
+Loading backups...: Загрузка резервных копий...
+will be permanently deleted.: будет удалена навсегда.
+This action cannot be undone. All current data will be replaced with the backup data.: Это действие нельзя отменить. Все текущие данные будут заменены данными из резервной копии.
+Restoring a backup will automatically restart the application.: Восстановление резервной копии автоматически перезапустит приложение.
\ No newline at end of file
diff --git a/packages/pastebar-app-ui/src/locales/lang/ru/common.yaml b/packages/pastebar-app-ui/src/locales/lang/ru/common.yaml
index 8af236ed..50f3371e 100644
--- a/packages/pastebar-app-ui/src/locales/lang/ru/common.yaml
+++ b/packages/pastebar-app-ui/src/locales/lang/ru/common.yaml
@@ -96,6 +96,7 @@ Enter Passcode: Введите код доступа
Enter Password: Введите пароль
Enter Recovery Password: Введите пароль восстановления
Enter passcode or password to unlock: Введите код доступа или пароль для разблокировки
+Error: Error
Errors:
Error loading link: Ошибка загрузки ссылки
Cant save file: Невозможно сохранить файл
diff --git a/packages/pastebar-app-ui/src/locales/lang/tr/backuprestore.yaml b/packages/pastebar-app-ui/src/locales/lang/tr/backuprestore.yaml
index e8e6de0b..72ba0618 100644
--- a/packages/pastebar-app-ui/src/locales/lang/tr/backuprestore.yaml
+++ b/packages/pastebar-app-ui/src/locales/lang/tr/backuprestore.yaml
@@ -1,4 +1,6 @@
+Auto Backup on Restore: Geri Yüklemede Otomatik Yedek
Backup and Restore: Yedekleme ve Geri Yükleme
+Browse: Gözat
Create Backup: Yedek Oluştur
Include images in backup: Yedekte görselleri dahil et
Backup Now: Şimdi Yedekle
@@ -6,7 +8,7 @@ Restore Data: Verileri Geri Yükle
Restore from File...: Dosyadan Geri Yükle...
Select backup file: Yedek dosyası seç
Available Backups: Mevcut Yedekler
-Total backup space: "{{size}}": Toplam yedek alanı: {{size}}
+Total backup space {{size}}: 'Toplam yedek alanı: {{size}}'
No backups found: Yedek bulunamadı
Restore: Geri Yükle
Delete: Sil
@@ -14,7 +16,7 @@ Create a backup of your data?: Verilerinizin bir yedeğini oluşturmak istiyor m
Backup created successfully: Yedek başarıyla oluşturuldu
Move to another location?: Başka bir konuma taşı?
This will replace all current data. Are you sure?: Bu, mevcut tüm verileri değiştirecek. Emin misiniz?
-Restore from "{{filename}}"? This will replace all current data.: {{filename}} dosyasından geri yüklensin mi? Bu, mevcut tüm verileri değiştirecek.
+Restore from {{filename}}? This will replace all current data.: '{{filename}} dosyasından geri yüklensin mi? Bu, mevcut tüm verileri değiştirecek.'
Delete this backup? This action cannot be undone.: Bu yedeği sil? Bu eylem geri alınamaz.
Restore completed. The application will restart.: Geri yükleme tamamlandı. Uygulama yeniden başlatılacak.
Creating backup...: Yedek oluşturuluyor...
@@ -24,4 +26,18 @@ Failed to delete backup: Yedek silinemedi
Invalid backup file: Geçersiz yedek dosyası
The selected file is not a valid PasteBar backup: Seçilen dosya geçerli bir PasteBar yedeği değil
Created: Oluşturulma
-Size: Boyut
\ No newline at end of file
+Size: Boyut
+Failed to load backup list: Yedek listesi yüklenemedi
+Failed to create backup: Yedek oluşturulamadı
+'Restored from {{filename}}': '{{filename}} dosyasından geri yüklendi'
+Failed to restore backup: Yedek geri yüklenemedi
+Backup Files: Yedek Dosyaları
+selected file: seçilen dosya
+Failed to select backup file: Yedek dosyası seçilemedi
+Failed to open backup folder: Yedek klasörü açılamadı
+This will create a backup file containing your database: Bu, veritabanınızı içeren bir yedek dosyası oluşturacak
+and images: ve görseller
+Loading backups...: Yedekler yükleniyor...
+will be permanently deleted.: kalıcı olarak silinecek.
+This action cannot be undone. All current data will be replaced with the backup data.: Bu eylem geri alınamaz. Mevcut tüm veriler yedek verileriyle değiştirilecek.
+Restoring a backup will automatically restart the application.: Bir yedeği geri yüklemek uygulamayı otomatik olarak yeniden başlatacak.
\ No newline at end of file
diff --git a/packages/pastebar-app-ui/src/locales/lang/uk/backuprestore.yaml b/packages/pastebar-app-ui/src/locales/lang/uk/backuprestore.yaml
index 7764bab8..c7a74276 100644
--- a/packages/pastebar-app-ui/src/locales/lang/uk/backuprestore.yaml
+++ b/packages/pastebar-app-ui/src/locales/lang/uk/backuprestore.yaml
@@ -1,4 +1,6 @@
+Auto Backup on Restore: Авто Бекап при Відновленні
Backup and Restore: Резервне копіювання та відновлення
+Browse: Переглянути
Create Backup: Створити резервну копію
Include images in backup: Включити зображення в резервну копію
Backup Now: Створити резервну копію зараз
@@ -6,7 +8,7 @@ Restore Data: Відновити дані
Restore from File...: Відновити з файлу...
Select backup file: Вибрати файл резервної копії
Available Backups: Доступні резервні копії
-Total backup space: "{{size}}": Загальний розмір резервних копій: {{size}}
+Total backup space {{size}}: 'Загальний розмір резервних копій: {{size}}'
No backups found: Резервні копії не знайдені
Restore: Відновити
Delete: Видалити
@@ -24,4 +26,18 @@ Failed to delete backup: Не вдалося видалити резервну
Invalid backup file: Недійсний файл резервної копії
The selected file is not a valid PasteBar backup: Вибраний файл не є дійсною резервною копією PasteBar
Created: Створено
-Size: Розмір
\ No newline at end of file
+Size: Розмір
+Failed to load backup list: Не вдалося завантажити список резервних копій
+Failed to create backup: Не вдалося створити резервну копію
+Restored from {{filename}}: Відновлено з {{filename}}
+Failed to restore backup: Не вдалося відновити резервну копію
+Backup Files: Файли резервних копій
+selected file: вибраний файл
+Failed to select backup file: Не вдалося вибрати файл резервної копії
+Failed to open backup folder: Не вдалося відкрити папку резервних копій
+This will create a backup file containing your database: Це створить файл резервної копії, що містить вашу базу даних
+and images: та зображення
+Loading backups...: Завантаження резервних копій...
+will be permanently deleted.: буде видалено назавжди.
+This action cannot be undone. All current data will be replaced with the backup data.: Цю дію неможливо скасувати. Всі поточні дані будуть замінені даними з резервної копії.
+Restoring a backup will automatically restart the application.: Відновлення резервної копії автоматично перезапустить додаток.
\ No newline at end of file
diff --git a/packages/pastebar-app-ui/src/locales/lang/zhCN/backuprestore.yaml b/packages/pastebar-app-ui/src/locales/lang/zhCN/backuprestore.yaml
index e723b9d5..eda48995 100644
--- a/packages/pastebar-app-ui/src/locales/lang/zhCN/backuprestore.yaml
+++ b/packages/pastebar-app-ui/src/locales/lang/zhCN/backuprestore.yaml
@@ -1,4 +1,6 @@
+Auto Backup on Restore: 恢复时自动备份
Backup and Restore: 备份和恢复
+Browse: 浏览
Create Backup: 创建备份
Include images in backup: 在备份中包含图片
Backup Now: 立即备份
@@ -6,7 +8,7 @@ Restore Data: 恢复数据
Restore from File...: 从文件恢复...
Select backup file: 选择备份文件
Available Backups: 可用备份
-Total backup space: "{{size}}": 备份总空间: {{size}}
+Total backup space {{size}}: '备份总空间: {{size}}'
No backups found: 未找到备份
Restore: 恢复
Delete: 删除
@@ -24,4 +26,18 @@ Failed to delete backup: 删除备份失败
Invalid backup file: 无效的备份文件
The selected file is not a valid PasteBar backup: 选择的文件不是有效的PasteBar备份
Created: 创建时间
-Size: 大小
\ No newline at end of file
+Size: 大小
+Failed to load backup list: 加载备份列表失败
+Failed to create backup: 创建备份失败
+Restored from {{filename}}: 从{{filename}}恢复
+Failed to restore backup: 恢复备份失败
+Backup Files: 备份文件
+selected file: 选择的文件
+Failed to select backup file: 选择备份文件失败
+Failed to open backup folder: 打开备份文件夹失败
+This will create a backup file containing your database: 这将创建一个包含您数据库的备份文件
+and images: 和图片
+Loading backups...: 正在加载备份...
+will be permanently deleted.: 将被永久删除。
+This action cannot be undone. All current data will be replaced with the backup data.: 此操作无法撤销。所有当前数据将被备份数据替换。
+Restoring a backup will automatically restart the application.: 恢复备份将自动重启应用程序。
\ No newline at end of file
diff --git a/packages/pastebar-app-ui/src/pages/settings/BackupRestoreSettings.tsx b/packages/pastebar-app-ui/src/pages/settings/BackupRestoreSettings.tsx
index fa19ab67..59ab70b5 100644
--- a/packages/pastebar-app-ui/src/pages/settings/BackupRestoreSettings.tsx
+++ b/packages/pastebar-app-ui/src/pages/settings/BackupRestoreSettings.tsx
@@ -1,23 +1,23 @@
import { useEffect, useState } from 'react'
import { invoke } from '@tauri-apps/api'
-import { useTranslation } from 'react-i18next'
-import {
- Archive,
- Download,
- FolderOpen,
- HardDrive,
- Loader2,
- Package,
- RotateCcw,
- Trash2,
- Upload
+import { open } from '@tauri-apps/api/dialog'
+import { settingsStoreAtom, uiStoreAtom } from '~/store'
+import { useAtomValue } from 'jotai'
+import {
+ Download,
+ ExternalLink,
+ FolderOpen,
+ HardDrive,
+ Loader2,
+ Package,
+ RotateCcw,
+ Trash2,
+ Upload,
} from 'lucide-react'
-import { useToast } from '~/components/ui/use-toast'
+import { useTranslation } from 'react-i18next'
+import { Link } from 'react-router-dom'
import AutoSize from 'react-virtualized-auto-sizer'
-import Spacer from '~/components/atoms/spacer'
-import { Icons } from '~/components/icons'
-import SimpleBar from '~/components/libs/simplebar-react'
import {
AlertDialog,
AlertDialogAction,
@@ -29,6 +29,12 @@ import {
AlertDialogTitle,
AlertDialogTrigger,
} from '~/components/ui/alert-dialog'
+import { useToast } from '~/components/ui/use-toast'
+import Spacer from '~/components/atoms/spacer'
+import { TimeAgo } from '~/components/atoms/time-ago/TimeAgo'
+import ToolTip from '~/components/atoms/tooltip'
+import { Icons } from '~/components/icons'
+import SimpleBar from '~/components/libs/simplebar-react'
import {
Badge,
Box,
@@ -40,7 +46,6 @@ import {
Checkbox,
Flex,
Text,
- TextNormal,
} from '~/components/ui'
interface BackupInfo {
@@ -60,14 +65,47 @@ interface BackupListResponse {
export default function BackupRestoreSettings() {
const { t } = useTranslation()
const { toast } = useToast()
+ const { returnRoute } = useAtomValue(uiStoreAtom)
+
+ const { relaunchApp } = useAtomValue(settingsStoreAtom)
const [includeImages, setIncludeImages] = useState(true)
+ const [backupOnRestore, setBackupOnRestore] = useState(() => {
+ const saved = localStorage.getItem('backupOnRestore')
+ return saved !== null ? JSON.parse(saved) : true
+ })
+
+ // Save backup on restore preference to localStorage
+ useEffect(() => {
+ localStorage.setItem('backupOnRestore', JSON.stringify(backupOnRestore))
+ }, [backupOnRestore])
const [isCreatingBackup, setIsCreatingBackup] = useState(false)
const [isRestoring, setIsRestoring] = useState(false)
+ const [restoringFromFile, setRestoringFromFile] = useState(false)
+ const [restoringBackupPath, setRestoringBackupPath] = useState(null)
const [backups, setBackups] = useState([])
const [totalSize, setTotalSize] = useState('')
const [isLoadingBackups, setIsLoadingBackups] = useState(false)
+ // Parse date from backup filename
+ const parseBackupDate = (filename: string): Date | null => {
+ // Extract date from filename format: pastebar-data-backup-YYYY-MM-DD-HH-MM.zip
+ const match = filename.match(
+ /pastebar-data-backup-(\d{4})-(\d{2})-(\d{2})-(\d{2})-(\d{2})\.zip/
+ )
+ if (match) {
+ const [, year, month, day, hour, minute] = match
+ return new Date(
+ parseInt(year),
+ parseInt(month) - 1,
+ parseInt(day),
+ parseInt(hour),
+ parseInt(minute)
+ )
+ }
+ return null
+ }
+
const loadBackups = async () => {
setIsLoadingBackups(true)
try {
@@ -77,8 +115,14 @@ export default function BackupRestoreSettings() {
} catch (error) {
console.error('Failed to load backups:', error)
toast({
+ id: 'backup-list-error',
title: t('Error', { ns: 'common' }),
- description: 'Failed to load backup list',
+ duration: 3000,
+ description: (
+
+ {t('Failed to load backup list', { ns: 'backuprestore' })}
+
+ ),
variant: 'destructive',
})
} finally {
@@ -96,19 +140,28 @@ export default function BackupRestoreSettings() {
const backupPath = await invoke('create_backup', {
includeImages,
})
-
+
toast({
+ id: 'backup-create-success',
title: t('Backup created successfully', { ns: 'backuprestore' }),
- description: backupPath,
+ duration: 3000,
+ description: {backupPath},
+ variant: 'success',
})
-
+
// Reload backup list
await loadBackups()
} catch (error) {
console.error('Failed to create backup:', error)
toast({
+ id: 'backup-create-error',
title: t('Error', { ns: 'common' }),
- description: `Failed to create backup: ${error}`,
+ duration: 3000,
+ description: (
+
+ {t('Failed to create backup', { ns: 'backuprestore' })}: {String(error)}
+
+ ),
variant: 'destructive',
})
} finally {
@@ -118,284 +171,518 @@ export default function BackupRestoreSettings() {
const handleRestoreBackup = async (backupPath: string, filename: string) => {
setIsRestoring(true)
+ setRestoringBackupPath(backupPath)
try {
- await invoke('restore_backup', { backupPath })
-
- toast({
- title: t('Restore completed. The application will restart.', { ns: 'backuprestore' }),
- description: `Restored from ${filename}`,
+ await invoke('restore_backup', {
+ backupPath,
+ createPreRestoreBackup: backupOnRestore,
})
-
+
+ toast({
+ id: 'backup-restore-success',
+ title: t('Restore completed. The application will restart.', {
+ ns: 'backuprestore',
+ }),
+ duration: 3000,
+ description: (
+
+ {t('Restored from {{filename}}', { ns: 'backuprestore', filename })}
+
+ ),
+ variant: 'success',
+ })
+
+ setTimeout(() => {
+ relaunchApp()
+ setIsRestoring(false)
+ }, 3000)
+
// Application should restart after restore
} catch (error) {
console.error('Failed to restore backup:', error)
toast({
title: t('Error', { ns: 'common' }),
- description: `Failed to restore backup: ${error}`,
+ id: 'backup-restore-error',
+ duration: 3000,
+ description: (
+
+ {t('Failed to restore backup', { ns: 'backuprestore' })}: {String(error)}
+
+ ),
variant: 'destructive',
})
} finally {
setIsRestoring(false)
+ setRestoringBackupPath(null)
}
}
const handleRestoreFromFile = async () => {
try {
- const selectedFile = await invoke('select_backup_file')
-
- if (selectedFile) {
- const filename = selectedFile.split(/[/\\]/).pop() || 'selected file'
- await handleRestoreBackup(selectedFile, filename)
+ setRestoringFromFile(true)
+ // Get current data directory path for defaultPath
+ const dataPaths = await invoke<{ data_dir: string }>('get_data_paths')
+
+ const selected = await open({
+ multiple: false,
+ defaultPath: dataPaths.data_dir,
+ filters: [
+ {
+ name: t('Backup Files', { ns: 'backuprestore' }),
+ extensions: ['zip'],
+ },
+ ],
+ })
+
+ if (selected && typeof selected === 'string') {
+ // Validate it's a valid backup file
+ const filename =
+ selected.split(/[/\\]/).pop() || t('selected file', { ns: 'backuprestore' })
+ if (!filename.includes('pastebar-data-backup-') || !filename.endsWith('.zip')) {
+ toast({
+ id: 'backup-invalid-file',
+ title: t('Invalid backup file', { ns: 'backuprestore' }),
+ duration: 3000,
+ description: (
+
+ {t('The selected file is not a valid PasteBar backup', {
+ ns: 'backuprestore',
+ })}
+
+ ),
+ variant: 'destructive',
+ })
+ setRestoringFromFile(false)
+ return
+ }
+
+ await handleRestoreBackup(selected, filename)
}
+ // If selected is null, user cancelled the dialog - no action needed
+ setRestoringFromFile(false)
} catch (error) {
console.error('Failed to select backup file:', error)
toast({
title: t('Error', { ns: 'common' }),
- description: `Failed to select backup file: ${error}`,
+ id: 'backup-select-error',
+ duration: 3000,
+ description: (
+
+ {t('Failed to select backup file', { ns: 'backuprestore' })}: {String(error)}
+
+ ),
variant: 'destructive',
})
+ setRestoringFromFile(false)
}
}
const handleDeleteBackup = async (backupPath: string, filename: string) => {
try {
await invoke('delete_backup', { backupPath })
-
+
toast({
+ id: 'backup-delete-success',
title: t('Backup deleted successfully', { ns: 'backuprestore' }),
- description: filename,
+ duration: 3000,
+ description: {filename},
+ variant: 'success',
})
-
+
// Reload backup list
await loadBackups()
} catch (error) {
console.error('Failed to delete backup:', error)
toast({
+ id: 'backup-delete-error',
title: t('Failed to delete backup', { ns: 'backuprestore' }),
- description: `${error}`,
+ duration: 3000,
+ description: {`${error}`},
+ variant: 'destructive',
+ })
+ }
+ }
+
+ const handleBrowseBackupFolder = async (backupPath: string) => {
+ try {
+ // Extract directory from the backup file path
+ const backupDir = backupPath.substring(
+ 0,
+ backupPath.lastIndexOf('/') || backupPath.lastIndexOf('\\')
+ )
+ await invoke('open_path_or_app', { path: backupDir })
+ } catch (error) {
+ console.error('Failed to open backup folder:', error)
+ toast({
+ id: 'backup-open-error',
+ title: t('Error', { ns: 'common' }),
+ duration: 3000,
+ description: (
+
+ {t('Failed to open backup folder', { ns: 'backuprestore' })}: {String(error)}
+
+ ),
variant: 'destructive',
})
}
}
return (
-
-
- {({ height }) => (
-
-
- {/* Header */}
-
-
-
+
+ {({ height }) => {
+ return (
+ height && (
+
+
+
{t('Backup and Restore', { ns: 'backuprestore' })}
+
+
+
+
+
+
+ {/* Create Backup Section */}
+
+
+
+
+
+ {t('Create Backup', { ns: 'backuprestore' })}
+
- {/* Create Backup Section */}
-
-
-
-
- {t('Create Backup', { ns: 'backuprestore' })}
-
-
-
-
- setIncludeImages(checked as boolean)}
- />
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ {t('Create a backup of your data?', {
+ ns: 'backuprestore',
+ })}
+
+
+ {t(
+ 'This will create a backup file containing your database',
+ { ns: 'backuprestore' }
+ )}
+ {includeImages
+ ? t('and images', { ns: 'backuprestore' })
+ : ''}
+ .
+
+
+
+
+ {t('Cancel', { ns: 'common' })}
+
+
+ {t('Create Backup', { ns: 'backuprestore' })}
+
+
+
+
+
+
+
+
+
+ {/* Restore Section */}
+
+
+
+
+
+ {t('Restore Data', { ns: 'backuprestore' })}
+
+
+
+ setBackupOnRestore(checked as boolean)}
+ >
+ {t('Auto Backup on Restore', { ns: 'backuprestore' })}
+
+
+
+
+
+
-
-
-
-
- {t('Create a backup of your data?', { ns: 'backuprestore' })}
-
-
- This will create a backup file containing your database
- {includeImages ? ' and images' : ''}.
-
-
-
- {t('Cancel', { ns: 'common' })}
-
- {t('Create Backup', { ns: 'backuprestore' })}
-
-
-
-
-
-
- {/* Restore Section */}
-
-
-
-
- {t('Restore Data', { ns: 'backuprestore' })}
-
-
-
-
+
-
+ {/* Available Backups */}
+
+
+
+ {t('Available Backups', { ns: 'backuprestore' })}
+
+ {totalSize && (
+
+ {t('Total backup space {{size}}', {
+ ns: 'backuprestore',
+ size: totalSize,
+ })}
+
+ )}
+
- {/* Available Backups */}
-
-
-
- {t('Available Backups', { ns: 'backuprestore' })}
+ {isLoadingBackups ? (
+
+
+
+ {t('Loading backups...', { ns: 'backuprestore' })}
+
+
+ ) : backups.length === 0 ? (
+
+
+ {t('No backups found', { ns: 'backuprestore' })}
+
+ ) : (
+
+ {backups.map(backup => {
+ const backupDate = parseBackupDate(backup.filename)
+ return (
+
+
+ {/* File name - bold on top */}
+
+ {backup.filename}
+
+
+ {/* Date and size line */}
+
+
+ {backupDate ? (
+
+ ) : (
+ {backup.created_date}
+ )}
+
+ {backup.size_formatted}
+
+
+ {/* Buttons row */}
+
+ {/* Delete button on the left */}
+
+
+
+
+
+
+
+
+
+ {t(
+ 'Delete this backup? This action cannot be undone.',
+ { ns: 'backuprestore' }
+ )}
+
+
+ {backup.filename} ({backup.size_formatted})
+ {t('will be permanently deleted.', {
+ ns: 'backuprestore',
+ })}
+
+
+
+
+ {t('Cancel', { ns: 'common' })}
+
+
+ handleDeleteBackup(
+ backup.full_path,
+ backup.filename
+ )
+ }
+ className="bg-red-600 hover:bg-red-700"
+ >
+ {t('Delete', { ns: 'backuprestore' })}
+
+
+
+
+
+ {/* Browse and Restore buttons on the right */}
+
+
+
+
+
+
+
+
+
+
+ {t(
+ 'Restore from {{filename}}? This will replace all current data.',
+ {
+ ns: 'backuprestore',
+ filename: backup.filename,
+ }
+ )}
+
+
+ {t(
+ 'This action cannot be undone. All current data will be replaced with the backup data.',
+ { ns: 'backuprestore' }
+ )}
+
+
+
+
+ {t('Cancel', { ns: 'common' })}
+
+
+ handleRestoreBackup(
+ backup.full_path,
+ backup.filename
+ )
+ }
+ className="bg-red-600 hover:bg-red-700"
+ >
+ {t('Restore', { ns: 'backuprestore' })}
+
+
+
+
+
+
+
+
+ )
+ })}
+
+ )}
+
+
+ {/* Restore note */}
+
+ {t(
+ 'Restoring a backup will automatically restart the application.',
+ { ns: 'backuprestore' }
+ )}
- {totalSize && (
-
- {t('Total backup space: {{size}}', {
- ns: 'settings',
- size: totalSize
- })}
-
- )}
-
+
+
- {isLoadingBackups ? (
-
-
- Loading backups...
-
- ) : backups.length === 0 ? (
-
-
- {t('No backups found', { ns: 'backuprestore' })}
-
- ) : (
-
- {backups.map((backup) => (
-
-
-
-
-
- {backup.filename}
-
-
- {t('Created', { ns: 'common' })}: {backup.created_date}
-
-
- {t('Size', { ns: 'common' })}: {backup.size_formatted}
-
-
-
-
-
-
-
-
-
-
-
- {t('Restore from {{filename}}? This will replace all current data.', {
- ns: 'settings',
- filename: backup.filename
- })}
-
-
- This action cannot be undone. All current data will be replaced with the backup data.
-
-
-
- {t('Cancel', { ns: 'common' })}
- handleRestoreBackup(backup.full_path, backup.filename)}
- className="bg-red-600 hover:bg-red-700"
- >
- {t('Restore', { ns: 'backuprestore' })}
-
-
-
-
-
-
-
-
-
-
-
-
- {t('Delete this backup? This action cannot be undone.', { ns: 'backuprestore' })}
-
-
- {backup.filename} ({backup.size_formatted}) will be permanently deleted.
-
-
-
- {t('Cancel', { ns: 'common' })}
- handleDeleteBackup(backup.full_path, backup.filename)}
- className="bg-red-600 hover:bg-red-700"
- >
- {t('Delete', { ns: 'backuprestore' })}
-
-
-
-
-
-
-
- ))}
-
- )}
-
-
-
+
+
+
+
+
+
+
-
- )}
-
-
+ )
+ )
+ }}
+
)
-}
\ No newline at end of file
+}
diff --git a/packages/pastebar-app-ui/src/pages/settings/UserPreferences.tsx b/packages/pastebar-app-ui/src/pages/settings/UserPreferences.tsx
index e483a0f9..3bc2e550 100644
--- a/packages/pastebar-app-ui/src/pages/settings/UserPreferences.tsx
+++ b/packages/pastebar-app-ui/src/pages/settings/UserPreferences.tsx
@@ -10,7 +10,6 @@ import {
themeStoreAtom,
uiStoreAtom,
} from '~/store'
-import CustomDatabaseLocationSettings from './CustomDatabaseLocationSettings'
import { useAtomValue } from 'jotai'
import { ChevronDown, ChevronUp, MessageSquare, MessageSquareDashed } from 'lucide-react'
import { useTheme } from 'next-themes'
@@ -40,6 +39,8 @@ import {
import md from '~/store/example.md?raw'
+import CustomDatabaseLocationSettings from './CustomDatabaseLocationSettings'
+
export default function UserPreferences() {
const { t } = useTranslation()
diff --git a/packages/pastebar-app-ui/src/styles/globals.css b/packages/pastebar-app-ui/src/styles/globals.css
index ee39f12c..0571918c 100644
--- a/packages/pastebar-app-ui/src/styles/globals.css
+++ b/packages/pastebar-app-ui/src/styles/globals.css
@@ -521,7 +521,7 @@ div.note-content.release-notes {
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
- --destructive: 0 100% 50%;
+ --destructive: 0 50% 50%;
--destructive-foreground: 210 40% 98%;
--ring: 215 20.2% 65.1%;
@@ -904,3 +904,7 @@ pre.code-editor-pre .token-line {
overflow: hidden;
border-radius: 13px;
}
+
+.word-break {
+ word-break: break-word;
+}
diff --git a/src-tauri/src/commands/backup_restore_commands.rs b/src-tauri/src/commands/backup_restore_commands.rs
index 84092781..43429472 100644
--- a/src-tauri/src/commands/backup_restore_commands.rs
+++ b/src-tauri/src/commands/backup_restore_commands.rs
@@ -3,12 +3,11 @@ use std::path::{Path, PathBuf};
use std::io::Write;
use chrono::{DateTime, Local};
use serde::{Deserialize, Serialize};
-use tauri::api::dialog::FileDialogBuilder;
use zip::{ZipWriter, ZipArchive};
use zip::write::FileOptions;
use std::io::{Read, Seek};
-use crate::db::APP_CONSTANTS;
+use crate::db::{get_data_dir, get_db_path, get_clip_images_dir, get_clipboard_images_dir};
use crate::services::utils::debug_output;
#[derive(Debug, Serialize, Deserialize)]
@@ -27,10 +26,6 @@ pub struct BackupListResponse {
pub total_size_formatted: String,
}
-fn get_data_dir() -> PathBuf {
- // Get current data directory - could be custom or default
- APP_CONSTANTS.get().unwrap().app_data_dir.clone()
-}
fn get_backup_filename() -> String {
let now = Local::now();
@@ -106,16 +101,22 @@ pub async fn create_backup(include_images: bool) -> Result {
let backup_filename = get_backup_filename();
let backup_path = data_dir.join(&backup_filename);
- // Database file path
- let db_filename = if cfg!(debug_assertions) {
- "local.pastebar-db.data"
- } else {
- "pastebar-db.data"
- };
- let db_path = data_dir.join(db_filename);
+ debug_output(|| {
+ println!("Data directory: {}", data_dir.display());
+ println!("Backup will be created at: {}", backup_path.display());
+ });
+
+ // Database file path - use the actual database path which handles debug/release naming
+ let db_path_str = get_db_path();
+ let db_path = PathBuf::from(&db_path_str);
+
+ debug_output(|| {
+ println!("Looking for database file at: {}", db_path_str);
+ println!("Database file exists: {}", db_path.exists());
+ });
if !db_path.exists() {
- return Err("Database file not found".to_string());
+ return Err(format!("Database file not found at: {}", db_path_str));
}
// Create zip file
@@ -134,6 +135,11 @@ pub async fn create_backup(include_images: bool) -> Result {
db_file.read_to_end(&mut db_buffer)
.map_err(|e| format!("Failed to read database file: {}", e))?;
+ // Get just the filename for the zip entry
+ let db_filename = db_path.file_name()
+ .and_then(|name| name.to_str())
+ .unwrap_or("pastebar-db.data");
+
zip.start_file(db_filename, options)
.map_err(|e| format!("Failed to start database file in zip: {}", e))?;
zip.write_all(&db_buffer)
@@ -141,8 +147,15 @@ pub async fn create_backup(include_images: bool) -> Result {
// Add image directories if requested
if include_images {
- let clip_images_dir = data_dir.join("clip-images");
- let history_images_dir = data_dir.join("history-images");
+ let clip_images_dir = get_clip_images_dir();
+ let history_images_dir = get_clipboard_images_dir();
+
+ debug_output(|| {
+ println!("Clip images directory: {}", clip_images_dir.display());
+ println!("Clip images exists: {}", clip_images_dir.exists());
+ println!("History images directory: {}", history_images_dir.display());
+ println!("History images exists: {}", history_images_dir.exists());
+ });
if clip_images_dir.exists() {
add_directory_to_zip(&mut zip, &clip_images_dir, &data_dir)
@@ -151,7 +164,7 @@ pub async fn create_backup(include_images: bool) -> Result {
if history_images_dir.exists() {
add_directory_to_zip(&mut zip, &history_images_dir, &data_dir)
- .map_err(|e| format!("Failed to add history-images directory: {}", e))?;
+ .map_err(|e| format!("Failed to add clipboard-images directory: {}", e))?;
}
}
@@ -224,87 +237,38 @@ pub async fn list_backups() -> Result {
})
}
-#[tauri::command]
-pub fn select_backup_file() -> Result