diff --git a/nostr/nostr_client.py b/nostr/nostr_client.py
index 175a1c6..ff02c93 100644
--- a/nostr/nostr_client.py
+++ b/nostr/nostr_client.py
@@ -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)
diff --git a/services.py b/services.py
index de619ad..a8cc54a 100644
--- a/services.py
+++ b/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)
diff --git a/static/components/direct-messages/direct-messages.html b/static/components/direct-messages/direct-messages.html
index fe66c12..e4d45af 100644
--- a/static/components/direct-messages/direct-messages.html
+++ b/static/components/direct-messages/direct-messages.html
@@ -57,7 +57,7 @@
-
+
...
diff --git a/static/components/direct-messages/direct-messages.js b/static/components/direct-messages/direct-messages.js
index 06cdca7..607342a 100644
--- a/static/components/direct-messages/direct-messages.js
+++ b/static/components/direct-messages/direct-messages.js
@@ -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)
diff --git a/static/components/order-list/order-list.js b/static/components/order-list/order-list.js
index 10681b5..285a077 100644
--- a/static/components/order-list/order-list.js
+++ b/static/components/order-list/order-list.js
@@ -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]
diff --git a/static/js/index.js b/static/js/index.js
index 6d2a6cb..72f1607 100644
--- a/static/js/index.js
+++ b/static/js/index.js
@@ -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
diff --git a/tasks.py b/tasks.py
index 12513b4..e02f781 100644
--- a/tasks.py
+++ b/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()
diff --git a/views_api.py b/views_api.py
index 26d295a..a1cde12 100644
--- a/views_api.py
+++ b/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: