app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit e8dbcfb1c3482f8e2931f21d3a66a9094256eef0
parent fa8cc692e2f6b3c9798b895b0b3105ee82d8109c
Author: triesap <triesap@radroots.dev>
Date:   Sun, 28 Dec 2025 17:02:40 +0000

backup: switch tangle db backup to json and add export UI

- Replace db export/import backup calls with export_json/import_json
- Add settings action to export database with optional Nostr signer
- Surface export failures via handle_err and localized alert
- Bump packages submodule to include new database export API

Diffstat:
Mapp/src/lib/utils/backup/export.ts | 2+-
Mapp/src/lib/utils/backup/import.ts | 2+-
Mapp/src/routes/(app)/settings/+page.svelte | 79++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 78 insertions(+), 5 deletions(-)

diff --git a/app/src/lib/utils/backup/export.ts b/app/src/lib/utils/backup/export.ts @@ -55,7 +55,7 @@ const export_nostr_keystore_state = async (): Promise<ExportedAppState["nostr_ke const export_tangle_db_state = async (): Promise<ExportedAppState["database"]> => { await app_init(); const store_key = db.get_store_key(); - const backup = await db.export_backup(); + const backup = await db.export_json(); if ("err" in backup) throw_err(backup); return { store_key, backup }; }; diff --git a/app/src/lib/utils/backup/import.ts b/app/src/lib/utils/backup/import.ts @@ -110,7 +110,7 @@ const restore_tangle_db_state = async (state: ImportableAppState["database"]): P } await reset_sql_cipher(current_store_key); await db.reinit(); - await db.import_backup(state.backup); + await db.import_json(state.backup); }; export const import_app_state = async (payload: ImportableAppState): Promise<AppImportStateResult | IError<string>> => { diff --git a/app/src/routes/(app)/settings/+page.svelte b/app/src/routes/(app)/settings/+page.svelte @@ -1,6 +1,62 @@ <script lang="ts"> import { ls } from "$lib/utils/i18n"; import { Settings } from "@radroots/apps-lib-pwa"; + import { get_store, handle_err } from "@radroots/apps-lib"; + import { nostr_event_sign } from "@radroots/nostr"; + import { app_init, datastore, db, nostr_keys, notif } from "$lib/utils/app"; + import type { cfg_datastore_key_obj_map_types } from "$lib/utils/config"; + import type { NostrEventEnvelope, TangleDatabaseExportSigner } from "@radroots/client/tangle"; + + declare const __APP_NAME__: string; + declare const __APP_VERSION__: string; + + const ls_val = get_store(ls); + + const logout = async (): Promise<void> => { + alert("not implemented"); + }; + + const export_database = async (): Promise<void> => { + try { + await app_init(); + const app_data = await datastore.get_obj<cfg_datastore_key_obj_map_types["app_data"]>("app_data"); + let signer: TangleDatabaseExportSigner | undefined; + if (!("err" in app_data)) { + const active_key = app_data.result.active_key; + const secret_key = await nostr_keys.read(active_key); + if (!("err" in secret_key)) { + const key = secret_key.secret_key; + signer = async ({ db_sha256, manifest }): Promise<NostrEventEnvelope | null> => { + const payload = JSON.stringify({ + db_sha256, + export_version: manifest.rs.export_version, + schema_hash: manifest.rs.schema_hash + }); + const event = nostr_event_sign({ + secret_key: key, + event: { + kind: 1, + created_at: Math.floor(Date.now() / 1000), + tags: [["t", "radroots:tangle-db-export"]], + content: payload + } + }); + return event; + }; + } + } + + const res = await db.export_database({ + app_name: __APP_NAME__, + app_version: __APP_VERSION__, + signer + }); + if (res && "err" in res) throw new Error(res.err); + } catch (e) { + handle_err(e, "settings.export_database"); + await notif.alert(`${ls_val(`error.backup.export_failure`)}`); + } + }; </script> <Settings @@ -14,7 +70,7 @@ label: { left: [ { - value: `${$ls(`common.logout`)}`, + value: "export database", classes: `capitalize`, }, ], @@ -24,9 +80,26 @@ key: `caret-right`, }, }, - callback: async () => { - alert("not implemented"); + callback: export_database, + }, + }, + { + hide_active: true, + touch: { + label: { + left: [ + { + value: `${$ls(`common.logout`)}`, + classes: `capitalize`, + }, + ], + }, + end: { + glyph: { + key: `caret-right`, + }, }, + callback: logout, }, }, ],