Fix subscription errors (#81)

* pref: merge filters in one

* chore: load nit

* feat: restore individual order
This commit is contained in:
Vlad Stan 2023-09-12 15:03:37 +03:00 committed by GitHub
parent a3299b63c4
commit 889152a80b
8 changed files with 138 additions and 66 deletions

View file

@ -77,45 +77,56 @@ class NostrClient:
async def publish_nostr_event(self, e: NostrEvent):
await self.send_req_queue.put(["EVENT", e.dict()])
async def subscribe_to_direct_messages(self, public_key: str, since: int):
async def subscribe_merchant(self, public_key: str, since = 0):
dm_filters = self._filters_for_direct_messages(public_key, since)
stall_filters = self._filters_for_stall_events(public_key, since)
product_filters = self._filters_for_product_events(public_key, since)
profile_filters = self._filters_for_user_profile(public_key, since)
# merchant_filters = [json.dumps(f) for f in dm_filters + stall_filters + product_filters + profile_filters]
merchant_filters = dm_filters + stall_filters + product_filters + profile_filters
await self.send_req_queue.put(
["REQ", f"merchant:{public_key}"] + merchant_filters
)
logger.debug(f"Subscribed to events for: '{public_key}'.")
# print("### merchant_filters", merchant_filters)
def _filters_for_direct_messages(self, public_key: str, since: int) -> List:
in_messages_filter = {"kinds": [4], "#p": [public_key]}
out_messages_filter = {"kinds": [4], "authors": [public_key]}
if since and since != 0:
in_messages_filter["since"] = since
out_messages_filter["since"] = since
await self.send_req_queue.put(
["REQ", f"direct-messages-in:{public_key}", in_messages_filter]
)
await self.send_req_queue.put(
["REQ", f"direct-messages-out:{public_key}", out_messages_filter]
)
return [in_messages_filter, out_messages_filter]
logger.debug(f"Subscribed to direct-messages '{public_key}'.")
async def subscribe_to_stall_events(self, public_key: str, since: int):
def _filters_for_stall_events(self, public_key: str, since: int) -> List:
stall_filter = {"kinds": [30017], "authors": [public_key]}
if since and since != 0:
stall_filter["since"] = since
await self.send_req_queue.put(
["REQ", f"stall-events:{public_key}", stall_filter]
)
return [stall_filter]
logger.debug(f"Subscribed to stall-events: '{public_key}'.")
async def subscribe_to_product_events(self, public_key: str, since: int):
def _filters_for_product_events(self, public_key: str, since: int) -> List:
product_filter = {"kinds": [30018], "authors": [public_key]}
if since and since != 0:
product_filter["since"] = since
await self.send_req_queue.put(
["REQ", f"product-events:{public_key}", product_filter]
)
logger.debug(f"Subscribed to product-events: '{public_key}'.")
return [product_filter]
async def subscribe_to_user_profile(self, public_key: str, since: int):
def _filters_for_user_profile(self, public_key: str, since: int) -> List:
profile_filter = {"kinds": [0], "authors": [public_key]}
if since and since != 0:
profile_filter["since"] = since + 1
return [profile_filter]
def subscribe_to_user_profile(self, public_key: str, since: int) -> List:
profile_filter = {"kinds": [0], "authors": [public_key]}
if since and since != 0:
profile_filter["since"] = since + 1
@ -127,17 +138,6 @@ class NostrClient:
# ["REQ", f"user-profile-events:{public_key}", profile_filter]
# )
async def unsubscribe_from_direct_messages(self, public_key: str):
await self.send_req_queue.put(["CLOSE", f"direct-messages-in:{public_key}"])
await self.send_req_queue.put(["CLOSE", f"direct-messages-out:{public_key}"])
logger.debug(f"Unsubscribed from direct-messages '{public_key}'.")
async def unsubscribe_from_merchant_events(self, public_key: str):
await self.send_req_queue.put(["CLOSE", f"stall-events:{public_key}"])
await self.send_req_queue.put(["CLOSE", f"product-events:{public_key}"])
logger.debug(f"Unsubscribed from stall-events and product-events '{public_key}'.")
async def restart(self, public_keys: List[str]):
await self.unsubscribe_merchants(public_keys)
@ -160,11 +160,15 @@ class NostrClient:
self.ws.close()
self.ws = None
async def unsubscribe_merchant(self, public_key: str):
await self.send_req_queue.put(["CLOSE", public_key])
logger.debug(f"Unsubscribed from merchant events: '{public_key}'.")
async def unsubscribe_merchants(self, public_keys: List[str]):
for pk in public_keys:
try:
await self.unsubscribe_from_direct_messages(pk)
await self.unsubscribe_from_merchant_events(pk)
await self.unsubscribe_merchant(pk)
except Exception as ex:
logger.warning(ex)

View file

@ -16,6 +16,10 @@ from .crud import (
create_product,
create_stall,
get_customer,
get_last_direct_messages_created_at,
get_last_order_time,
get_last_product_update_time,
get_last_stall_update_time,
get_merchant_by_pubkey,
get_order,
get_order_by_event_id,
@ -364,6 +368,12 @@ async def extract_customer_order_from_dm(
return order
async def get_last_event_date_for_merchant(id) -> int:
last_order_time = await get_last_order_time(id)
last_dm_time = await get_last_direct_messages_created_at(id)
last_stall_update = await get_last_stall_update_time(id)
last_product_update = await get_last_product_update_time(id)
return max(last_order_time, last_dm_time, last_stall_update, last_product_update)
async def _handle_nip04_message(merchant_public_key: str, event: NostrEvent):
merchant = await get_merchant_by_pubkey(merchant_public_key)

View file

@ -57,7 +57,7 @@
<div>
<span v-text="dm.message.message"></span>
<q-badge color="orange">
<span v-text="dm.message.id" @click="showOrderDetails(dm.message.id)" class="cursor-pointer"></span>
<span v-text="dm.message.id" @click="showOrderDetails(dm.message.id, dm.event_id)" class="cursor-pointer"></span>
</q-badge>
</div>
<q-badge @click="showMessageRawData(index)" class="cursor-pointer">...</q-badge>

View file

@ -140,8 +140,8 @@ async function directMessages(path) {
}
this.getCustomersDebounced()
},
showOrderDetails: function (orderId) {
this.$emit('order-selected', orderId)
showOrderDetails: function (orderId, eventId) {
this.$emit('order-selected', { orderId, eventId })
},
showClientOrders: function () {
this.$emit('customer-selected', this.activePublicKey)

View file

@ -189,12 +189,33 @@ async function orderList(path) {
LNbits.utils.notifyApiError(error)
}
},
restoreOrder: async function (eventId) {
console.log('### restoreOrder', eventId)
try {
this.search.restoring = true
const {data} = await LNbits.api.request(
'PUT',
`/nostrmarket/api/v1/order/restore/${eventId}`,
this.adminkey
)
await this.getOrders()
this.$q.notify({
type: 'positive',
message: 'Order restored!'
})
return data
} catch (error) {
LNbits.utils.notifyApiError(error)
} finally {
this.search.restoring = false
}
},
restoreOrders: async function () {
try {
this.search.restoring = true
await LNbits.api.request(
'PUT',
`/nostrmarket/api/v1/order/restore`,
`/nostrmarket/api/v1/orders/restore`,
this.adminkey
)
await this.getOrders()
@ -269,9 +290,25 @@ async function orderList(path) {
}
},
orderSelected: async function (orderId) {
orderSelected: async function (orderId, eventId) {
const order = await this.getOrder(orderId)
if (!order) return
if (!order) {
LNbits.utils
.confirmDialog(
"Order could not be found. Do you want to restore it from this direct message?"
)
.onOk(async () => {
const restoredOrder = await this.restoreOrder(eventId)
console.log('### restoredOrder', restoredOrder)
if (restoredOrder) {
restoredOrder.expanded = true
restoredOrder.isNew = false
this.orders = [restoredOrder]
}
})
return
}
order.expanded = true
order.isNew = false
this.orders = [order]

View file

@ -152,8 +152,8 @@ const merchant = async () => {
filterOrdersForCustomer: function (customerPubkey) {
this.orderPubkey = customerPubkey
},
showOrderDetails: async function (orderId) {
await this.$refs.orderListRef.orderSelected(orderId)
showOrderDetails: async function (orderData) {
await this.$refs.orderListRef.orderSelected(orderData.orderId, orderData.eventId)
},
waitForNotifications: async function () {
if (!this.merchant) return

View file

@ -1,4 +1,5 @@
from asyncio import Queue
import asyncio
from lnbits.core.models import Payment
from lnbits.tasks import register_invoice_listener
@ -12,7 +13,7 @@ from .crud import (
get_merchants_ids_with_pubkeys,
)
from .nostr.nostr_client import NostrClient
from .services import handle_order_paid, process_nostr_message
from .services import get_last_event_date_for_merchant, handle_order_paid, process_nostr_message
async def wait_for_paid_invoices():
@ -39,23 +40,13 @@ async def on_invoice_paid(payment: Payment) -> None:
async def wait_for_nostr_events(nostr_client: NostrClient):
merchant_ids = await get_merchants_ids_with_pubkeys()
for id, pk in merchant_ids:
last_order_time = await get_last_order_time(id)
last_dm_time = await get_last_direct_messages_created_at(id)
since = max(last_order_time, last_dm_time)
since = await get_last_event_date_for_merchant(id)
await nostr_client.subscribe_merchant(pk, since + 1)
await asyncio.sleep(0.1) # try to avoid 'too many concurrent REQ' from relays
await nostr_client.subscribe_to_direct_messages(pk, since)
for id, pk in merchant_ids:
last_stall_update = await get_last_stall_update_time(id)
await nostr_client.subscribe_to_stall_events(pk, last_stall_update)
for id, pk in merchant_ids:
last_product_update = await get_last_product_update_time(id)
await nostr_client.subscribe_to_product_events(pk, last_product_update)
customers = await get_all_unique_customers()
for c in customers:
await nostr_client.subscribe_to_user_profile(c.public_key, c.event_created_at)
# customers = await get_all_unique_customers()
# for c in customers:
# await nostr_client.subscribe_to_user_profile(c.public_key, c.event_created_at)
while True:
message = await nostr_client.get_event()

View file

@ -35,12 +35,14 @@ from .crud import (
delete_zone,
get_customer,
get_customers,
get_direct_message_by_event_id,
get_direct_messages,
get_last_direct_messages_time,
get_merchant_by_pubkey,
get_merchant_for_user,
get_merchants_ids_with_pubkeys,
get_order,
get_order_by_event_id,
get_orders,
get_orders_for_stall,
get_orders_from_direct_messages,
@ -117,9 +119,7 @@ async def api_create_merchant(
),
)
await nostr_client.subscribe_to_stall_events(data.public_key, 0)
await nostr_client.subscribe_to_product_events(data.public_key, 0)
await nostr_client.subscribe_to_direct_messages(data.public_key, 0)
await nostr_client.subscribe_merchant(data.public_key, 0)
return merchant
except AssertionError as ex:
@ -170,14 +170,15 @@ async def api_delete_merchant(
assert merchant, "Merchant cannot be found"
assert merchant.id == merchant_id, "Wrong merchant ID"
# first unsubscribe so new events are not created during the clean-up
await nostr_client.unsubscribe_merchant(merchant.public_key)
await delete_merchant_orders(merchant.id)
await delete_merchant_products(merchant.id)
await delete_merchant_stalls(merchant.id)
await delete_merchant_direct_messages(merchant.id)
await delete_merchant_zones(merchant.id)
await nostr_client.unsubscribe_from_direct_messages(merchant.public_key)
await nostr_client.unsubscribe_from_merchant_events(merchant.public_key)
await delete_merchant(merchant.id)
except AssertionError as ex:
raise HTTPException(
@ -833,10 +834,39 @@ async def api_update_order_status(
)
@nostrmarket_ext.put("/api/v1/order/restore")
@nostrmarket_ext.put("/api/v1/order/restore/{event_id}")
async def api_restore_orders(
event_id: str,
wallet: WalletTypeInfo = Depends(require_admin_key),
) -> Order:
try:
merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found"
dm = await get_direct_message_by_event_id(merchant.id, event_id)
assert dm, "Canot find direct message"
await create_or_update_order_from_dm(merchant.id, merchant.public_key, dm)
return await get_order_by_event_id(merchant.id, event_id)
except 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 restore order",
)
@nostrmarket_ext.put("/api/v1/orders/restore")
async def api_restore_orders(
wallet: WalletTypeInfo = Depends(require_admin_key),
) -> None:
try:
merchant = await get_merchant_for_user(wallet.wallet.user)
assert merchant, "Merchant cannot be found"
@ -849,7 +879,7 @@ async def api_restore_orders(
)
except Exception as e:
logger.debug(
f"Failed to restore order friom event '{dm.event_id}': '{str(e)}'."
f"Failed to restore order from event '{dm.event_id}': '{str(e)}'."
)
except AssertionError as ex: