Fix subscription errors (#81)
* pref: merge filters in one * chore: load nit * feat: restore individual order
This commit is contained in:
parent
a3299b63c4
commit
889152a80b
8 changed files with 138 additions and 66 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
10
services.py
10
services.py
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
25
tasks.py
25
tasks.py
|
|
@ -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()
|
||||
|
|
|
|||
44
views_api.py
44
views_api.py
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue