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:
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