Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@
"BackupFileDownload": "Stáhnout soubor",
"BackupFiles": "Soubory záloh",
"BackupNoBackup": "V současné době nejsou k dispozici žádné zálohy.",
"BackupDownloadAll": "Stáhnout úplnou zálohu",
"BackupPreparingDownload": "Příprava zálohy…",
"BackupDownloadAllInfo": "Stáhněte všechny soubory zálohy jako jeden archiv .zip, který si můžete uložit do počítače.",
"BackupCopyScript": "Kopírovat skript pro stažení",
"BackupCopyToken": "Kopírovat token",
"BackupScriptInfo": "Shellový skript, který stáhne všechny soubory zálohy pomocí curl. Uložte jej a spusťte v terminálu; vyžádá si váš token zálohy, takže ve skriptu nejsou uložena žádná tajemství.",
"NonBackupedBlobs": "Non Backed up blobs",
"Calendar": "Kalendář",
"StartOfTheWeek": "Začátek týdne",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@
"BackupFileDownload": "Datei herunterladen",
"BackupFiles": "Sicherungsdateien",
"BackupNoBackup": "Derzeit sind keine Sicherungen verfügbar.",
"BackupDownloadAll": "Vollständige Sicherung herunterladen",
"BackupPreparingDownload": "Sicherung wird vorbereitet…",
"BackupDownloadAllInfo": "Lade alle Sicherungsdateien als ein einzelnes .zip-Archiv herunter, das du auf deinem Computer aufbewahren kannst.",
"BackupCopyScript": "Download-Skript kopieren",
"BackupCopyToken": "Token kopieren",
"BackupScriptInfo": "Ein Shell-Skript, das alle Sicherungsdateien mit curl herunterlädt. Speichere es und führe es in einem Terminal aus; es fragt nach deinem Sicherungs-Token, sodass keine Geheimnisse im Skript gespeichert werden.",
"NonBackupedBlobs": "Nicht gesicherte Blobs",
"Calendar": "Kalender",
"StartOfTheWeek": "Wochenstart",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@
"BackupFileDownload": "Download File",
"BackupFiles": "Backup Files",
"BackupNoBackup": "No backups are currently available.",
"BackupDownloadAll": "Download full backup",
"BackupPreparingDownload": "Preparing backup…",
"BackupDownloadAllInfo": "Download every backup file as a single .zip archive you can keep on your computer.",
"BackupCopyScript": "Copy download script",
"BackupCopyToken": "Copy token",
"BackupScriptInfo": "A shell script that downloads every backup file with curl. Save it and run it in a terminal; it prompts for your backup token, so no secrets are stored in the script.",
"NonBackupedBlobs": "Non Backed up blobs",
"Calendar": "Calendar",
"StartOfTheWeek": "Start of the week",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@
"BackupFileDownload": "Descargar archivo",
"BackupFiles": "Archivos de copia de seguridad",
"BackupNoBackup": "No hay copias de seguridad disponibles actualmente.",
"BackupDownloadAll": "Descargar copia de seguridad completa",
"BackupPreparingDownload": "Preparando la copia de seguridad…",
"BackupDownloadAllInfo": "Descarga todos los archivos de la copia de seguridad en un único archivo .zip que puedes guardar en tu ordenador.",
"BackupCopyScript": "Copiar script de descarga",
"BackupCopyToken": "Copiar token",
"BackupScriptInfo": "Un script de shell que descarga todos los archivos de la copia de seguridad con curl. Guárdalo y ejecútalo en una terminal; te pedirá tu token de copia de seguridad, por lo que no se almacenan secretos en el script.",
"NonBackupedBlobs": "Blobs no respaldados",
"Calendar": "Calendario",
"StartOfTheWeek": "Inicio de la semana",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@
"BackupFileDownload": "Télécharger le fichier",
"BackupFiles": "Fichiers de sauvegarde",
"BackupNoBackup": "Aucune sauvegarde n'est actuellement disponible.",
"BackupDownloadAll": "Télécharger la sauvegarde complète",
"BackupPreparingDownload": "Préparation de la sauvegarde…",
"BackupDownloadAllInfo": "Téléchargez tous les fichiers de sauvegarde dans une seule archive .zip que vous pouvez conserver sur votre ordinateur.",
"BackupCopyScript": "Copier le script de téléchargement",
"BackupCopyToken": "Copier le jeton",
"BackupScriptInfo": "Un script shell qui télécharge tous les fichiers de sauvegarde avec curl. Enregistrez-le et exécutez-le dans un terminal ; il demande votre jeton de sauvegarde, aucun secret n'est donc stocké dans le script.",
"NonBackupedBlobs": "Non Backed up blobs",
"Calendar": "Calendrier",
"StartOfTheWeek": "Début de semaine",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@
"BackupFileDownload": "Scarica file",
"BackupFiles": "File di backup",
"BackupNoBackup": "Non sono attualmente disponibili backup.",
"BackupDownloadAll": "Scarica backup completo",
"BackupPreparingDownload": "Preparazione del backup…",
"BackupDownloadAllInfo": "Scarica tutti i file di backup in un unico archivio .zip che puoi conservare sul tuo computer.",
"BackupCopyScript": "Copia script di download",
"BackupCopyToken": "Copia token",
"BackupScriptInfo": "Uno script shell che scarica tutti i file di backup con curl. Salvalo ed eseguilo in un terminale; richiederà il tuo token di backup, quindi nello script non vengono memorizzati segreti.",
"NonBackupedBlobs": "Non Backed up blobs",
"Calendar": "Calendario",
"StartOfTheWeek": "Inizio settimana",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@
"BackupFileDownload": "ファイルをダウンロード",
"BackupFiles": "バックアップファイル",
"BackupNoBackup": "現在、利用可能なバックアップはありません。",
"BackupDownloadAll": "完全バックアップをダウンロード",
"BackupPreparingDownload": "バックアップを準備中…",
"BackupDownloadAllInfo": "すべてのバックアップファイルを 1 つの .zip アーカイブとしてダウンロードし、コンピューターに保存できます。",
"BackupCopyScript": "ダウンロードスクリプトをコピー",
"BackupCopyToken": "トークンをコピー",
"BackupScriptInfo": "curl ですべてのバックアップファイルをダウンロードするシェルスクリプトです。保存してターミナルで実行してください。バックアップトークンの入力を求められるため、スクリプトに機密情報は保存されません。",
"NonBackupedBlobs": "バックアップされていないブロブ",
"Calendar": "カレンダー",
"StartOfTheWeek": "週の開始",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@
"BackupFileDownload": "파일 다운로드",
"BackupFiles": "백업 파일",
"BackupNoBackup": "현재 사용 가능한 백업이 없습니다.",
"BackupDownloadAll": "전체 백업 다운로드",
"BackupPreparingDownload": "백업 준비 중…",
"BackupDownloadAllInfo": "모든 백업 파일을 컴퓨터에 보관할 수 있는 단일 .zip 아카이브로 다운로드합니다.",
"BackupCopyScript": "다운로드 스크립트 복사",
"BackupCopyToken": "토큰 복사",
"BackupScriptInfo": "curl로 모든 백업 파일을 다운로드하는 셸 스크립트입니다. 저장한 후 터미널에서 실행하세요. 백업 토큰을 입력하라는 메시지가 표시되므로 스크립트에 비밀 정보가 저장되지 않습니다.",
"NonBackupedBlobs": "백업되지 않은 Blob",
"Calendar": "캘린더",
"StartOfTheWeek": "주 시작일",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@
"BackupFileDownload": "Pobierz plik",
"BackupFiles": "Pliki kopii zapasowej",
"BackupNoBackup": "Aktualnie brak kopii zapasowych.",
"BackupDownloadAll": "Pobierz pełną kopię zapasową",
"BackupPreparingDownload": "Przygotowywanie kopii zapasowej…",
"BackupDownloadAllInfo": "Pobierz wszystkie pliki kopii zapasowej jako jedno archiwum .zip, które możesz zachować na komputerze.",
"BackupCopyScript": "Kopiuj skrypt pobierania",
"BackupCopyToken": "Kopiuj token",
"BackupScriptInfo": "Skrypt powłoki, który pobiera wszystkie pliki kopii zapasowej za pomocą curl. Zapisz go i uruchom w terminalu; poprosi o token kopii zapasowej, więc w skrypcie nie są przechowywane żadne sekrety.",
"NonBackupedBlobs": "Pliki nieobjęte kopią zapasową",
"Calendar": "Kalendarz",
"StartOfTheWeek": "Początek tygodnia",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/pt-br.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@
"BackupFileDownload": "Baixar Arquivo",
"BackupFiles": "Arquivos de Backup",
"BackupNoBackup": "Não há backups disponíveis no momento.",
"BackupDownloadAll": "Baixar backup completo",
"BackupPreparingDownload": "Preparando backup…",
"BackupDownloadAllInfo": "Baixe todos os arquivos de backup em um único arquivo .zip que você pode guardar no seu computador.",
"BackupCopyScript": "Copiar script de download",
"BackupCopyToken": "Copiar token",
"BackupScriptInfo": "Um script de shell que baixa todos os arquivos de backup com curl. Salve-o e execute-o em um terminal; ele solicitará seu token de backup, então nenhum segredo é armazenado no script.",
"NonBackupedBlobs": "Blobs sem backup",
"Calendar": "Calendário",
"StartOfTheWeek": "Início da semana",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@
"BackupFileDownload": "Baixar Arquivo",
"BackupFiles": "Arquivos de Backup",
"BackupNoBackup": "Não há backups disponíveis no momento.",
"BackupDownloadAll": "Transferir cópia de segurança completa",
"BackupPreparingDownload": "A preparar a cópia de segurança…",
"BackupDownloadAllInfo": "Transfira todos os ficheiros da cópia de segurança num único arquivo .zip que pode guardar no seu computador.",
"BackupCopyScript": "Copiar script de transferência",
"BackupCopyToken": "Copiar token",
"BackupScriptInfo": "Um script de shell que transfere todos os ficheiros da cópia de segurança com curl. Guarde-o e execute-o num terminal; irá pedir o seu token da cópia de segurança, pelo que não são armazenados segredos no script.",
"NonBackupedBlobs": "Non Backed up blobs",
"Calendar": "Calendário",
"StartOfTheWeek": "Início da semana",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@
"BackupFileDownload": "Скачать файл",
"BackupFiles": "Файлы резервной копии",
"BackupNoBackup": "В настоящее время резервные копии недоступны.",
"BackupDownloadAll": "Скачать полную резервную копию",
"BackupPreparingDownload": "Подготовка резервной копии…",
"BackupDownloadAllInfo": "Скачать все файлы резервной копии одним архивом .zip, который можно сохранить на компьютере.",
"BackupCopyScript": "Копировать скрипт загрузки",
"BackupCopyToken": "Копировать токен",
"BackupScriptInfo": "Скрипт оболочки, который скачивает все файлы резервной копии с помощью curl. Сохраните его и запустите в терминале; он запросит токен резервной копии, поэтому в скрипте не хранятся секреты.",
"NonBackupedBlobs": "Не резервируемые блобы",
"Calendar": "Календарь",
"StartOfTheWeek": "Начало недели",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@
"BackupFileDownload": "Dosya İndir",
"BackupFiles": "Yedek Dosyaları",
"BackupNoBackup": "Şu anda kullanılabilir yedek yok.",
"BackupDownloadAll": "Tam yedeği indir",
"BackupPreparingDownload": "Yedek hazırlanıyor…",
"BackupDownloadAllInfo": "Tüm yedek dosyalarını bilgisayarınızda saklayabileceğiniz tek bir .zip arşivi olarak indirin.",
"BackupCopyScript": "İndirme betiğini kopyala",
"BackupCopyToken": "Jetonu kopyala",
"BackupScriptInfo": "Tüm yedek dosyalarını curl ile indiren bir kabuk betiği. Kaydedip bir terminalde çalıştırın; yedek jetonunuzu ister, bu nedenle betikte hiçbir gizli bilgi saklanmaz.",
"NonBackupedBlobs": "Yedeklenmemiş blob'lar",
"Calendar": "Takvim",
"StartOfTheWeek": "Haftanın başlangıcı",
Expand Down
6 changes: 6 additions & 0 deletions plugins/setting-assets/lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@
"BackupFileDownload": "下载文件",
"BackupFiles": "备份文件",
"BackupNoBackup": "当前没有可用的备份。",
"BackupDownloadAll": "下载完整备份",
"BackupPreparingDownload": "正在准备备份…",
"BackupDownloadAllInfo": "将所有备份文件下载为一个 .zip 压缩包,可保存到您的计算机上。",
"BackupCopyScript": "复制下载脚本",
"BackupCopyToken": "复制令牌",
"BackupScriptInfo": "一个使用 curl 下载所有备份文件的 shell 脚本。保存并在终端中运行;它会提示输入您的备份令牌,因此脚本中不会存储任何机密信息。",
"NonBackupedBlobs": "未备份的 Blob",
"Calendar": "日历",
"StartOfTheWeek": "本周开始",
Expand Down
156 changes: 156 additions & 0 deletions plugins/setting-resources/src/__tests__/backup.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//
// Copyright © 2025 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//

import {
buildStoreZip,
collectBackupFileNames,
crc32,
generateBackupScript,
generateRestoreReadme
} from '../utils/backup'

describe('collectBackupFileNames', () => {
it('returns the well-known root files when index is empty', () => {
expect(collectBackupFileNames(undefined)).toEqual(['index.json', 'backup.json.gz', 'blob-info.json.gz'])
})

it('includes index files and snapshot files without duplicates', () => {
const names = collectBackupFileNames({
files: [
{ name: 'backup.json.gz', size: 1 },
{ name: 'tx-0.snp.gz', size: 2 }
],
info: {
workspace: 'ws',
version: '0.7',
snapshots: [
{
date: 1,
domains: {
tx: {
storage: ['tx-0.snp.gz', 'tx-1.snp.gz'],
snapshots: ['tx.snapshot.gz'],
added: 0,
updated: 0,
removed: 0
}
}
}
]
}
})

expect(names).toEqual([
'index.json',
'backup.json.gz',
'blob-info.json.gz',
'tx-0.snp.gz',
'tx-1.snp.gz',
'tx.snapshot.gz'
])
})
})

describe('generateBackupScript', () => {
const script = generateBackupScript({
baseUrl: 'https://example.com/backup/ws-1/',
files: ['index.json', 'tx-0.snp.gz']
})

it('embeds the base url without a trailing slash', () => {
expect(script).toContain("BASE_URL='https://example.com/backup/ws-1'")
})

it('never embeds the token; reads it from env or prompts at runtime', () => {
expect(script).not.toContain("TOKEN='")
expect(script).toContain('HULY_BACKUP_TOKEN')
expect(script).toContain('read -rsp')
})

it('emits a download line per file', () => {
expect(script).toContain("download 'index.json'")
expect(script).toContain("download 'tx-0.snp.gz'")
})

it('escapes single quotes in values', () => {
const evil = generateBackupScript({ baseUrl: "ho'st", files: [] })
expect(evil).toContain("BASE_URL='ho'\\''st'")
})

it('writes the restore readme when provided', () => {
const withReadme = generateBackupScript({
baseUrl: 'u',
files: [],
restoreReadme: '# Restore me\n'
})
expect(withReadme).toContain('RESTORE.md')
expect(withReadme).toContain('# Restore me')
})
})

describe('generateRestoreReadme', () => {
const readme = generateRestoreReadme({ sourceWorkspace: 'ws-uuid-123', fileCount: 7 })

it('names the source workspace and file count', () => {
expect(readme).toContain('ws-uuid-123')
expect(readme).toContain('7')
})

it('documents the exact restore command', () => {
expect(readme).toContain('tool backup-restore')
})

it('warns about blobs excluded from the backup', () => {
expect(readme.toLowerCase()).toContain('video')
expect(readme.toLowerCase()).toContain('blob')
})
})

describe('crc32', () => {
it('matches the standard check value for "123456789"', () => {
expect(crc32(new TextEncoder().encode('123456789'))).toBe(0xcbf43926)
})

it('is zero for empty input', () => {
expect(crc32(new Uint8Array(0))).toBe(0)
})
})

describe('buildStoreZip', () => {
it('produces a valid local-file and EOCD signature', () => {
const zip = buildStoreZip([{ name: 'a.txt', data: new TextEncoder().encode('hello') }])
const view = new DataView(zip.buffer)
expect(view.getUint32(0, true)).toBe(0x04034b50) // local file header
expect(view.getUint32(zip.length - 22, true)).toBe(0x06054b50) // EOCD
})

it('records every entry in the central directory', () => {
const zip = buildStoreZip([
{ name: 'a.txt', data: new TextEncoder().encode('hello') },
{ name: 'b.txt', data: new TextEncoder().encode('world!') }
])
const view = new DataView(zip.buffer)
const totalEntries = view.getUint16(zip.length - 22 + 10, true)
expect(totalEntries).toBe(2)
})

it('stores data uncompressed so the payload is embedded verbatim', () => {
const payload = new TextEncoder().encode('hello')
const zip = buildStoreZip([{ name: 'a.txt', data: payload }])
// local header is 30 bytes + 5-byte name => data begins at offset 35
const stored = zip.slice(35, 40)
expect(new TextDecoder().decode(stored)).toBe('hello')
})
})
Loading
Loading