From 26681195b0aae8db1f475c2a9c08d547f8bc669f Mon Sep 17 00:00:00 2001 From: pavlov346346-source Date: Tue, 15 Sep 2026 17:30:31 +0300 Subject: [PATCH] fix: accept emergency export format in Settings backup restore The Settings "load from file" button only understood its own sun-catering-backup format. Extend it to also read the caterium-full-export format produced by the browser-console emergency export (localStorage + IndexedDB), so an already-downloaded export file can be restored directly through the UI. Co-Authored-By: Claude Sonnet 5 --- public/app-runtime.js | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/public/app-runtime.js b/public/app-runtime.js index b73670d..9d17313 100644 --- a/public/app-runtime.js +++ b/public/app-runtime.js @@ -464,12 +464,35 @@ window.SUN_LEGACY_CATALOG_V175=[{"id":"1","name":"Фуршетный бокс // ---------------- Backup to/from JSON file ---------------- function backupFileState(){const storage={};for(let i=0;iURL.revokeObjectURL(url),2000);toast('Файл резервной копии скачан.','success');}catch(_){toast('Не удалось создать файл резервной копии.','warn')}} + function restoreIdbFromExport(indexedDbDump){ + function openDb(name){return new Promise((resolve,reject)=>{const req=indexedDB.open(name);req.onsuccess=()=>resolve(req.result);req.onerror=()=>reject(req.error);});} + function putRecord(store,record){return new Promise((resolve,reject)=>{let req;try{req=store.put(record);}catch(_){const guessKey=record&&(record.id??record.key??record.path);try{req=store.put(record,guessKey);}catch(e2){reject(e2);return;}}req.onsuccess=()=>resolve();req.onerror=()=>reject(req.error);});} + return (async()=>{ + for(const [dbName,stores] of Object.entries(indexedDbDump||{})){ + let db;try{db=await openDb(dbName);}catch(_){continue;} + for(const [storeName,records] of Object.entries(stores||{})){ + if(!db.objectStoreNames.contains(storeName))continue; + const tx=db.transaction(storeName,'readwrite'),store=tx.objectStore(storeName); + for(const record of (records||[])){try{await putRecord(store,record);}catch(_){}} + await new Promise(resolve=>{tx.oncomplete=resolve;tx.onerror=resolve;}); + } + db.close(); + } + })(); + } async function restoreBackupFile(file){ let state; try{state=JSON.parse(await file.text());}catch(_){toast('Не удалось прочитать файл — это не JSON.','warn');return;} - if(!state||state.format!=='sun-catering-backup'||!state.storage||typeof state.storage!=='object'){toast('Неверный формат файла резервной копии.','warn');return;} + const isSettingsFormat=state&&state.format==='sun-catering-backup'&&state.storage&&typeof state.storage==='object'; + const isFullExport=state&&state.format==='caterium-full-export'&&state.localStorage&&typeof state.localStorage==='object'; + if(!isSettingsFormat&&!isFullExport){toast('Неверный формат файла резервной копии.','warn');return;} if(!confirm('Загрузить базу из файла? Текущие локальные данные будут заменены содержимым файла.'))return; - Object.entries(state.storage).forEach(([key,value])=>{if(key.startsWith('sun')){try{localStorage.setItem(key,String(value));}catch(_){}}}); + if(isSettingsFormat){ + Object.entries(state.storage).forEach(([key,value])=>{if(key.startsWith('sun')){try{localStorage.setItem(key,String(value));}catch(_){}}}); + }else{ + Object.entries(state.localStorage).forEach(([key,value])=>{if(key.startsWith('sun')){try{localStorage.setItem(key,String(value));}catch(_){}}}); + await restoreIdbFromExport(state.indexedDB); + } toast('База восстановлена из файла. Перезагрузка…','success'); setTimeout(()=>location.reload(),600); }