Auto-provision merchant from account keypair on first access
The LNbits user account IS the merchant identity. GET /api/v1/merchant now auto-creates the merchant record using the account's existing Nostr keypair if one doesn't exist yet, so the extension is immediately usable without any setup screen. - Extract _auto_create_merchant() helper used by both GET and POST - Remove welcome/key-generation screen (replaced with loading spinner) - Remove dead frontend code (generateKeys, importKeys dialogs) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
725944ae9c
commit
5c38947fc6
3 changed files with 62 additions and 136 deletions
|
|
@ -13,19 +13,6 @@ window.app = Vue.createApp({
|
|||
orderPubkey: null,
|
||||
showKeys: false,
|
||||
stallCount: 0,
|
||||
importKeyDialog: {
|
||||
show: false,
|
||||
data: {
|
||||
privateKey: null
|
||||
}
|
||||
},
|
||||
generateKeyDialog: {
|
||||
show: false,
|
||||
privateKey: null,
|
||||
nsec: null,
|
||||
npub: null,
|
||||
showNsec: false
|
||||
},
|
||||
wsConnection: null,
|
||||
nostrStatus: {
|
||||
connected: false,
|
||||
|
|
@ -49,23 +36,6 @@ window.app = Vue.createApp({
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
generateKeys: async function () {
|
||||
// No longer need to generate keys here - the backend will use user's existing keypairs
|
||||
await this.createMerchant()
|
||||
},
|
||||
importKeys: async function () {
|
||||
this.importKeyDialog.show = false
|
||||
// Import keys functionality removed since we use user's native keypairs
|
||||
// Show a message that this is no longer needed
|
||||
this.$q.notify({
|
||||
type: 'info',
|
||||
message: 'Merchants now use your account Nostr keys automatically. Key import is no longer needed.',
|
||||
timeout: 3000
|
||||
})
|
||||
},
|
||||
showImportKeysDialog: async function () {
|
||||
this.importKeyDialog.show = true
|
||||
},
|
||||
toggleShowKeys: function () {
|
||||
this.showKeys = !this.showKeys
|
||||
},
|
||||
|
|
@ -379,7 +349,11 @@ window.app = Vue.createApp({
|
|||
}
|
||||
},
|
||||
created: async function () {
|
||||
await this.getMerchant()
|
||||
const merchant = await this.getMerchant()
|
||||
if (!merchant) {
|
||||
// Auto-create merchant using the account's existing Nostr keypair
|
||||
await this.createMerchant()
|
||||
}
|
||||
await this.checkNostrStatus()
|
||||
setInterval(async () => {
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -124,58 +124,9 @@
|
|||
</q-card>
|
||||
</div>
|
||||
<q-card v-else>
|
||||
<q-card-section>
|
||||
<span class="text-h4">Welcome to Nostr Market!</span><br />
|
||||
In Nostr Market, merchant and customer communicate via NOSTR relays, so
|
||||
loss of money, product information, and reputation become far less
|
||||
likely if attacked.
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<span class="text-h4">Terms</span><br />
|
||||
<ul>
|
||||
<li>
|
||||
<span class="text-bold">merchant</span> - seller of products with
|
||||
NOSTR key-pair
|
||||
</li>
|
||||
<li>
|
||||
<span class="text-bold">customer</span> - buyer of products with
|
||||
NOSTR key-pair
|
||||
</li>
|
||||
<li>
|
||||
<span class="text-bold">product</span> - item for sale by the
|
||||
merchant
|
||||
</li>
|
||||
<li>
|
||||
<span class="text-bold">stall</span> - list of products controlled
|
||||
by merchant (a merchant can have multiple stalls)
|
||||
</li>
|
||||
<li>
|
||||
<span class="text-bold">marketplace</span> - clientside software for
|
||||
searching stalls and purchasing products
|
||||
</li>
|
||||
</ul>
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<q-btn
|
||||
@click="showImportKeysDialog"
|
||||
label="Import Key"
|
||||
color="primary"
|
||||
class="float-left"
|
||||
>
|
||||
<q-tooltip> Use an existing private key (hex or npub) </q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
label="Generate New Key"
|
||||
color="green"
|
||||
@click="generateKeys"
|
||||
class="float-right"
|
||||
>
|
||||
<q-tooltip> A new key pair will be generated for you </q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</div>
|
||||
<q-card-section class="text-center q-pa-xl">
|
||||
<q-spinner color="primary" size="3em" class="q-mb-md"></q-spinner>
|
||||
<div class="text-h6">Setting up Nostr Market...</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</div>
|
||||
|
|
|
|||
107
views_api.py
107
views_api.py
|
|
@ -93,6 +93,56 @@ from .services import (
|
|||
######################################## MERCHANT ######################################
|
||||
|
||||
|
||||
async def _auto_create_merchant(
|
||||
wallet: WalletTypeInfo,
|
||||
config: MerchantConfig | None = None,
|
||||
) -> Merchant:
|
||||
"""
|
||||
Provision a merchant record from the user's account keypair.
|
||||
Called automatically on first GET or explicitly via POST.
|
||||
"""
|
||||
account = await get_account(wallet.wallet.user)
|
||||
assert account, "User account not found"
|
||||
|
||||
# In our fork, accounts always have keypairs.
|
||||
# Generate as fallback only if somehow missing.
|
||||
if not account.pubkey or not account.prvkey:
|
||||
private_key, public_key = generate_keypair()
|
||||
account.pubkey = public_key
|
||||
account.prvkey = private_key
|
||||
await update_account(account)
|
||||
else:
|
||||
public_key = account.pubkey
|
||||
private_key = account.prvkey
|
||||
|
||||
existing_merchant = await get_merchant_by_pubkey(public_key)
|
||||
assert existing_merchant is None, "A merchant already uses this public key"
|
||||
|
||||
partial_merchant = PartialMerchant(
|
||||
private_key=private_key,
|
||||
public_key=public_key,
|
||||
config=config or MerchantConfig(),
|
||||
)
|
||||
|
||||
merchant = await create_merchant(wallet.wallet.user, partial_merchant)
|
||||
|
||||
await create_zone(
|
||||
merchant.id,
|
||||
Zone(
|
||||
id=f"online-{merchant.public_key}",
|
||||
name="Online",
|
||||
currency="sat",
|
||||
cost=0,
|
||||
countries=["Free (digital)"],
|
||||
),
|
||||
)
|
||||
|
||||
await resubscribe_to_all_merchants()
|
||||
await nostr_client.merchant_temp_subscription(public_key)
|
||||
|
||||
return merchant
|
||||
|
||||
|
||||
@nostrmarket_ext.post("/api/v1/merchant")
|
||||
async def api_create_merchant(
|
||||
data: CreateMerchantRequest,
|
||||
|
|
@ -100,60 +150,10 @@ async def api_create_merchant(
|
|||
) -> Merchant:
|
||||
|
||||
try:
|
||||
# Check if merchant already exists for this user
|
||||
merchant = await get_merchant_for_user(wallet.wallet.user)
|
||||
assert merchant is None, "A merchant already exists for this user"
|
||||
|
||||
# Get user's account to access their Nostr keypairs
|
||||
account = await get_account(wallet.wallet.user)
|
||||
if not account:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND,
|
||||
detail="User account not found",
|
||||
)
|
||||
|
||||
# Check if user has Nostr keypairs, generate them if not
|
||||
if not account.pubkey or not account.prvkey:
|
||||
# Generate new keypair for user
|
||||
private_key, public_key = generate_keypair()
|
||||
|
||||
# Update user account with new keypairs
|
||||
account.pubkey = public_key
|
||||
account.prvkey = private_key
|
||||
await update_account(account)
|
||||
else:
|
||||
public_key = account.pubkey
|
||||
private_key = account.prvkey
|
||||
|
||||
# Check if another merchant is already using this public key
|
||||
existing_merchant = await get_merchant_by_pubkey(public_key)
|
||||
assert existing_merchant is None, "A merchant already uses this public key"
|
||||
|
||||
# Create PartialMerchant with user's keypairs
|
||||
partial_merchant = PartialMerchant(
|
||||
private_key=private_key,
|
||||
public_key=public_key,
|
||||
config=data.config
|
||||
)
|
||||
|
||||
merchant = await create_merchant(wallet.wallet.user, partial_merchant)
|
||||
|
||||
await create_zone(
|
||||
merchant.id,
|
||||
Zone(
|
||||
id=f"online-{merchant.public_key}",
|
||||
name="Online",
|
||||
currency="sat",
|
||||
cost=0,
|
||||
countries=["Free (digital)"],
|
||||
),
|
||||
)
|
||||
|
||||
await resubscribe_to_all_merchants()
|
||||
|
||||
await nostr_client.merchant_temp_subscription(public_key)
|
||||
|
||||
return merchant
|
||||
return await _auto_create_merchant(wallet, data.config)
|
||||
except AssertionError as ex:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.BAD_REQUEST,
|
||||
|
|
@ -170,12 +170,13 @@ async def api_create_merchant(
|
|||
@nostrmarket_ext.get("/api/v1/merchant")
|
||||
async def api_get_merchant(
|
||||
wallet: WalletTypeInfo = Depends(require_invoice_key),
|
||||
) -> Optional[Merchant]:
|
||||
) -> Merchant:
|
||||
|
||||
try:
|
||||
merchant = await get_merchant_for_user(wallet.wallet.user)
|
||||
if not merchant:
|
||||
return None
|
||||
# Auto-provision merchant from the user's account keypair
|
||||
merchant = await _auto_create_merchant(wallet)
|
||||
|
||||
merchant = await touch_merchant(wallet.wallet.user, merchant.id)
|
||||
assert merchant
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue