diff --git a/src/modules/market/composables/useMarket.ts b/src/modules/market/composables/useMarket.ts index 66af02c..47ccaa0 100644 --- a/src/modules/market/composables/useMarket.ts +++ b/src/modules/market/composables/useMarket.ts @@ -14,32 +14,6 @@ const MARKET_EVENT_KINDS = { PRODUCT: 30018 } as const -/** - * Resolve a product's parent stall id from the event. - * - * NIP-15 lists `stall_id` inside the JSON `content`, but some publishers - * (older nostrmarket builds, third-party clients) only emit the parent - * reference via an `a` tag of the form - * ["a", "30017::"] - * - * Read content first, then fall back to the tag, then a sentinel that won't - * match any real stall. Returning the tag form prevents "Unknown Stall" - * from sticking when the JSON omits the field. - */ -function resolveStallId(event: any, productData: any): string { - if (productData?.stall_id && typeof productData.stall_id === 'string') { - return productData.stall_id - } - const aTag = event.tags?.find( - (t: any) => Array.isArray(t) && t[0] === 'a' && typeof t[1] === 'string' && t[1].startsWith(`${MARKET_EVENT_KINDS.STALL}:`) - ) - if (aTag) { - const parts = aTag[1].split(':') - if (parts[2]) return parts[2] - } - return 'unknown' -} - export function useMarket() { const marketStore = useMarketStore() const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB) as any @@ -341,7 +315,7 @@ export function useMarket() { try { const productData = JSON.parse(latestEvent.content) - const stallId = resolveStallId(latestEvent, productData) + const stallId = productData.stall_id || 'unknown' // Extract categories from Nostr event tags (standard approach) const categories = latestEvent.tags @@ -541,7 +515,7 @@ export function useMarket() { const productId = event.tags.find((tag: any) => tag[0] === 'd')?.[1] if (productId) { const productData = JSON.parse(event.content) - const stallId = resolveStallId(event, productData) + const stallId = productData.stall_id || 'unknown' // Extract categories from Nostr event tags (standard approach) const categories = event.tags diff --git a/src/modules/market/composables/useMarketStallSelfHeal.ts b/src/modules/market/composables/useMarketStallSelfHeal.ts deleted file mode 100644 index 6a0eb65..0000000 --- a/src/modules/market/composables/useMarketStallSelfHeal.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { useAuth } from '@/composables/useAuthService' -import { injectService, SERVICE_TOKENS } from '@/core/di-container' -import type { NostrmarketAPI } from '../services/nostrmarketAPI' - -const SESSION_FLAG = 'market-stall-self-heal-checked' -const STALL_EVENT_KIND = 30017 - -/** - * Detect-and-recover from the LNbits orphan-stall bug - * (aiolabs/lnbits#10): _create_default_merchant provisions the merchant - * + stall in nostrmarket's internal SQLite but historically never - * published the kind-30017 stall event to relays. The upstream fix is - * already in c0f3743c on aiolabs/lnbits@demo, but it only helps NEW - * signups. Existing accounts whose auto-stall never made it to a relay - * stay orphaned until somebody republishes — which manifests in our - * webapp as "Unknown Stall" on every product authored by them. - * - * This composable runs once per browser session (sessionStorage gate) - * for any logged-in user who lands on the merchant dashboard: - * - * 1. Ask the relay for kind-30017 events authored by their pubkey. - * 2. Ask LNbits for the merchant's known stalls. - * 3. For each stall in (2) whose id isn't represented in (1), PUT the - * stall back to LNbits. The PUT path on the LNbits side already - * calls sign_and_send_to_nostr, so the kind-30017 event lands on - * the relay without any user interaction. - * - * Silent on success. Logs to console.info on republish; console.warn on - * failure. Never toasts — this is supposed to be invisible. - * - * Tracked in aiolabs/webapp#38. - */ -export function useMarketStallSelfHeal() { - const { user } = useAuth() - const relayHub = injectService(SERVICE_TOKENS.RELAY_HUB) as any - const nostrmarketAPI = injectService(SERVICE_TOKENS.NOSTRMARKET_API) as NostrmarketAPI - - async function selfHealOnce(): Promise { - if (sessionStorage.getItem(SESSION_FLAG)) return - - const currentUser = user.value - if (!currentUser?.pubkey) return - - const wallets = (currentUser as any).wallets as Array<{ adminkey?: string; inkey?: string }> | undefined - if (!wallets?.length) return - - const adminWallet = wallets.find(w => w.adminkey) || wallets[0] - if (!adminWallet?.adminkey || !adminWallet?.inkey) return - - // Mark checked early — even on failure we don't want to retry on every - // dashboard mount during the same tab session. - sessionStorage.setItem(SESSION_FLAG, '1') - - if (!relayHub || !nostrmarketAPI) { - console.warn('[market-self-heal] Required services unavailable, skipping') - return - } - - try { - const relayEvents: Array<{ tags?: Array<[string, string?]> }> = await relayHub.queryEvents([ - { kinds: [STALL_EVENT_KIND], authors: [currentUser.pubkey] }, - ]) - const publishedStallIds = new Set() - for (const ev of relayEvents) { - const dTag = ev.tags?.find(t => Array.isArray(t) && t[0] === 'd') - const stallId = dTag?.[1] - if (stallId) publishedStallIds.add(stallId) - } - - const lnbitsStalls = await nostrmarketAPI.getStalls(adminWallet.inkey) - const orphans = lnbitsStalls.filter(s => !publishedStallIds.has(s.id)) - - if (orphans.length === 0) { - console.info( - `[market-self-heal] All ${lnbitsStalls.length} stall(s) have a relay event — no recovery needed.`, - ) - return - } - - console.info( - `[market-self-heal] Republishing ${orphans.length} orphan stall(s):`, - orphans.map(s => `${s.id} (${s.name})`), - ) - - for (const stall of orphans) { - try { - await nostrmarketAPI.updateStall(adminWallet.adminkey, stall) - console.info(`[market-self-heal] Republished ${stall.id} (${stall.name})`) - } catch (err) { - console.warn(`[market-self-heal] Failed to republish ${stall.id}:`, err) - } - } - } catch (err) { - console.warn('[market-self-heal] Self-heal check failed:', err) - } - } - - return { selfHealOnce } -} diff --git a/src/modules/market/views/MarketDashboard.vue b/src/modules/market/views/MarketDashboard.vue index 5e08423..ded7018 100644 --- a/src/modules/market/views/MarketDashboard.vue +++ b/src/modules/market/views/MarketDashboard.vue @@ -77,7 +77,6 @@ import OrderHistory from '../components/OrderHistory.vue' import MerchantStore from '../components/MerchantStore.vue' import MarketSettings from '../components/MarketSettings.vue' import { auth } from '@/composables/useAuthService' -import { useMarketStallSelfHeal } from '../composables/useMarketStallSelfHeal' const route = useRoute() const marketStore = useMarketStore() @@ -140,7 +139,6 @@ const tabs = computed(() => [ ]) const router = useRouter() -const { selfHealOnce } = useMarketStallSelfHeal() // Lifecycle onMounted(() => { @@ -155,9 +153,6 @@ onMounted(() => { return } console.log('Market Dashboard mounted') - // Self-heal orphan stalls (issue #38) once per browser session. - // Fire-and-forget — never blocks the dashboard render. - void selfHealOnce() })