web


git clone https://radroots.dev/git/web.git
Log | Files | Refs | Submodules | README | LICENSE

commit 120b29cc610121cd81c5da9e927bfbb03ab80129
parent dd8c004fe0fcae919409ca2135a5d4755e0f7cf4
Author: triesap <triesap@radroots.dev>
Date:   Sun, 28 Dec 2025 19:57:16 +0000

settings: reset init state and sync Nostr before DB export

- Export __APP_INFO__ constants for app name/version/hash
- Add app_init_reset() and call it during reset/logout flows
- Sync Nostr events using signers/relays prior to database export
- Normalize nostr_profile profile_type mapping and bump workspace deps

Diffstat:
Mapp/src/lib/utils/app/index.ts | 17++++++++++++++++-
Mapp/src/routes/(app)/settings/+page.svelte | 135++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mapp/src/routes/(cfg)/setup/+page.svelte | 6+++++-
Mpnpm-lock.yaml | 5+++++
4 files changed, 126 insertions(+), 37 deletions(-)

diff --git a/app/src/lib/utils/app/index.ts b/app/src/lib/utils/app/index.ts @@ -15,9 +15,9 @@ import { WebTangleDatabase } from "@radroots/client/tangle"; import { Geocoder } from "@radroots/geocoder"; import { WebHttp } from "@radroots/http"; import type { CallbackPromise } from "@radroots/utils"; +import { writable } from "svelte/store"; import { reset_sql_cipher } from "./cipher"; import type { NavigationRoute } from "./routes"; -import { writable } from "svelte/store"; const { GEOCODER_DB_URL, RADROOTS_API, SQL_WASM_URL } = _env; @@ -27,6 +27,12 @@ declare const __APP_GIT_HASH__: string; declare const __APP_NAME__: string; declare const __APP_VERSION__: string; +export const __APP_INFO__ = { + git_hash: __APP_GIT_HASH__, + name: __APP_NAME__, + version: __APP_VERSION__ +} + export const datastore = new WebDatastore( cfg_datastore_key_map, cfg_datastore_key_param_map, @@ -89,6 +95,14 @@ export const app_init_has_completed = (): boolean => { return localStorage.getItem(app_init_storage_key) === "1"; }; +export const app_init_reset = (): void => { + if (typeof localStorage !== "undefined") localStorage.removeItem(app_init_storage_key); + app_init_state.set({ ...app_init_state_default }); + app_init_promise = null; + db_init_promise = null; + geoc_init_promise = null; +}; + const app_init_mark_completed = (): void => { if (typeof localStorage === "undefined") return; localStorage.setItem(app_init_storage_key, "1"); @@ -237,6 +251,7 @@ export const reset = async (): Promise<void> => { await datastore.reset(); await reset_sql_cipher(db.get_store_key()); await db.reinit(); + app_init_reset(); goto(`/`); app_notify.set(`${ls_val(`notification.device.reset_complete`)}`); } catch (e) { diff --git a/app/src/routes/(app)/settings/+page.svelte b/app/src/routes/(app)/settings/+page.svelte @@ -1,56 +1,121 @@ <script lang="ts"> + import { + __APP_INFO__, + app_init, + app_init_reset, + datastore, + db, + nostr_keys, + notif, + } from "$lib/utils/app"; + import type { cfg_datastore_key_obj_map_types } from "$lib/utils/config"; import { ls } from "$lib/utils/i18n"; - import { Settings } from "@radroots/apps-lib-pwa"; import { get_store, handle_err } from "@radroots/apps-lib"; + import { Settings } from "@radroots/apps-lib-pwa"; + import type { + NostrEventEnvelope, + TangleDatabaseExportSigner, + TangleNostrSyncSigner + } from "@radroots/client/tangle"; 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"); + app_init_reset(); + }; + + const load_sync_signers = async (): Promise<TangleNostrSyncSigner[]> => { + const keys_res = await nostr_keys.keys(); + if ("err" in keys_res) throw new Error(keys_res.err); + const signers: TangleNostrSyncSigner[] = []; + for (const public_key of keys_res.results) { + const secret_res = await nostr_keys.read(public_key); + if ("err" in secret_res) throw new Error(secret_res.err); + const secret_key = secret_res.secret_key; + if (!secret_key || typeof secret_key !== "string") continue; + signers.push({ secret_key }); + } + if (!signers.length) throw new Error("nostr sync requires signer keys"); + return signers; + }; + + const load_sync_relays = async (public_key: string): Promise<string[]> => { + const relays_res = await db.nostr_relay_find_many({ + rel: { + on_profile: { + public_key, + }, + }, + }); + if ("err" in relays_res) throw new Error(relays_res.err); + const relays = Array.from( + new Set( + relays_res.results + .map((relay) => relay.url) + .filter((url) => typeof url === "string" && url.trim().length) + .map((url) => url.trim()) + ) + ); + if (!relays.length) throw new Error("nostr sync requires relays"); + return relays; + }; + + const sync_nostr_events = async (public_key: string): Promise<void> => { + const signers = await load_sync_signers(); + const relays = await load_sync_relays(public_key); + const sync_res = await db.nostr_sync_all({ + relays, + signers, + publish_timeout_ms: 10000, + }); + if (sync_res && "err" in sync_res) throw new Error(sync_res.err); }; 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"); + console.log(`done app_init()`); + const app_data = + await datastore.get_obj< + cfg_datastore_key_obj_map_types["app_data"] + >("app_data"); + console.log(JSON.stringify(app_data, null, 4), `app_data`); + if ("err" in app_data) throw new Error(app_data.err); + await sync_nostr_events(app_data.result.active_key); 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 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.rust.export_version, + schema_hash: manifest.rust.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 + app_name: __APP_INFO__.name, + app_version: __APP_INFO__.version, + signer, }); + console.log(`res `, res); if (res && "err" in res) throw new Error(res.err); } catch (e) { handle_err(e, "settings.export_database"); diff --git a/app/src/routes/(cfg)/setup/+page.svelte b/app/src/routes/(cfg)/setup/+page.svelte @@ -560,9 +560,13 @@ public_key: string, config_data: ConfigData, ): Promise<ResultPass | IError<string>> => { + const profile_type = + config_data.role === `farmer` + ? `individual` + : config_data.role ?? `individual`; const nostr_profile_add = await db.nostr_profile_create({ public_key, - profile_type: config_data.role ?? "individual", + profile_type, name: config_data.nostr_profile ? config_data.nostr_profile : `${$ls(`common.default`)}`, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml @@ -83,6 +83,8 @@ importers: ../crates/tangle-db-wasm/pkg: {} + ../crates/tangle-events-wasm/pkg: {} + ../crates/trade/bindings/ts: dependencies: '@radroots/core-bindings': @@ -798,6 +800,9 @@ importers: '@radroots/tangle-db-wasm': specifier: workspace:* version: link:../../../crates/tangle-db-wasm/pkg + '@radroots/tangle-events-wasm': + specifier: workspace:* + version: link:../../../crates/tangle-events-wasm/pkg '@radroots/types-bindings': specifier: workspace:* version: link:../../../crates/types/bindings/ts