commit
a1ec9f3e13
7 changed files with 138 additions and 14 deletions
19
helpers.py
19
helpers.py
|
|
@ -4,6 +4,7 @@ import secrets
|
||||||
from typing import Any, Optional, Tuple
|
from typing import Any, Optional, Tuple
|
||||||
|
|
||||||
import secp256k1
|
import secp256k1
|
||||||
|
from bech32 import bech32_decode, convertbits
|
||||||
from cffi import FFI
|
from cffi import FFI
|
||||||
from cryptography.hazmat.primitives import padding
|
from cryptography.hazmat.primitives import padding
|
||||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
|
@ -80,3 +81,21 @@ def order_from_json(s: str) -> Tuple[Optional[Any], Optional[str]]:
|
||||||
return (order, s) if (type(order) is dict) and "items" in order else (None, s)
|
return (order, s) if (type(order) is dict) and "items" in order else (None, s)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None, s
|
return None, s
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_public_key(pubkey: str) -> str:
|
||||||
|
if pubkey.startswith("npub1"):
|
||||||
|
_, decoded_data = bech32_decode(pubkey)
|
||||||
|
if not decoded_data:
|
||||||
|
raise ValueError("Public Key is not valid npub")
|
||||||
|
|
||||||
|
decoded_data_bits = convertbits(decoded_data, 5, 8, False)
|
||||||
|
if not decoded_data_bits:
|
||||||
|
raise ValueError("Public Key is not valid npub")
|
||||||
|
return bytes(decoded_data_bits).hex()
|
||||||
|
|
||||||
|
# check if valid hex
|
||||||
|
if len(pubkey) != 64:
|
||||||
|
raise ValueError("Public Key is not valid hex")
|
||||||
|
int(pubkey, 16)
|
||||||
|
return pubkey
|
||||||
|
|
|
||||||
|
|
@ -352,7 +352,7 @@ async def _handle_new_order(order: PartialOrder) -> Optional[str]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
async def _handle_new_customer(event, merchant):
|
async def _handle_new_customer(event, merchant: Merchant):
|
||||||
await create_customer(
|
await create_customer(
|
||||||
merchant.id, Customer(merchant_id=merchant.id, public_key=event.pubkey)
|
merchant.id, Customer(merchant_id=merchant.id, public_key=event.pubkey)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,31 @@
|
||||||
<q-separator></q-separator>
|
<q-separator></q-separator>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<q-select
|
<div class="row">
|
||||||
v-model="activePublicKey"
|
<div class="col-10">
|
||||||
:options="customers.map(c => ({label: buildCustomerLabel(c), value: c.public_key}))"
|
<q-select
|
||||||
label="Select Customer"
|
v-model="activePublicKey"
|
||||||
emit-value
|
:options="customers.map(c => ({label: buildCustomerLabel(c), value: c.public_key}))"
|
||||||
@input="selectActiveCustomer()"
|
label="Select Customer"
|
||||||
>
|
emit-value
|
||||||
</q-select>
|
@input="selectActiveCustomer()"
|
||||||
|
>
|
||||||
|
</q-select>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<q-btn
|
||||||
|
label="Add"
|
||||||
|
color="green"
|
||||||
|
class="float-right q-mt-md"
|
||||||
|
@click="showAddPublicKey = true"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
Add a public key to chat with
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<div class="chat-container" ref="chatCard">
|
<div class="chat-container" ref="chatCard">
|
||||||
|
|
@ -76,4 +93,30 @@
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
<div>
|
||||||
|
<q-dialog v-model="showAddPublicKey" position="top">
|
||||||
|
<q-card class="q-pa-lg q-pt-xl" style="width: 500px">
|
||||||
|
<q-form @submit="addPublicKey" class="q-gutter-md">
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
v-model.trim="newPublicKey"
|
||||||
|
label="Public Key (hex or nsec)"
|
||||||
|
></q-input>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
:disable="!newPublicKey"
|
||||||
|
type="submit"
|
||||||
|
>Add</q-btn
|
||||||
|
>
|
||||||
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
||||||
|
>Cancel</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</q-form>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ async function directMessages(path) {
|
||||||
const template = await loadTemplateAsync(path)
|
const template = await loadTemplateAsync(path)
|
||||||
Vue.component('direct-messages', {
|
Vue.component('direct-messages', {
|
||||||
name: 'direct-messages',
|
name: 'direct-messages',
|
||||||
props: ['active-chat-customer', 'adminkey', 'inkey'],
|
props: ['active-chat-customer', 'merchant-id', 'adminkey', 'inkey'],
|
||||||
template,
|
template,
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|
@ -19,7 +19,9 @@ async function directMessages(path) {
|
||||||
unreadMessages: 0,
|
unreadMessages: 0,
|
||||||
activePublicKey: null,
|
activePublicKey: null,
|
||||||
messages: [],
|
messages: [],
|
||||||
newMessage: ''
|
newMessage: '',
|
||||||
|
showAddPublicKey: false,
|
||||||
|
newPublicKey: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -56,7 +58,7 @@ async function directMessages(path) {
|
||||||
try {
|
try {
|
||||||
const {data} = await LNbits.api.request(
|
const {data} = await LNbits.api.request(
|
||||||
'GET',
|
'GET',
|
||||||
'/nostrmarket/api/v1/customers',
|
'/nostrmarket/api/v1/customer',
|
||||||
this.inkey
|
this.inkey
|
||||||
)
|
)
|
||||||
this.customers = data
|
this.customers = data
|
||||||
|
|
@ -83,6 +85,27 @@ async function directMessages(path) {
|
||||||
LNbits.utils.notifyApiError(error)
|
LNbits.utils.notifyApiError(error)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
addPublicKey: async function(){
|
||||||
|
try {
|
||||||
|
const {data} = await LNbits.api.request(
|
||||||
|
'POST',
|
||||||
|
'/nostrmarket/api/v1/customer',
|
||||||
|
this.adminkey,
|
||||||
|
{
|
||||||
|
public_key: this.newPublicKey,
|
||||||
|
merchant_id: this.merchantId,
|
||||||
|
unread_messages: 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
this.newPublicKey = null
|
||||||
|
this.activePublicKey = data.public_key
|
||||||
|
await this.selectActiveCustomer()
|
||||||
|
} catch (error) {
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
} finally {
|
||||||
|
this.showAddPublicKey = false
|
||||||
|
}
|
||||||
|
},
|
||||||
handleNewMessage: async function (data) {
|
handleNewMessage: async function (data) {
|
||||||
if (data.customerPubkey === this.activePublicKey) {
|
if (data.customerPubkey === this.activePublicKey) {
|
||||||
await this.getDirectMessages(this.activePublicKey)
|
await this.getDirectMessages(this.activePublicKey)
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ async function orderList(path) {
|
||||||
try {
|
try {
|
||||||
const {data} = await LNbits.api.request(
|
const {data} = await LNbits.api.request(
|
||||||
'GET',
|
'GET',
|
||||||
'/nostrmarket/api/v1/customers',
|
'/nostrmarket/api/v1/customer',
|
||||||
this.inkey
|
this.inkey
|
||||||
)
|
)
|
||||||
this.customers = data
|
this.customers = data
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@
|
||||||
:inkey="g.user.wallets[0].inkey"
|
:inkey="g.user.wallets[0].inkey"
|
||||||
:adminkey="g.user.wallets[0].adminkey"
|
:adminkey="g.user.wallets[0].adminkey"
|
||||||
:active-chat-customer="activeChatCustomer"
|
:active-chat-customer="activeChatCustomer"
|
||||||
|
:merchant-id="merchant.id"
|
||||||
@customer-selected="filterOrdersForCustomer"
|
@customer-selected="filterOrdersForCustomer"
|
||||||
>
|
>
|
||||||
</direct-messages>
|
</direct-messages>
|
||||||
|
|
|
||||||
40
views_api.py
40
views_api.py
|
|
@ -13,10 +13,12 @@ from lnbits.decorators import (
|
||||||
require_admin_key,
|
require_admin_key,
|
||||||
require_invoice_key,
|
require_invoice_key,
|
||||||
)
|
)
|
||||||
|
from lnbits.extensions.nostrmarket.helpers import normalize_public_key
|
||||||
from lnbits.utils.exchange_rates import currencies
|
from lnbits.utils.exchange_rates import currencies
|
||||||
|
|
||||||
from . import nostr_client, nostrmarket_ext, scheduled_tasks
|
from . import nostr_client, nostrmarket_ext, scheduled_tasks
|
||||||
from .crud import (
|
from .crud import (
|
||||||
|
create_customer,
|
||||||
create_direct_message,
|
create_direct_message,
|
||||||
create_merchant,
|
create_merchant,
|
||||||
create_product,
|
create_product,
|
||||||
|
|
@ -31,6 +33,7 @@ from .crud import (
|
||||||
delete_product,
|
delete_product,
|
||||||
delete_stall,
|
delete_stall,
|
||||||
delete_zone,
|
delete_zone,
|
||||||
|
get_customer,
|
||||||
get_customers,
|
get_customers,
|
||||||
get_direct_messages,
|
get_direct_messages,
|
||||||
get_merchant_by_pubkey,
|
get_merchant_by_pubkey,
|
||||||
|
|
@ -796,7 +799,7 @@ async def api_create_message(
|
||||||
######################################## CUSTOMERS ########################################
|
######################################## CUSTOMERS ########################################
|
||||||
|
|
||||||
|
|
||||||
@nostrmarket_ext.get("/api/v1/customers")
|
@nostrmarket_ext.get("/api/v1/customer")
|
||||||
async def api_get_customers(
|
async def api_get_customers(
|
||||||
wallet: WalletTypeInfo = Depends(get_key_type),
|
wallet: WalletTypeInfo = Depends(get_key_type),
|
||||||
) -> List[Customer]:
|
) -> List[Customer]:
|
||||||
|
|
@ -818,6 +821,41 @@ async def api_get_customers(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@nostrmarket_ext.post("/api/v1/customer")
|
||||||
|
async def api_createcustomer(
|
||||||
|
data: Customer,
|
||||||
|
wallet: WalletTypeInfo = Depends(require_admin_key),
|
||||||
|
) -> Customer:
|
||||||
|
|
||||||
|
try:
|
||||||
|
pubkey = normalize_public_key(data.public_key)
|
||||||
|
|
||||||
|
merchant = await get_merchant_for_user(wallet.wallet.user)
|
||||||
|
assert merchant, "A merchant does not exists for this user"
|
||||||
|
assert merchant.id == data.merchant_id, "Invalid merchant id for user"
|
||||||
|
|
||||||
|
existing_customer = await get_customer(merchant.id, pubkey)
|
||||||
|
assert existing_customer == None, "This public key already exists"
|
||||||
|
|
||||||
|
customer = await create_customer(
|
||||||
|
merchant.id, Customer(merchant_id=merchant.id, public_key=pubkey)
|
||||||
|
)
|
||||||
|
await nostr_client.subscribe_to_user_profile(pubkey, 0)
|
||||||
|
|
||||||
|
return customer
|
||||||
|
except (ValueError, AssertionError) as ex:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
|
detail=str(ex),
|
||||||
|
)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.warning(ex)
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Cannot create customer",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
######################################## OTHER ########################################
|
######################################## OTHER ########################################
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue