From 99492b36c87c3bc7350ab3557979bec0f42152ab Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Tue, 28 Feb 2023 11:46:40 +0200 Subject: [PATCH] feat: init merchant --- crud.py | 42 +++++++++++++++++++++++++++++ migrations.py | 3 ++- models.py | 25 ++++++++++++++++++ static/js/index.js | 43 +++++++++++++++++++++++++++--- templates/nostrmarket/index.html | 14 +++++++--- views_api.py | 45 ++++++++++++++++++++++++++++++++ 6 files changed, 164 insertions(+), 8 deletions(-) create mode 100644 crud.py create mode 100644 models.py diff --git a/crud.py b/crud.py new file mode 100644 index 0000000..8f41bff --- /dev/null +++ b/crud.py @@ -0,0 +1,42 @@ +import json +from typing import Optional + +from lnbits.helpers import urlsafe_short_hash + +from . import db +from .models import Merchant, PartialMerchant + + +async def create_merchant(user_id: str, m: PartialMerchant) -> Merchant: + merchant_id = urlsafe_short_hash() + await db.execute( + """ + INSERT INTO nostrmarket.merchants (user_id, id, private_key, public_key, meta) + VALUES (?, ?, ?, ?, ?) + """, + (user_id, merchant_id, m.private_key, m.public_key, json.dumps(dict(m.config))), + ) + merchant = await get_merchant(user_id, merchant_id) + assert merchant, "Created merchant cannot be retrieved" + return merchant + + +async def get_merchant(user_id: str, merchant_id: str) -> Optional[Merchant]: + row = await db.fetchone( + """SELECT * FROM nostrmarket.merchants WHERE user_id = ? AND id = ?""", + ( + user_id, + merchant_id, + ), + ) + + return Merchant.from_row(row) if row else None + + +async def get_merchant_for_user(user_id: str) -> Optional[Merchant]: + row = await db.fetchone( + """SELECT * FROM nostrmarket.merchants WHERE user_id = ? """, + (user_id,), + ) + + return Merchant.from_row(row) if row else None diff --git a/migrations.py b/migrations.py index 02565f7..0880b62 100644 --- a/migrations.py +++ b/migrations.py @@ -7,9 +7,10 @@ async def m001_initial(db): """ CREATE TABLE nostrmarket.merchants ( user_id TEXT NOT NULL, + id TEXT PRIMARY KEY, private_key TEXT NOT NULL, public_key TEXT NOT NULL, - config TEXT NOT NULL + meta TEXT NOT NULL DEFAULT '{}' ); """ ) diff --git a/models.py b/models.py new file mode 100644 index 0000000..e6a4e5e --- /dev/null +++ b/models.py @@ -0,0 +1,25 @@ +import json +from sqlite3 import Row +from typing import Optional + +from pydantic import BaseModel + + +class MerchantConfig(BaseModel): + name: Optional[str] + + +class PartialMerchant(BaseModel): + private_key: str + public_key: str + config: MerchantConfig = MerchantConfig() + + +class Merchant(PartialMerchant): + id: str + + @classmethod + def from_row(cls, row: Row) -> "Merchant": + merchant = cls(**dict(row)) + merchant.config = MerchantConfig(**json.loads(row["meta"])) + return merchant diff --git a/static/js/index.js b/static/js/index.js index 71d65d5..a5adbd6 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -1,15 +1,52 @@ -const stalls = async () => { +const merchant = async () => { Vue.component(VueQrcode.name, VueQrcode) await stallDetails('static/components/stall-details/stall-details.html') + const nostr = window.NostrTools + new Vue({ el: '#vue', mixins: [windowMixin], data: function () { - return {} + return { + merchant: null + } + }, + methods: { + generateKeys: async function () { + const privkey = nostr.generatePrivateKey() + const pubkey = nostr.getPublicKey(privkey) + + const data = {private_key: privkey, public_key: pubkey, config: {}} + try { + const resp = await LNbits.api.request( + 'POST', + '/nostrmarket/api/v1/merchant', + this.g.user.wallets[0].adminkey, + data + ) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + getMerchant: async function () { + try { + const {data} = await LNbits.api.request( + 'get', + '/nostrmarket/api/v1/merchant', + this.g.user.wallets[0].adminkey + ) + this.merchant = data + } catch (error) { + LNbits.utils.notifyApiError(error) + } + } + }, + created: async function () { + await this.getMerchant() } }) } -stalls() +merchant() diff --git a/templates/nostrmarket/index.html b/templates/nostrmarket/index.html index 410fbf4..37710a5 100644 --- a/templates/nostrmarket/index.html +++ b/templates/nostrmarket/index.html @@ -2,7 +2,7 @@ %} {% block page %}
- + Wellcome to Nostr Market!
In Nostr Market, merchant and customer communicate via NOSTR relays, so @@ -41,15 +41,14 @@ disabled label="Import Key" color="primary" - @click="importKey" class="float-left" > Use an existing private key (hex or npub) A new key pair will be generated for you @@ -58,6 +57,11 @@
+
+ + Merchant Exists + +
@@ -75,6 +79,8 @@
{% endblock%}{% block scripts %} {{ window_vars(user) }} + + diff --git a/views_api.py b/views_api.py index e69de29..ad4d66e 100644 --- a/views_api.py +++ b/views_api.py @@ -0,0 +1,45 @@ +from http import HTTPStatus +from typing import Optional + +from fastapi import Depends +from fastapi.exceptions import HTTPException +from loguru import logger + +from lnbits.decorators import WalletTypeInfo, require_admin_key, require_invoice_key + +from . import nostrmarket_ext +from .crud import create_merchant, get_merchant_for_user +from .models import Merchant, PartialMerchant + + +@nostrmarket_ext.post("/api/v1/merchant") +async def api_create_merchant( + data: PartialMerchant, + wallet: WalletTypeInfo = Depends(require_admin_key), +) -> Merchant: + + try: + merchant = await create_merchant(wallet.wallet.user, data) + return merchant + except Exception as ex: + logger.warning(ex) + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, + detail="Cannot create merchant", + ) + + +@nostrmarket_ext.get("/api/v1/merchant") +async def api_get_merchant( + wallet: WalletTypeInfo = Depends(require_invoice_key), +) -> Optional[Merchant]: + + try: + merchant = await get_merchant_for_user(wallet.wallet.user) + return merchant + except Exception as ex: + logger.warning(ex) + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, + detail="Cannot create merchant", + )