+page.svelte (6402B)
1 <script lang="ts"> 2 import { 3 __APP_INFO__, 4 app_init, 5 app_init_reset, 6 datastore, 7 db, 8 nostr_keys, 9 notif, 10 } from "$lib/utils/app"; 11 import type { cfg_datastore_key_obj_map_types } from "$lib/utils/config"; 12 import { ls } from "$lib/utils/i18n"; 13 import { get_store, handle_err } from "@radroots/apps-lib"; 14 import { Settings } from "@radroots/apps-lib-pwa"; 15 import type { 16 NostrEventEnvelope, 17 ReplicaDatabaseExportSigner, 18 ReplicaNostrSyncSigner 19 } from "@radroots/client/replica"; 20 import { nostr_event_sign } from "@radroots/nostr"; 21 22 const ls_val = get_store(ls); 23 24 const logout = async (): Promise<void> => { 25 app_init_reset(); 26 }; 27 28 const load_sync_signers = async (): Promise<ReplicaNostrSyncSigner[]> => { 29 const keys_res = await nostr_keys.keys(); 30 if ("err" in keys_res) throw new Error(keys_res.err); 31 const signers: ReplicaNostrSyncSigner[] = []; 32 for (const public_key of keys_res.results) { 33 const secret_res = await nostr_keys.read(public_key); 34 if ("err" in secret_res) throw new Error(secret_res.err); 35 const secret_key = secret_res.secret_key; 36 if (!secret_key || typeof secret_key !== "string") continue; 37 signers.push({ secret_key }); 38 } 39 if (!signers.length) throw new Error("nostr sync requires signer keys"); 40 return signers; 41 }; 42 43 const load_sync_relays = async (public_key: string): Promise<string[]> => { 44 const relays_res = await db.nostr_relay_find_many({ 45 rel: { 46 on_profile: { 47 public_key, 48 }, 49 }, 50 }); 51 if ("err" in relays_res) throw new Error(relays_res.err); 52 const relays = Array.from( 53 new Set( 54 relays_res.results 55 .map((relay) => relay.url) 56 .filter((url) => typeof url === "string" && url.trim().length) 57 .map((url) => url.trim()) 58 ) 59 ); 60 if (!relays.length) throw new Error("nostr sync requires relays"); 61 return relays; 62 }; 63 64 const sync_nostr_events = async (public_key: string): Promise<void> => { 65 const signers = await load_sync_signers(); 66 const relays = await load_sync_relays(public_key); 67 const sync_res = await db.nostr_sync_all({ 68 relays, 69 signers, 70 publish_timeout_ms: 10000, 71 }); 72 if (sync_res && "err" in sync_res) throw new Error(sync_res.err); 73 }; 74 75 const export_database = async (): Promise<void> => { 76 try { 77 await app_init(); 78 console.log(`done app_init()`); 79 const app_data = 80 await datastore.get_obj< 81 cfg_datastore_key_obj_map_types["app_data"] 82 >("app_data"); 83 console.log(JSON.stringify(app_data, null, 4), `app_data`); 84 if ("err" in app_data) throw new Error(app_data.err); 85 await sync_nostr_events(app_data.result.active_key); 86 let signer: ReplicaDatabaseExportSigner | undefined; 87 const active_key = app_data.result.active_key; 88 const secret_key = await nostr_keys.read(active_key); 89 if (!("err" in secret_key)) { 90 const key = secret_key.secret_key; 91 signer = async ({ 92 db_sha256, 93 manifest, 94 }): Promise<NostrEventEnvelope | null> => { 95 const payload = JSON.stringify({ 96 db_sha256, 97 export_version: manifest.rust.export_version, 98 schema_hash: manifest.rust.schema_hash, 99 }); 100 const event = nostr_event_sign({ 101 secret_key: key, 102 event: { 103 kind: 1, 104 created_at: Math.floor(Date.now() / 1000), 105 tags: [["t", "radroots:replica-db-export"]], 106 content: payload, 107 }, 108 }); 109 return event; 110 }; 111 } 112 113 const res = await db.export_database({ 114 app_name: __APP_INFO__.name, 115 app_version: __APP_INFO__.version, 116 signer, 117 }); 118 console.log(`res `, res); 119 if (res && "err" in res) throw new Error(res.err); 120 } catch (e) { 121 handle_err(e, "settings.export_database"); 122 await notif.alert(`${ls_val(`error.backup.export_failure`)}`); 123 } 124 }; 125 </script> 126 127 <Settings 128 basis={{ 129 trellis_ext: [ 130 { 131 list: [ 132 { 133 hide_active: true, 134 touch: { 135 label: { 136 left: [ 137 { 138 value: "export database", 139 classes: `capitalize`, 140 }, 141 ], 142 }, 143 end: { 144 glyph: { 145 key: `caret-right`, 146 }, 147 }, 148 callback: export_database, 149 }, 150 }, 151 { 152 hide_active: true, 153 touch: { 154 label: { 155 left: [ 156 { 157 value: `${$ls(`common.logout`)}`, 158 classes: `capitalize`, 159 }, 160 ], 161 }, 162 end: { 163 glyph: { 164 key: `caret-right`, 165 }, 166 }, 167 callback: logout, 168 }, 169 }, 170 ], 171 }, 172 ], 173 }} 174 />