From b2c580268e0ad915bbbb1d715126da655dfa6fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dni=20=E2=9A=A1?= Date: Fri, 17 Feb 2023 15:20:05 +0100 Subject: [PATCH] remove market (#1516) --- lnbits/extensions/market/README.md | 283 ---- lnbits/extensions/market/__init__.py | 43 - lnbits/extensions/market/config.json | 6 - lnbits/extensions/market/crud.py | 488 ------ lnbits/extensions/market/migrations.py | 156 -- lnbits/extensions/market/models.json | 227 --- lnbits/extensions/market/models.py | 135 -- lnbits/extensions/market/notifier.py | 91 -- .../market/static/images/bitcoin-shop.png | Bin 6034 -> 0 bytes .../market/static/images/placeholder.png | Bin 2840 -> 0 bytes lnbits/extensions/market/tasks.py | 39 - .../market/templates/market/_api_docs.html | 128 -- .../market/templates/market/_chat_box.html | 58 - .../market/templates/market/_dialogs.html | 405 ----- .../market/templates/market/_tables.html | 443 ----- .../market/templates/market/index.html | 1422 ----------------- .../market/templates/market/market.html | 175 -- .../market/templates/market/order.html | 564 ------- .../market/templates/market/product.html | 14 - .../market/templates/market/stall.html | 531 ------ lnbits/extensions/market/views.py | 155 -- lnbits/extensions/market/views_api.py | 527 ------ 22 files changed, 5890 deletions(-) delete mode 100644 lnbits/extensions/market/README.md delete mode 100644 lnbits/extensions/market/__init__.py delete mode 100644 lnbits/extensions/market/config.json delete mode 100644 lnbits/extensions/market/crud.py delete mode 100644 lnbits/extensions/market/migrations.py delete mode 100644 lnbits/extensions/market/models.json delete mode 100644 lnbits/extensions/market/models.py delete mode 100644 lnbits/extensions/market/notifier.py delete mode 100644 lnbits/extensions/market/static/images/bitcoin-shop.png delete mode 100644 lnbits/extensions/market/static/images/placeholder.png delete mode 100644 lnbits/extensions/market/tasks.py delete mode 100644 lnbits/extensions/market/templates/market/_api_docs.html delete mode 100644 lnbits/extensions/market/templates/market/_chat_box.html delete mode 100644 lnbits/extensions/market/templates/market/_dialogs.html delete mode 100644 lnbits/extensions/market/templates/market/_tables.html delete mode 100644 lnbits/extensions/market/templates/market/index.html delete mode 100644 lnbits/extensions/market/templates/market/market.html delete mode 100644 lnbits/extensions/market/templates/market/order.html delete mode 100644 lnbits/extensions/market/templates/market/product.html delete mode 100644 lnbits/extensions/market/templates/market/stall.html delete mode 100644 lnbits/extensions/market/views.py delete mode 100644 lnbits/extensions/market/views_api.py diff --git a/lnbits/extensions/market/README.md b/lnbits/extensions/market/README.md deleted file mode 100644 index bbd8fc635..000000000 --- a/lnbits/extensions/market/README.md +++ /dev/null @@ -1,283 +0,0 @@ -## Nostr Diagon Alley protocol (for resilient marketplaces) - -`authur: Ben Arc` - -#### Original protocol https://github.com/lnbits/Diagon-Alley - -> The concepts around resilience in Diagon Alley helped influence the creation of the NOSTR protocol, now we get to build Diagon Alley on NOSTR! - -In Diagon Alley, `merchant` and `customer` communicate via NOSTR relays, so loss of money, product information, and reputation become far less likely if attacked. - -A `merchant` and `customer` both have a NOSTR key-pair that are used to sign notes and subscribe to events. - -#### For further information about NOSTR, see https://github.com/nostr-protocol/nostr - - -## Terms - -* `merchant` - seller of products with NOSTR key-pair -* `customer` - buyer of products with NOSTR key-pair -* `product` - item for sale by the `merchant` -* `stall` - list of products controlled by `merchant` (a `merchant` can have multiple stalls) -* `marketplace` - clientside software for searching `stalls` and purchasing `products` - -## Diagon Alley Clients - -### Merchant admin - -Where the `merchant` creates, updates and deletes `stalls` and `products`, as well as where they manage sales, payments and communication with `customers`. - -The `merchant` admin software can be purely clientside, but for `convenience` and uptime, implementations will likely have a server listening for NOSTR events. - -### Marketplace - -`Marketplace` software should be entirely clientside, either as a stand-alone app, or as a purely frontend webpage. A `customer` subscribes to different merchant NOSTR public keys, and those `merchants` `stalls` and `products` become listed and searchable. The marketplace client is like any other ecommerce site, with basket and checkout. `Marketplaces` may also wish to include a `customer` support area for direct message communication with `merchants`. - -## `Merchant` publishing/updating products (event) - -NIP-01 https://github.com/nostr-protocol/nips/blob/master/01.md uses the basic NOSTR event type. - -The `merchant` event that publishes and updates product lists - -The below json goes in `content` of NIP-01. - -Data from newer events should replace data from older events. - -`action` types (used to indicate changes): -* `update` element has changed -* `delete` element should be deleted -* `suspend` element is suspended -* `unsuspend` element is unsuspended - - -``` -{ - "name": , - "description": , - "currency": , - "action": , - "shipping": [ - { - "id": , - "zones": , - "price": , - }, - { - "id": , - "zones": , - "price": , - }, - { - "id": , - "zones": , - "price": , - } - ], - "stalls": [ - { - "id": , - "name": , - "description": , - "categories": , - "shipping": , - "action": , - "products": [ - { - "id": , - "name": , - "description": , - "categories": , - "amount": , - "price": , - "images": [ - { - "id": , - "name": , - "link": - } - ], - "action": , - }, - { - "id": , - "name": , - "description": , - "categories": , - "amount": , - "price": , - "images": [ - { - "id": , - "name": , - "link": - }, - { - "id": , - "name": , - "link": - } - ], - "action": , - }, - ] - }, - { - "id": , - "name": , - "description": , - "categories": , - "shipping": , - "action": , - "products": [ - { - "id": , - "name": , - "categories": , - "amount": , - "price": , - "images": [ - { - "id": , - "name": , - "link": - } - ], - "action": , - } - ] - } - ] -} - -``` - -As all elements are optional, an `update` `action` to a `product` `image`, may look as simple as: - -``` -{ - "stalls": [ - { - "id": , - "products": [ - { - "id": , - "images": [ - { - "id": , - "name": , - "link": - } - ], - "action": , - }, - ] - } - ] -} - -``` - - -## Checkout events - -NIP-04 https://github.com/nostr-protocol/nips/blob/master/04.md, all checkout events are encrypted - -The below json goes in `content` of NIP-04. - -### Step 1: `customer` order (event) - - -``` -{ - "id": , - "name": , - "description": , - "address": , - "message": , - "contact": [ - "nostr": , - "phone": , - "email": - ], - "items": [ - { - "id": , - "quantity": , - "message": - }, - { - "id": , - "quantity": , - "message": - }, - { - "id": , - "quantity": , - "message": - } - -} - -``` - -Merchant should verify the sum of product ids + timestamp. - -### Step 2: `merchant` request payment (event) - -Sent back from the merchant for payment. Any payment option is valid that the merchant can check. - -The below json goes in `content` of NIP-04. - -`payment_options`/`type` include: -* `url` URL to a payment page, stripe, paypal, btcpayserver, etc -* `btc` onchain bitcoin address -* `ln` bitcoin lightning invoice -* `lnurl` bitcoin lnurl-pay - -``` -{ - "id": , - "message": , - "payment_options": [ - { - "type": , - "link": - }, - { - "type": , - "link": - }, - { - "type": , - "link": - } -} - -``` - -### Step 3: `merchant` verify payment/shipped (event) - -Once payment has been received and processed. - -The below json goes in `content` of NIP-04. - -``` -{ - "id": , - "message": , - "paid": , - "shipped": , -} - -``` - -## Customer support events - -Customer support is handle over whatever communication method was specified. If communicationg via nostr, NIP-04 is used https://github.com/nostr-protocol/nips/blob/master/04.md. - -## Additional - -Standard data models can be found here here - - - diff --git a/lnbits/extensions/market/__init__.py b/lnbits/extensions/market/__init__.py deleted file mode 100644 index a14fe6afc..000000000 --- a/lnbits/extensions/market/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -import asyncio - -from fastapi import APIRouter -from starlette.staticfiles import StaticFiles - -from lnbits.db import Database -from lnbits.helpers import template_renderer -from lnbits.tasks import catch_everything_and_restart - -db = Database("ext_market") - -market_ext: APIRouter = APIRouter(prefix="/market", tags=["market"]) - -market_static_files = [ - { - "path": "/market/static", - "app": StaticFiles(directory="lnbits/extensions/market/static"), - "name": "market_static", - } -] - -# if 'nostradmin' not in LNBITS_ADMIN_EXTENSIONS: -# @market_ext.get("/", response_class=HTMLResponse) -# async def index(request: Request): -# return template_renderer().TemplateResponse( -# "error.html", {"request": request, "err": "Ask system admin to enable NostrAdmin!"} -# ) -# else: - - -def market_renderer(): - return template_renderer(["lnbits/extensions/market/templates"]) - # return template_renderer(["lnbits/extensions/market/templates"]) - - -from .tasks import wait_for_paid_invoices -from .views import * # noqa: F401,F403 -from .views_api import * # noqa: F401,F403 - - -def market_start(): - loop = asyncio.get_event_loop() - loop.create_task(catch_everything_and_restart(wait_for_paid_invoices)) diff --git a/lnbits/extensions/market/config.json b/lnbits/extensions/market/config.json deleted file mode 100644 index 8a2948679..000000000 --- a/lnbits/extensions/market/config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Marketplace", - "short_description": "Webshop/market on LNbits", - "tile": "/market/static/images/bitcoin-shop.png", - "contributors": ["benarc", "talvasconcelos"] -} diff --git a/lnbits/extensions/market/crud.py b/lnbits/extensions/market/crud.py deleted file mode 100644 index c2a6ace12..000000000 --- a/lnbits/extensions/market/crud.py +++ /dev/null @@ -1,488 +0,0 @@ -from typing import List, Optional, Union - -# from lnbits.db import open_ext_db -from lnbits.db import SQLITE -from lnbits.helpers import urlsafe_short_hash - -from . import db -from .models import ( - ChatMessage, - CreateChatMessage, - CreateMarket, - Market, - MarketSettings, - OrderDetail, - Orders, - Products, - Stalls, - Zones, - createOrder, - createOrderDetails, - createProduct, - createStalls, - createZones, -) - -###Products - - -async def create_market_product(data: createProduct) -> Products: - product_id = urlsafe_short_hash() - await db.execute( - """ - INSERT INTO market.products (id, stall, product, categories, description, image, price, quantity) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - product_id, - data.stall, - data.product, - data.categories, - data.description, - data.image, - data.price, - data.quantity, - ), - ) - product = await get_market_product(product_id) - assert product, "Newly created product couldn't be retrieved" - return product - - -async def update_market_product(product_id: str, **kwargs) -> Optional[Products]: - q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) - - await db.execute( - f"UPDATE market.products SET {q} WHERE id = ?", - (*kwargs.values(), product_id), - ) - row = await db.fetchone("SELECT * FROM market.products WHERE id = ?", (product_id,)) - - return Products(**row) if row else None - - -async def get_market_product(product_id: str) -> Optional[Products]: - row = await db.fetchone("SELECT * FROM market.products WHERE id = ?", (product_id,)) - return Products(**row) if row else None - - -async def get_market_products(stall_ids: Union[str, List[str]]) -> List[Products]: - if isinstance(stall_ids, str): - stall_ids = [stall_ids] - - # with open_ext_db("market") as db: - q = ",".join(["?"] * len(stall_ids)) - rows = await db.fetchall( - f""" - SELECT * FROM market.products WHERE stall IN ({q}) - """, - (*stall_ids,), - ) - return [Products(**row) for row in rows] - - -async def delete_market_product(product_id: str) -> None: - await db.execute("DELETE FROM market.products WHERE id = ?", (product_id,)) - - -###zones - - -async def create_market_zone(user, data: createZones) -> Zones: - zone_id = urlsafe_short_hash() - await db.execute( - """ - INSERT INTO market.zones ( - id, - "user", - cost, - countries - - ) - VALUES (?, ?, ?, ?) - """, - (zone_id, user, data.cost, data.countries.lower()), - ) - - zone = await get_market_zone(zone_id) - assert zone, "Newly created zone couldn't be retrieved" - return zone - - -async def update_market_zone(zone_id: str, **kwargs) -> Optional[Zones]: - q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) - await db.execute( - f"UPDATE market.zones SET {q} WHERE id = ?", - (*kwargs.values(), zone_id), - ) - row = await db.fetchone("SELECT * FROM market.zones WHERE id = ?", (zone_id,)) - return Zones(**row) if row else None - - -async def get_market_zone(zone_id: str) -> Optional[Zones]: - row = await db.fetchone("SELECT * FROM market.zones WHERE id = ?", (zone_id,)) - return Zones(**row) if row else None - - -async def get_market_zones(user: str) -> List[Zones]: - rows = await db.fetchall('SELECT * FROM market.zones WHERE "user" = ?', (user,)) - return [Zones(**row) for row in rows] - - -async def delete_market_zone(zone_id: str) -> None: - await db.execute("DELETE FROM market.zones WHERE id = ?", (zone_id,)) - - -###Stalls - - -async def create_market_stall(data: createStalls) -> Stalls: - stall_id = urlsafe_short_hash() - await db.execute( - """ - INSERT INTO market.stalls ( - id, - wallet, - name, - currency, - publickey, - relays, - shippingzones - ) - VALUES (?, ?, ?, ?, ?, ?, ?) - """, - ( - stall_id, - data.wallet, - data.name, - data.currency, - data.publickey, - data.relays, - data.shippingzones, - ), - ) - - stall = await get_market_stall(stall_id) - assert stall, "Newly created stall couldn't be retrieved" - return stall - - -async def update_market_stall(stall_id: str, **kwargs) -> Optional[Stalls]: - q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) - await db.execute( - f"UPDATE market.stalls SET {q} WHERE id = ?", - (*kwargs.values(), stall_id), - ) - row = await db.fetchone("SELECT * FROM market.stalls WHERE id = ?", (stall_id,)) - return Stalls(**row) if row else None - - -async def get_market_stall(stall_id: str) -> Optional[Stalls]: - row = await db.fetchone("SELECT * FROM market.stalls WHERE id = ?", (stall_id,)) - return Stalls(**row) if row else None - - -async def get_market_stalls(wallet_ids: Union[str, List[str]]) -> List[Stalls]: - q = ",".join(["?"] * len(wallet_ids)) - rows = await db.fetchall( - f"SELECT * FROM market.stalls WHERE wallet IN ({q})", (*wallet_ids,) - ) - return [Stalls(**row) for row in rows] - - -async def get_market_stalls_by_ids(stall_ids: Union[str, List[str]]) -> List[Stalls]: - q = ",".join(["?"] * len(stall_ids)) - rows = await db.fetchall( - f"SELECT * FROM market.stalls WHERE id IN ({q})", (*stall_ids,) - ) - return [Stalls(**row) for row in rows] - - -async def delete_market_stall(stall_id: str) -> None: - await db.execute("DELETE FROM market.stalls WHERE id = ?", (stall_id,)) - - -###Orders - - -async def create_market_order(data: createOrder, invoiceid: str): - returning = "" if db.type == SQLITE else "RETURNING ID" - method = db.execute if db.type == SQLITE else db.fetchone - - result = await (method)( - f""" - INSERT INTO market.orders (wallet, shippingzone, address, email, total, invoiceid, paid, shipped) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - {returning} - """, - ( - data.wallet, - data.shippingzone, - data.address, - data.email, - data.total, - invoiceid, - False, - False, - ), - ) - if db.type == SQLITE: - return result._result_proxy.lastrowid - else: - return result[0] - - -async def create_market_order_details(order_id: str, data: List[createOrderDetails]): - for item in data: - item_id = urlsafe_short_hash() - await db.execute( - """ - INSERT INTO market.order_details (id, order_id, product_id, quantity) - VALUES (?, ?, ?, ?) - """, - ( - item_id, - order_id, - item.product_id, - item.quantity, - ), - ) - order_details = await get_market_order_details(order_id) - return order_details - - -async def get_market_order_details(order_id: str) -> List[OrderDetail]: - rows = await db.fetchall( - "SELECT * FROM market.order_details WHERE order_id = ?", (order_id,) - ) - - return [OrderDetail(**row) for row in rows] - - -async def get_market_order(order_id: str) -> Optional[Orders]: - row = await db.fetchone("SELECT * FROM market.orders WHERE id = ?", (order_id,)) - return Orders(**row) if row else None - - -async def get_market_order_invoiceid(invoice_id: str) -> Optional[Orders]: - row = await db.fetchone( - "SELECT * FROM market.orders WHERE invoiceid = ?", (invoice_id,) - ) - return Orders(**row) if row else None - - -async def set_market_order_paid(payment_hash: str): - await db.execute( - """ - UPDATE market.orders - SET paid = true - WHERE invoiceid = ? - """, - (payment_hash,), - ) - - -async def set_market_order_pubkey(payment_hash: str, pubkey: str): - await db.execute( - """ - UPDATE market.orders - SET pubkey = ? - WHERE invoiceid = ? - """, - ( - pubkey, - payment_hash, - ), - ) - - -async def update_market_product_stock(products): - - q = "\n".join( - [f"""WHEN id='{p.product_id}' THEN quantity - {p.quantity}""" for p in products] - ) - v = ",".join(["?"] * len(products)) - - await db.execute( - f""" - UPDATE market.products - SET quantity=(CASE - {q} - END) - WHERE id IN ({v}); - """, - (*[p.product_id for p in products],), - ) - - -async def get_market_orders(wallet_ids: Union[str, List[str]]) -> List[Orders]: - if isinstance(wallet_ids, str): - wallet_ids = [wallet_ids] - - q = ",".join(["?"] * len(wallet_ids)) - rows = await db.fetchall( - f"SELECT * FROM market.orders WHERE wallet IN ({q})", (*wallet_ids,) - ) - # - return [Orders(**row) for row in rows] - - -async def delete_market_order(order_id: str) -> None: - await db.execute("DELETE FROM market.orders WHERE id = ?", (order_id,)) - - -### Market/Marketplace - - -async def get_market_markets(user: str) -> List[Market]: - rows = await db.fetchall("SELECT * FROM market.markets WHERE usr = ?", (user,)) - return [Market(**row) for row in rows] - - -async def get_market_market(market_id: str) -> Optional[Market]: - row = await db.fetchone("SELECT * FROM market.markets WHERE id = ?", (market_id,)) - return Market(**row) if row else None - - -async def get_market_market_stalls(market_id: str): - rows = await db.fetchall( - "SELECT * FROM market.market_stalls WHERE marketid = ?", (market_id,) - ) - - ids = [row["stallid"] for row in rows] - - return await get_market_stalls_by_ids(ids) - - -async def create_market_market(data: CreateMarket): - market_id = urlsafe_short_hash() - - await db.execute( - """ - INSERT INTO market.markets (id, usr, name) - VALUES (?, ?, ?) - """, - ( - market_id, - data.usr, - data.name, - ), - ) - market = await get_market_market(market_id) - assert market, "Newly created market couldn't be retrieved" - return market - - -async def create_market_market_stalls(market_id: str, data: List[str]): - for stallid in data: - id = urlsafe_short_hash() - - await db.execute( - """ - INSERT INTO market.market_stalls (id, marketid, stallid) - VALUES (?, ?, ?) - """, - ( - id, - market_id, - stallid, - ), - ) - market_stalls = await get_market_market_stalls(market_id) - return market_stalls - - -async def update_market_market(market_id: str, name: str): - await db.execute( - "UPDATE market.markets SET name = ? WHERE id = ?", - (name, market_id), - ) - await db.execute( - "DELETE FROM market.market_stalls WHERE marketid = ?", - (market_id,), - ) - - market = await get_market_market(market_id) - return market - - -### CHAT / MESSAGES - - -async def create_chat_message(data: CreateChatMessage): - await db.execute( - """ - INSERT INTO market.messages (msg, pubkey, id_conversation) - VALUES (?, ?, ?) - """, - ( - data.msg, - data.pubkey, - data.room_name, - ), - ) - - -async def get_market_latest_chat_messages(room_name: str): - rows = await db.fetchall( - "SELECT * FROM market.messages WHERE id_conversation = ? ORDER BY timestamp DESC LIMIT 20", - (room_name,), - ) - - return [ChatMessage(**row) for row in rows] - - -async def get_market_chat_messages(room_name: str): - rows = await db.fetchall( - "SELECT * FROM market.messages WHERE id_conversation = ? ORDER BY timestamp DESC", - (room_name,), - ) - - return [ChatMessage(**row) for row in rows] - - -async def get_market_chat_by_merchant(ids: List[str]) -> List[ChatMessage]: - - q = ",".join(["?"] * len(ids)) - rows = await db.fetchall( - f"SELECT * FROM market.messages WHERE id_conversation IN ({q})", - (*ids,), - ) - return [ChatMessage(**row) for row in rows] - - -async def get_market_settings(user) -> Optional[MarketSettings]: - row = await db.fetchone( - """SELECT * FROM market.settings WHERE "user" = ?""", (user,) - ) - - return MarketSettings(**row) if row else None - - -async def create_market_settings(user: str, data): - await db.execute( - """ - INSERT INTO market.settings ("user", currency, fiat_base_multiplier) - VALUES (?, ?, ?) - """, - ( - user, - data.currency, - data.fiat_base_multiplier, - ), - ) - - -async def set_market_settings(user: str, data): - await db.execute( - """ - UPDATE market.settings - SET currency = ?, fiat_base_multiplier = ? - WHERE "user" = ?; - """, - ( - data.currency, - data.fiat_base_multiplier, - user, - ), - ) diff --git a/lnbits/extensions/market/migrations.py b/lnbits/extensions/market/migrations.py deleted file mode 100644 index 81c3e14f9..000000000 --- a/lnbits/extensions/market/migrations.py +++ /dev/null @@ -1,156 +0,0 @@ -async def m001_initial(db): - """ - Initial Market settings table. - """ - await db.execute( - """ - CREATE TABLE market.settings ( - "user" TEXT PRIMARY KEY, - currency TEXT DEFAULT 'sat', - fiat_base_multiplier INTEGER DEFAULT 1 - ); - """ - ) - - """ - Initial stalls table. - """ - await db.execute( - """ - CREATE TABLE market.stalls ( - id TEXT PRIMARY KEY, - wallet TEXT NOT NULL, - name TEXT NOT NULL, - currency TEXT, - publickey TEXT, - relays TEXT, - shippingzones TEXT NOT NULL, - rating INTEGER DEFAULT 0 - ); - """ - ) - - """ - Initial products table. - """ - await db.execute( - f""" - CREATE TABLE market.products ( - id TEXT PRIMARY KEY, - stall TEXT NOT NULL REFERENCES {db.references_schema}stalls (id) ON DELETE CASCADE, - product TEXT NOT NULL, - categories TEXT, - description TEXT, - image TEXT, - price INTEGER NOT NULL, - quantity INTEGER NOT NULL, - rating INTEGER DEFAULT 0 - ); - """ - ) - - """ - Initial zones table. - """ - await db.execute( - """ - CREATE TABLE market.zones ( - id TEXT PRIMARY KEY, - "user" TEXT NOT NULL, - cost TEXT NOT NULL, - countries TEXT NOT NULL - ); - """ - ) - - """ - Initial orders table. - """ - await db.execute( - f""" - CREATE TABLE market.orders ( - id {db.serial_primary_key}, - wallet TEXT NOT NULL, - username TEXT, - pubkey TEXT, - shippingzone TEXT NOT NULL, - address TEXT NOT NULL, - email TEXT NOT NULL, - total INTEGER NOT NULL, - invoiceid TEXT NOT NULL, - paid BOOLEAN NOT NULL, - shipped BOOLEAN NOT NULL, - time TIMESTAMP NOT NULL DEFAULT """ - + db.timestamp_now - + """ - ); - """ - ) - - """ - Initial order details table. - """ - await db.execute( - f""" - CREATE TABLE market.order_details ( - id TEXT PRIMARY KEY, - order_id INTEGER NOT NULL REFERENCES {db.references_schema}orders (id) ON DELETE CASCADE, - product_id TEXT NOT NULL REFERENCES {db.references_schema}products (id) ON DELETE CASCADE, - quantity INTEGER NOT NULL - ); - """ - ) - - """ - Initial market table. - """ - await db.execute( - """ - CREATE TABLE market.markets ( - id TEXT PRIMARY KEY, - usr TEXT NOT NULL, - name TEXT - ); - """ - ) - - """ - Initial market stalls table. - """ - await db.execute( - f""" - CREATE TABLE market.market_stalls ( - id TEXT PRIMARY KEY, - marketid TEXT NOT NULL REFERENCES {db.references_schema}markets (id) ON DELETE CASCADE, - stallid TEXT NOT NULL REFERENCES {db.references_schema}stalls (id) ON DELETE CASCADE - ); - """ - ) - - """ - Initial chat messages table. - """ - await db.execute( - f""" - CREATE TABLE market.messages ( - id {db.serial_primary_key}, - msg TEXT NOT NULL, - pubkey TEXT NOT NULL, - id_conversation TEXT NOT NULL, - timestamp TIMESTAMP NOT NULL DEFAULT """ - + db.timestamp_now - + """ - ); - """ - ) - - if db.type != "SQLITE": - """ - Create indexes for message fetching - """ - await db.execute( - "CREATE INDEX idx_messages_timestamp ON market.messages (timestamp DESC)" - ) - await db.execute( - "CREATE INDEX idx_messages_conversations ON market.messages (id_conversation)" - ) diff --git a/lnbits/extensions/market/models.json b/lnbits/extensions/market/models.json deleted file mode 100644 index 05bb4b11f..000000000 --- a/lnbits/extensions/market/models.json +++ /dev/null @@ -1,227 +0,0 @@ -{ - "shipping_zones": [ - "Free (digital)", - "Worldwide", - "Europe", - "Australia", - "Austria", - "Belgium", - "Brazil", - "Canada", - "Denmark", - "Finland", - "France", - "Germany", - "Greece", - "Hong Kong", - "Hungary", - "Ireland", - "Indonesia", - "Israel", - "Italy", - "Japan", - "Kazakhstan", - "Korea", - "Luxembourg", - "Malaysia", - "Mexico", - "Netherlands", - "New Zealand", - "Norway", - "Poland", - "Portugal", - "Russia", - "Saudi Arabia", - "Singapore", - "Spain", - "Sweden", - "Switzerland", - "Thailand", - "Turkey", - "Ukraine", - "United Kingdom**", - "United States***", - "Vietnam", - "China" - ], - "categories": [ - "Fashion (clothing and accessories)", - "Health (and beauty)", - "Toys (and baby equipment)", - "Media (Books and CDs)", - "Groceries (Food and Drink)", - "Technology (Phones and Computers)", - "Home (furniture and accessories)", - "Gifts (flowers, cards, etc)", - "Adult", - "Other" - ], - "currency": { - "BTC": "Bitcoin", - "SAT": "Bitcoin satoshis", - "AED": "United Arab Emirates Dirham", - "AFN": "Afghan Afghani", - "ALL": "Albanian Lek", - "AMD": "Armenian Dram", - "ANG": "Netherlands Antillean Gulden", - "AOA": "Angolan Kwanza", - "ARS": "Argentine Peso", - "AUD": "Australian Dollar", - "AWG": "Aruban Florin", - "AZN": "Azerbaijani Manat", - "BAM": "Bosnia and Herzegovina Convertible Mark", - "BBD": "Barbadian Dollar", - "BDT": "Bangladeshi Taka", - "BGN": "Bulgarian Lev", - "BHD": "Bahraini Dinar", - "BIF": "Burundian Franc", - "BMD": "Bermudian Dollar", - "BND": "Brunei Dollar", - "BOB": "Bolivian Boliviano", - "BRL": "Brazilian Real", - "BSD": "Bahamian Dollar", - "BTN": "Bhutanese Ngultrum", - "BWP": "Botswana Pula", - "BYN": "Belarusian Ruble", - "BYR": "Belarusian Ruble", - "BZD": "Belize Dollar", - "CAD": "Canadian Dollar", - "CDF": "Congolese Franc", - "CHF": "Swiss Franc", - "CLF": "Unidad de Fomento", - "CLP": "Chilean Peso", - "CNH": "Chinese Renminbi Yuan Offshore", - "CNY": "Chinese Renminbi Yuan", - "COP": "Colombian Peso", - "CRC": "Costa Rican Colón", - "CUC": "Cuban Convertible Peso", - "CVE": "Cape Verdean Escudo", - "CZK": "Czech Koruna", - "DJF": "Djiboutian Franc", - "DKK": "Danish Krone", - "DOP": "Dominican Peso", - "DZD": "Algerian Dinar", - "EGP": "Egyptian Pound", - "ERN": "Eritrean Nakfa", - "ETB": "Ethiopian Birr", - "EUR": "Euro", - "FJD": "Fijian Dollar", - "FKP": "Falkland Pound", - "GBP": "British Pound", - "GEL": "Georgian Lari", - "GGP": "Guernsey Pound", - "GHS": "Ghanaian Cedi", - "GIP": "Gibraltar Pound", - "GMD": "Gambian Dalasi", - "GNF": "Guinean Franc", - "GTQ": "Guatemalan Quetzal", - "GYD": "Guyanese Dollar", - "HKD": "Hong Kong Dollar", - "HNL": "Honduran Lempira", - "HRK": "Croatian Kuna", - "HTG": "Haitian Gourde", - "HUF": "Hungarian Forint", - "IDR": "Indonesian Rupiah", - "ILS": "Israeli New Sheqel", - "IMP": "Isle of Man Pound", - "INR": "Indian Rupee", - "IQD": "Iraqi Dinar", - "ISK": "Icelandic Króna", - "JEP": "Jersey Pound", - "JMD": "Jamaican Dollar", - "JOD": "Jordanian Dinar", - "JPY": "Japanese Yen", - "KES": "Kenyan Shilling", - "KGS": "Kyrgyzstani Som", - "KHR": "Cambodian Riel", - "KMF": "Comorian Franc", - "KRW": "South Korean Won", - "KWD": "Kuwaiti Dinar", - "KYD": "Cayman Islands Dollar", - "KZT": "Kazakhstani Tenge", - "LAK": "Lao Kip", - "LBP": "Lebanese Pound", - "LKR": "Sri Lankan Rupee", - "LRD": "Liberian Dollar", - "LSL": "Lesotho Loti", - "LYD": "Libyan Dinar", - "MAD": "Moroccan Dirham", - "MDL": "Moldovan Leu", - "MGA": "Malagasy Ariary", - "MKD": "Macedonian Denar", - "MMK": "Myanmar Kyat", - "MNT": "Mongolian Tögrög", - "MOP": "Macanese Pataca", - "MRO": "Mauritanian Ouguiya", - "MUR": "Mauritian Rupee", - "MVR": "Maldivian Rufiyaa", - "MWK": "Malawian Kwacha", - "MXN": "Mexican Peso", - "MYR": "Malaysian Ringgit", - "MZN": "Mozambican Metical", - "NAD": "Namibian Dollar", - "NGN": "Nigerian Naira", - "NIO": "Nicaraguan Córdoba", - "NOK": "Norwegian Krone", - "NPR": "Nepalese Rupee", - "NZD": "New Zealand Dollar", - "OMR": "Omani Rial", - "PAB": "Panamanian Balboa", - "PEN": "Peruvian Sol", - "PGK": "Papua New Guinean Kina", - "PHP": "Philippine Peso", - "PKR": "Pakistani Rupee", - "PLN": "Polish Złoty", - "PYG": "Paraguayan Guaraní", - "QAR": "Qatari Riyal", - "RON": "Romanian Leu", - "RSD": "Serbian Dinar", - "RUB": "Russian Ruble", - "RWF": "Rwandan Franc", - "SAR": "Saudi Riyal", - "SBD": "Solomon Islands Dollar", - "SCR": "Seychellois Rupee", - "SEK": "Swedish Krona", - "SGD": "Singapore Dollar", - "SHP": "Saint Helenian Pound", - "SLL": "Sierra Leonean Leone", - "SOS": "Somali Shilling", - "SRD": "Surinamese Dollar", - "SSP": "South Sudanese Pound", - "STD": "São Tomé and Príncipe Dobra", - "SVC": "Salvadoran Colón", - "SZL": "Swazi Lilangeni", - "THB": "Thai Baht", - "TJS": "Tajikistani Somoni", - "TMT": "Turkmenistani Manat", - "TND": "Tunisian Dinar", - "TOP": "Tongan Paʻanga", - "TRY": "Turkish Lira", - "TTD": "Trinidad and Tobago Dollar", - "TWD": "New Taiwan Dollar", - "TZS": "Tanzanian Shilling", - "UAH": "Ukrainian Hryvnia", - "UGX": "Ugandan Shilling", - "USD": "US Dollar", - "UYU": "Uruguayan Peso", - "UZS": "Uzbekistan Som", - "VEF": "Venezuelan Bolívar", - "VES": "Venezuelan Bolívar Soberano", - "VND": "Vietnamese Đồng", - "VUV": "Vanuatu Vatu", - "WST": "Samoan Tala", - "XAF": "Central African Cfa Franc", - "XAG": "Silver (Troy Ounce)", - "XAU": "Gold (Troy Ounce)", - "XCD": "East Caribbean Dollar", - "XDR": "Special Drawing Rights", - "XOF": "West African Cfa Franc", - "XPD": "Palladium", - "XPF": "Cfp Franc", - "XPT": "Platinum", - "YER": "Yemeni Rial", - "ZAR": "South African Rand", - "ZMW": "Zambian Kwacha", - "ZWL": "Zimbabwean Dollar" - } -} diff --git a/lnbits/extensions/market/models.py b/lnbits/extensions/market/models.py deleted file mode 100644 index ea7f6f205..000000000 --- a/lnbits/extensions/market/models.py +++ /dev/null @@ -1,135 +0,0 @@ -from typing import List, Optional - -from fastapi.param_functions import Query -from pydantic import BaseModel - - -class MarketSettings(BaseModel): - user: str - currency: str - fiat_base_multiplier: int - - -class SetSettings(BaseModel): - currency: str - fiat_base_multiplier: int = Query(100, ge=1) - - -class Stalls(BaseModel): - id: str - wallet: str - name: str - currency: str - publickey: Optional[str] - relays: Optional[str] - shippingzones: str - - -class createStalls(BaseModel): - wallet: str = Query(...) - name: str = Query(...) - currency: str = Query("sat") - publickey: str = Query(None) - relays: str = Query(None) - shippingzones: str = Query(...) - - -class createProduct(BaseModel): - stall: str = Query(...) - product: str = Query(...) - categories: str = Query(None) - description: str = Query(None) - image: str = Query(None) - price: float = Query(0, ge=0) - quantity: int = Query(0, ge=0) - - -class Products(BaseModel): - id: str - stall: str - product: str - categories: Optional[str] - description: Optional[str] - image: Optional[str] - price: float - quantity: int - - -class createZones(BaseModel): - cost: float = Query(0, ge=0) - countries: str = Query(...) - - -class Zones(BaseModel): - id: str - user: str - cost: float - countries: str - - -class OrderDetail(BaseModel): - id: str - order_id: str - product_id: str - quantity: int - - -class createOrderDetails(BaseModel): - product_id: str = Query(...) - quantity: int = Query(..., ge=1) - - -class createOrder(BaseModel): - wallet: str = Query(...) - username: str = Query(None) - pubkey: str = Query(None) - shippingzone: str = Query(...) - address: str = Query(...) - email: str = Query(...) - total: int = Query(...) - products: List[createOrderDetails] - - -class Orders(BaseModel): - id: str - wallet: str - username: Optional[str] - pubkey: Optional[str] - shippingzone: str - address: str - email: str - total: int - invoiceid: str - paid: bool - shipped: bool - time: int - - -class CreateMarket(BaseModel): - usr: str = Query(...) - name: str = Query(None) - stalls: List[str] = Query(...) - - -class Market(BaseModel): - id: str - usr: str - name: Optional[str] - - -class CreateMarketStalls(BaseModel): - stallid: str - - -class ChatMessage(BaseModel): - id: str - msg: str - pubkey: str - id_conversation: str - timestamp: int - - -class CreateChatMessage(BaseModel): - msg: str = Query(..., min_length=1) - pubkey: str = Query(...) - room_name: str = Query(...) diff --git a/lnbits/extensions/market/notifier.py b/lnbits/extensions/market/notifier.py deleted file mode 100644 index 4fe366f3e..000000000 --- a/lnbits/extensions/market/notifier.py +++ /dev/null @@ -1,91 +0,0 @@ -## adapted from https://github.com/Sentymental/chat-fastapi-websocket -""" -Create a class Notifier that will handle messages -and delivery to the specific person -""" - -import json -from collections import defaultdict -from typing import AsyncGenerator - -from fastapi import WebSocket -from loguru import logger - -from .crud import create_chat_message -from .models import CreateChatMessage - - -class Notifier: - """ - Manages chatrooms, sessions and members. - - Methods: - - get_notification_generator(self): async generator with notification messages - - get_members(self, room_name: str): get members in room - - push(message: str, room_name: str): push message - - connect(websocket: WebSocket, room_name: str): connect to room - - remove(websocket: WebSocket, room_name: str): remove - - _notify(message: str, room_name: str): notifier - """ - - def __init__(self): - # Create sessions as a dict: - self.sessions: dict = defaultdict(dict) - - # Create notification generator: - self.generator = self.get_notification_generator() - - async def get_notification_generator(self) -> AsyncGenerator: - """Notification Generator""" - - while True: - message = yield - msg = message["message"] - room_name = message["room_name"] - await self._notify(msg, room_name) - - def get_members(self, room_name: str): - """Get all members in a room""" - - try: - logger.info(f"Looking for members in room: {room_name}") - return self.sessions[room_name] - - except Exception: - logger.exception(f"There is no member in room: {room_name}") - return None - - async def push(self, message: str, room_name: str): - """Push a message""" - message_body = {"message": message, "room_name": room_name} - await self.generator.asend(message_body) - - async def connect(self, websocket: WebSocket, room_name: str): - """Connect to room""" - - await websocket.accept() - if self.sessions[room_name] == {} or len(self.sessions[room_name]) == 0: - self.sessions[room_name] = [] - - self.sessions[room_name].append(websocket) - print(f"Connections ...: {self.sessions[room_name]}") - - def remove(self, websocket: WebSocket, room_name: str): - """Remove websocket from room""" - - self.sessions[room_name].remove(websocket) - print(f"Connection removed...\nOpen connections...: {self.sessions[room_name]}") - - async def _notify(self, message: str, room_name: str): - """Notifier""" - d = json.loads(message) - d["room_name"] = room_name - db_msg = CreateChatMessage.parse_obj(d) - await create_chat_message(data=db_msg) - - remaining_sessions = [] - while len(self.sessions[room_name]) > 0: - websocket = self.sessions[room_name].pop() - await websocket.send_text(message) - remaining_sessions.append(websocket) - self.sessions[room_name] = remaining_sessions diff --git a/lnbits/extensions/market/static/images/bitcoin-shop.png b/lnbits/extensions/market/static/images/bitcoin-shop.png deleted file mode 100644 index debffbb28586737964b97aa788242ebf68ded85e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6034 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 F{|^ymw<7=m diff --git a/lnbits/extensions/market/tasks.py b/lnbits/extensions/market/tasks.py deleted file mode 100644 index b102e0f1c..000000000 --- a/lnbits/extensions/market/tasks.py +++ /dev/null @@ -1,39 +0,0 @@ -import asyncio - -from loguru import logger - -from lnbits.core.models import Payment -from lnbits.tasks import register_invoice_listener - -from .crud import ( - get_market_order_details, - get_market_order_invoiceid, - set_market_order_paid, - update_market_product_stock, -) - - -async def wait_for_paid_invoices(): - invoice_queue = asyncio.Queue() - register_invoice_listener(invoice_queue) - - while True: - payment = await invoice_queue.get() - await on_invoice_paid(payment) - - -async def on_invoice_paid(payment: Payment) -> None: - if payment.extra.get("tag") != "market": - return - - order = await get_market_order_invoiceid(payment.payment_hash) - if not order: - logger.error("this should never happen", payment) - return - - # set order as paid - await set_market_order_paid(payment.payment_hash) - - # deduct items sold from stock - details = await get_market_order_details(order.id) - await update_market_product_stock(details) diff --git a/lnbits/extensions/market/templates/market/_api_docs.html b/lnbits/extensions/market/templates/market/_api_docs.html deleted file mode 100644 index f0d97dbf1..000000000 --- a/lnbits/extensions/market/templates/market/_api_docs.html +++ /dev/null @@ -1,128 +0,0 @@ - - - -
- LNbits Market (Nostr support coming soon) -
- -
    -
  1. Create Shipping Zones you're willing to ship to
  2. -
  3. Create a Stall to list yiur products on
  4. -
  5. Create products to put on the Stall
  6. -
  7. Take orders
  8. -
  9. Includes chat support!
  10. -
- The first LNbits market idea 'Diagon Alley' helped create Nostr, and soon - this market extension will have the option to work on Nostr 'Diagon Alley' - mode, by the merchant, market, and buyer all having keys, and data being - routed through Nostr relays. -
- - Created by, - Tal Vasconcelos, - Ben Arc - -
-
-
- - - - - - GET - /market/api/v1/stall/products/<relay_id> -
Body (application/json)
-
- Returns 201 CREATED (application/json) -
- Product JSON list -
Curl example
- curl -X GET {{ request.url_root - }}api/v1/stall/products/<relay_id> -
-
-
- - - - POST - /market/api/v1/stall/order/<relay_id> -
Body (application/json)
- {"id": <string>, "address": <string>, "shippingzone": - <integer>, "email": <string>, "quantity": - <integer>} -
- Returns 201 CREATED (application/json) -
- {"checking_id": <string>,"payment_request": - <string>} -
Curl example
- curl -X POST {{ request.url_root - }}api/v1/stall/order/<relay_id> -d '{"id": <product_id&>, - "email": <customer_email>, "address": <customer_address>, - "quantity": 2, "shippingzone": 1}' -H "Content-type: application/json" - -
-
-
- - - - GET - /market/api/v1/stall/checkshipped/<checking_id> -
Headers
-
- Returns 200 OK (application/json) -
- {"shipped": <boolean>} -
Curl example
- curl -X GET {{ request.url_root - }}api/v1/stall/checkshipped/<checking_id> -H "Content-type: - application/json" -
-
-
-
diff --git a/lnbits/extensions/market/templates/market/_chat_box.html b/lnbits/extensions/market/templates/market/_chat_box.html deleted file mode 100644 index 05b0c58fe..000000000 --- a/lnbits/extensions/market/templates/market/_chat_box.html +++ /dev/null @@ -1,58 +0,0 @@ - - -
Messages
-
- - - - - - - -
-
- -
- -
-
- - - - - - - -
-
-
diff --git a/lnbits/extensions/market/templates/market/_dialogs.html b/lnbits/extensions/market/templates/market/_dialogs.html deleted file mode 100644 index a0ab84b3f..000000000 --- a/lnbits/extensions/market/templates/market/_dialogs.html +++ /dev/null @@ -1,405 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - -
- Update Product - - Create Product - - Cancel -
-
-
-
- - - - - - -
- Update Shipping Zone - Create Shipping Zone - - Cancel -
-
-
-
- - - - - - - -
- Update Marketplace - Launch Marketplace - - Cancel -
-
-
-
- - - - - - - - - - -
-
- Generate keys -
-
- Restore keys -
-
- - - - - -
- Update Stall - Create Stall - Cancel -
-
-
-
- - - -
How to use Market
- - - Create Shipping Zones you're willing to ship to. You can define - different values for different zones. - - - - - - Create a Stall and provide private and public keys to use for - communication. If you don't have one, LNbits will create a key pair for - you. It will be saved and can be used on other stalls. - - - - - - - Create your products, add a small description and an image. Choose to - what stall, if you have more than one, it belongs to - - - -
- -
-
-
-
-
diff --git a/lnbits/extensions/market/templates/market/_tables.html b/lnbits/extensions/market/templates/market/_tables.html deleted file mode 100644 index 280bb9f18..000000000 --- a/lnbits/extensions/market/templates/market/_tables.html +++ /dev/null @@ -1,443 +0,0 @@ - - - -
-
-
Orders
-
-
- Export to CSV -
-
- - {% raw %} - - - {% endraw %} - -
-
- - - - -
-
-
- Products - - - Add a product - -
-
-
- Export to CSV -
-
- - {% raw %} - - - {% endraw %} - -
-
- - - - -
-
-
Market Stalls
-
-
- Export to CSV -
-
- - {% raw %} - - - {% endraw %} - -
-
- - - - -
-
-
Marketplaces
-
-
- Export to CSV -
-
- - {% raw %} - - - {% endraw %} - -
-
- - - - -
-
-
Shipping Zones
-
-
- Export to CSV -
-
- - {% raw %} - - - {% endraw %} - -
-
diff --git a/lnbits/extensions/market/templates/market/index.html b/lnbits/extensions/market/templates/market/index.html deleted file mode 100644 index 0f8fa78bb..000000000 --- a/lnbits/extensions/market/templates/market/index.html +++ /dev/null @@ -1,1422 +0,0 @@ -{% extends "base.html" %} {% from "macros.jinja" import window_vars with context -%} {% block page %} -
- {% include "market/_dialogs.html" %} -
- - - + Shipping Zone Create a shipping zone - + Stall - - Create a market stall to list products on - - + Stall - - Create a market stall to list products on - - + Product List a product - + Product List a product - - Create Market - - Makes a simple frontend market for your stalls (not - NOSTR) - - - -
Market
-
Make a market of multiple stalls.
-
- - - - Coming soon... - Export all Data - - Export all data (markets, products, orders, etc...) - -
- - {% include "market/_tables.html" %} - - - -
-
-
Keys
-
-
- Export to CSV -
-
-
- -
-
-
- {% raw %} - - - {{ keys[type] }} - -

- {{ type == 'pubkey' ? 'Public Key' : 'Private Key' }}
Click to copy -

- {% endraw %} -
-
-
-
-
-
- -
- - -
- LNbits Market Extension (Nostr support coming soon) -
-
- - - {% include "market/_api_docs.html" %} - -
- - {% include "market/_chat_box.html" %} -
-
- -{% endblock %} {% block scripts %} {{ window_vars(user) }} - - - - - - -{% endblock %} diff --git a/lnbits/extensions/market/templates/market/market.html b/lnbits/extensions/market/templates/market/market.html deleted file mode 100644 index e59bb245a..000000000 --- a/lnbits/extensions/market/templates/market/market.html +++ /dev/null @@ -1,175 +0,0 @@ -{% extends "public.html" %} {% block page %} -
-
- -
- Market: {{ market.name }} -
-
- - - -
-
-
-
-
-
- - {% raw %} - - - -
-
- {{ item.product }} -
-
- - -
- - -
-
- {{ item.stallName }} -
- - {{ item.price }} satsBTC {{ (item.price / 1e8).toFixed(8) }} - - - {{ getAmountFormated(item.price, item.currency) }} - ({{ getValueInSats(item.price, item.currency) }} sats) - - {{item.quantity}} left -
-
- {{cat}} -
-
-

{{ item.description }}

-
-
- - - - - Stall: {{ item.stallName }} - - Visit Stall - - - {% endraw %} -
-
-
-{% endblock %} {% block scripts %} - -{% endblock %} diff --git a/lnbits/extensions/market/templates/market/order.html b/lnbits/extensions/market/templates/market/order.html deleted file mode 100644 index 5be606f91..000000000 --- a/lnbits/extensions/market/templates/market/order.html +++ /dev/null @@ -1,564 +0,0 @@ -{% extends "public.html" %} {% block page %} -
-
- -
-
- -
- -
-
- - - - - -
-
-
-
- - - {% raw %} -
{{ stall.name }}
-

- Public Key: {{ sliceKey(stall.publickey) }} - Click to copy -

- {% endraw %} -
- - - - - - - - - {% raw %} - - - {{p.quantity}} x - - - - - - - - {{ p.name }} - - - - {{ getAmountFormated(p.price) }} - {{p.price}} sats - - - {% endraw %} - - - - - -

- Bellow are the keys needed to contact the merchant. They are - stored in the browser! -

-
-
-
- {% raw %} - - - {{ user.keys[type] }} - -

- {{ type == 'publickey' ? 'Public Key' : 'Private Key' }} -

- {% endraw %} -
-
-
- -
- Backup keys - Download your keys - - Restore keys - Restore keys - - Delete data - Delete all data from browser - -
-
-
- -

Export, or send, this page to another device

-
- - - Click to copy - -
-
- Copy URL - Export, or send, this page to another device - -
-
-
-
-
- - - - - - - -
- - -
-
-
-
- - - -
Bookmark this page
-

- Don't forget to bookmark this page to be able to check on your order! -

-

- You can backup your keys, and export the page to another device also. -

-
- Close -
-
-
-
-{% endblock %} {% block scripts %} - - - - -{% endblock %} diff --git a/lnbits/extensions/market/templates/market/product.html b/lnbits/extensions/market/templates/market/product.html deleted file mode 100644 index 66f566914..000000000 --- a/lnbits/extensions/market/templates/market/product.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "public.html" %} {% block page %} -

Product page

-{% endblock %} {% block scripts %} - -{% endblock %} diff --git a/lnbits/extensions/market/templates/market/stall.html b/lnbits/extensions/market/templates/market/stall.html deleted file mode 100644 index f9189b30d..000000000 --- a/lnbits/extensions/market/templates/market/stall.html +++ /dev/null @@ -1,531 +0,0 @@ -{% extends "public.html" %} {% block page %} -
-
- -
- Stall: {{ stall.name }} -
-
- - - -
- - {% raw %} - - {{ cart.size }} - - {% endraw %} - - - {% raw %} - - - {{p.quantity}} x - - - - - - - - - {{ p.name }} - - - - - {{unit != 'sat' ? getAmountFormated(p.price) : p.price + - 'sats'}} - - - - - {% endraw %} - - -
- - -
-
-
-
-
-
-
-
- - {% raw %} - - - - Add to cart - -
-
- {{ item.product }} -
-
- - -
- - -
- - {{ item.price }} satsBTC {{ (item.price / 1e8).toFixed(8) }} - - - {{ getAmountFormated(item.price) }} - ({{ getValueInSats(item.price) }} sats) - - {{item.quantity}} left -
-
- {{cat}} -
-
-

{{ item.description }}

-
-
- - - {% endraw %} -
-
- - - - - - - - - - - -

Select the shipping zone:

-
- -
-
- {% raw %} Total: {{ unit != 'sat' ? getAmountFormated(finalCost) : - finalCost + 'sats' }} - ({{ getValueInSats(finalCost) }} sats) - {% endraw %} -
-
- Checkout - Cancel -
-
-
-
- - - - - - -
- Copy invoice - Close -
-
-
-
-{% endblock %} {% block scripts %} - -{% endblock %} diff --git a/lnbits/extensions/market/views.py b/lnbits/extensions/market/views.py deleted file mode 100644 index f9e7131bc..000000000 --- a/lnbits/extensions/market/views.py +++ /dev/null @@ -1,155 +0,0 @@ -from http import HTTPStatus - -from fastapi import Depends, Query, Request, WebSocket, WebSocketDisconnect -from fastapi.templating import Jinja2Templates -from loguru import logger -from starlette.exceptions import HTTPException -from starlette.responses import HTMLResponse - -from lnbits.core.models import User -from lnbits.decorators import check_user_exists - -from . import market_ext, market_renderer -from .crud import ( - create_market_settings, - get_market_market, - get_market_market_stalls, - get_market_order_details, - get_market_order_invoiceid, - get_market_products, - get_market_settings, - get_market_stall, - get_market_zone, -) -from .models import SetSettings -from .notifier import Notifier - -templates = Jinja2Templates(directory="templates") - - -@market_ext.get("/", response_class=HTMLResponse) -async def index(request: Request, user: User = Depends(check_user_exists)): - settings = await get_market_settings(user=user.id) - - if not settings: - await create_market_settings( - user=user.id, data=SetSettings(currency="sat", fiat_base_multiplier=1) - ) - settings = await get_market_settings(user.id) - assert settings - return market_renderer().TemplateResponse( - "market/index.html", - {"request": request, "user": user.dict(), "currency": settings.currency}, - ) - - -@market_ext.get("/stalls/{stall_id}", response_class=HTMLResponse) -async def stall(request: Request, stall_id): - stall = await get_market_stall(stall_id) - products = await get_market_products(stall_id) - - if not stall: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Stall does not exist." - ) - - zones = [] - for id in stall.shippingzones.split(","): - zone = await get_market_zone(id) - assert zone - z = zone.dict() - zones.append({"label": z["countries"], "cost": z["cost"], "value": z["id"]}) - - _stall = stall.dict() - - _stall["zones"] = zones - - return market_renderer().TemplateResponse( - "market/stall.html", - { - "request": request, - "stall": _stall, - "products": [product.dict() for product in products], - }, - ) - - -@market_ext.get("/market/{market_id}", response_class=HTMLResponse) -async def market(request: Request, market_id): - market = await get_market_market(market_id) - - if not market: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Marketplace doesn't exist." - ) - - stalls = await get_market_market_stalls(market_id) - stalls_ids = [stall.id for stall in stalls] - products = [product.dict() for product in await get_market_products(stalls_ids)] - - return market_renderer().TemplateResponse( - "market/market.html", - { - "request": request, - "market": market, - "stalls": [stall.dict() for stall in stalls], - "products": products, - }, - ) - - -@market_ext.get("/order", response_class=HTMLResponse) -async def order_chat( - request: Request, - merch: str = Query(...), - invoice_id: str = Query(...), - keys: str = Query(None), -): - stall = await get_market_stall(merch) - assert stall - order = await get_market_order_invoiceid(invoice_id) - assert order - _order = await get_market_order_details(order.id) - products = await get_market_products(stall.id) - assert products - - return market_renderer().TemplateResponse( - "market/order.html", - { - "request": request, - "stall": { - "id": stall.id, - "name": stall.name, - "publickey": stall.publickey, - "wallet": stall.wallet, - "currency": stall.currency, - }, - "publickey": keys.split(",")[0] if keys else None, - "privatekey": keys.split(",")[1] if keys else None, - "order_id": order.invoiceid, - "order": [details.dict() for details in _order], - "products": [product.dict() for product in products], - }, - ) - - -##################WEBSOCKET ROUTES######################## - -# Initialize Notifier: -notifier = Notifier() - - -@market_ext.websocket("/ws/{room_name}") -async def websocket_endpoint(websocket: WebSocket, room_name: str): - await notifier.connect(websocket, room_name) - try: - while True: - data = await websocket.receive_text() - room_members = notifier.get_members(room_name) or [] - if websocket not in room_members: - logger.warning("Sender not in room member: Reconnecting...") - await notifier.connect(websocket, room_name) - await notifier._notify(data, room_name) - - except WebSocketDisconnect: - notifier.remove(websocket, room_name) diff --git a/lnbits/extensions/market/views_api.py b/lnbits/extensions/market/views_api.py deleted file mode 100644 index 221d51bbf..000000000 --- a/lnbits/extensions/market/views_api.py +++ /dev/null @@ -1,527 +0,0 @@ -from http import HTTPStatus -from typing import Optional - -from fastapi import Depends, Query -from loguru import logger -from starlette.exceptions import HTTPException - -from lnbits.core.crud import get_user -from lnbits.core.services import create_invoice -from lnbits.core.views.api import api_payment -from lnbits.decorators import ( - WalletTypeInfo, - get_key_type, - require_admin_key, - require_invoice_key, -) -from lnbits.helpers import urlsafe_short_hash -from lnbits.utils.exchange_rates import currencies - -from . import db, market_ext -from .crud import ( - create_market_market, - create_market_market_stalls, - create_market_order, - create_market_order_details, - create_market_product, - create_market_settings, - create_market_stall, - create_market_zone, - delete_market_order, - delete_market_product, - delete_market_stall, - delete_market_zone, - get_market_chat_by_merchant, - get_market_chat_messages, - get_market_latest_chat_messages, - get_market_market, - get_market_market_stalls, - get_market_markets, - get_market_order, - get_market_order_details, - get_market_order_invoiceid, - get_market_orders, - get_market_product, - get_market_products, - get_market_settings, - get_market_stall, - get_market_stalls, - get_market_zone, - get_market_zones, - set_market_order_pubkey, - set_market_settings, - update_market_market, - update_market_product, - update_market_stall, - update_market_zone, -) -from .models import ( - CreateMarket, - SetSettings, - createOrder, - createProduct, - createStalls, - createZones, -) - -# from lnbits.db import open_ext_db - - -### Products -@market_ext.get("/api/v1/products") -async def api_market_products( - wallet: WalletTypeInfo = Depends(require_invoice_key), - all_stalls: bool = Query(False), -): - wallet_ids = [wallet.wallet.id] - - if all_stalls: - user = await get_user(wallet.wallet.user) - wallet_ids = user.wallet_ids if user else [] - - stalls = [stall.id for stall in await get_market_stalls(wallet_ids)] - - if not stalls: - return - - return [product.dict() for product in await get_market_products(stalls)] - - -@market_ext.post("/api/v1/products") -@market_ext.put("/api/v1/products/{product_id}") -async def api_market_product_create( - data: createProduct, - product_id=None, - wallet: WalletTypeInfo = Depends(require_invoice_key), -): - # For fiat currencies, - # we multiply by data.fiat_base_multiplier (usually 100) to save the value in cents. - settings = await get_market_settings(user=wallet.wallet.user) - assert settings - - stall = await get_market_stall(stall_id=data.stall) - assert stall - - if stall.currency != "sat": - data.price *= settings.fiat_base_multiplier - - if data.image: - image_is_url = data.image.startswith("https://") or data.image.startswith( - "http://" - ) - - if not image_is_url: - - def size(b64string): - return int((len(b64string) * 3) / 4 - b64string.count("=", -2)) - - image_size = size(data.image) / 1024 - if image_size > 100: - raise HTTPException( - status_code=HTTPStatus.BAD_REQUEST, - detail=f"Image size is too big, {int(image_size)}Kb. Max: 100kb, Compress the image at https://tinypng.com, or use an URL.", - ) - - if product_id: - product = await get_market_product(product_id) - if not product: - return {"message": "Product does not exist."} - - # stall = await get_market_stall(stall_id=product.stall) - if stall.wallet != wallet.wallet.id: - return {"message": "Not your product."} - - product = await update_market_product(product_id, **data.dict()) - else: - product = await create_market_product(data=data) - assert product - return product.dict() - - -@market_ext.delete("/api/v1/products/{product_id}") -async def api_market_products_delete( - product_id, wallet: WalletTypeInfo = Depends(require_admin_key) -): - product = await get_market_product(product_id) - - if not product: - return {"message": "Product does not exist."} - - stall = await get_market_stall(product.stall) - assert stall - - if stall.wallet != wallet.wallet.id: - return {"message": "Not your Market."} - - await delete_market_product(product_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) - - -# # # Shippingzones - - -@market_ext.get("/api/v1/zones") -async def api_market_zones(wallet: WalletTypeInfo = Depends(get_key_type)): - - return await get_market_zones(wallet.wallet.user) - - -@market_ext.post("/api/v1/zones") -async def api_market_zone_create( - data: createZones, wallet: WalletTypeInfo = Depends(get_key_type) -): - zone = await create_market_zone(user=wallet.wallet.user, data=data) - return zone.dict() - - -@market_ext.post("/api/v1/zones/{zone_id}") -async def api_market_zone_update( - data: createZones, - zone_id: str, - wallet: WalletTypeInfo = Depends(require_admin_key), -): - zone = await get_market_zone(zone_id) - if not zone: - return {"message": "Zone does not exist."} - if zone.user != wallet.wallet.user: - return {"message": "Not your record."} - zone = await update_market_zone(zone_id, **data.dict()) - return zone - - -@market_ext.delete("/api/v1/zones/{zone_id}") -async def api_market_zone_delete( - zone_id, wallet: WalletTypeInfo = Depends(require_admin_key) -): - zone = await get_market_zone(zone_id) - - if not zone: - return {"message": "zone does not exist."} - - if zone.user != wallet.wallet.user: - return {"message": "Not your zone."} - - await delete_market_zone(zone_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) - - -# # # Stalls - - -@market_ext.get("/api/v1/stalls") -async def api_market_stalls( - wallet: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False) -): - wallet_ids = [wallet.wallet.id] - - if all_wallets: - user = await get_user(wallet.wallet.user) - wallet_ids = user.wallet_ids if user else [] - - return [stall.dict() for stall in await get_market_stalls(wallet_ids)] - - -@market_ext.post("/api/v1/stalls") -@market_ext.put("/api/v1/stalls/{stall_id}") -async def api_market_stall_create( - data: createStalls, - stall_id: Optional[str] = None, - wallet: WalletTypeInfo = Depends(require_invoice_key), -): - - if stall_id: - stall = await get_market_stall(stall_id) - if not stall: - return {"message": "Withdraw stall does not exist."} - - if stall.wallet != wallet.wallet.id: - return {"message": "Not your withdraw stall."} - - stall = await update_market_stall(stall_id, **data.dict()) - else: - stall = await create_market_stall(data=data) - assert stall - return stall.dict() - - -@market_ext.delete("/api/v1/stalls/{stall_id}") -async def api_market_stall_delete( - stall_id: str, wallet: WalletTypeInfo = Depends(require_admin_key) -): - stall = await get_market_stall(stall_id) - - if not stall: - return {"message": "Stall does not exist."} - - if stall.wallet != wallet.wallet.id: - return {"message": "Not your Stall."} - - await delete_market_stall(stall_id) - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) - - -###Orders - - -@market_ext.get("/api/v1/orders") -async def api_market_orders( - wallet: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False) -): - wallet_ids = [wallet.wallet.id] - if all_wallets: - user = await get_user(wallet.wallet.user) - wallet_ids = user.wallet_ids if user else [] - - orders = await get_market_orders(wallet_ids) - if not orders: - return - orders_with_details = [] - for order in orders: - _order = order.dict() - _order["details"] = await get_market_order_details(_order["id"]) - orders_with_details.append(_order) - try: - return orders_with_details # [order for order in orders] - # return [order.dict() for order in await get_market_orders(wallet_ids)] - except: - return {"message": "We could not retrieve the orders."} - - -@market_ext.get("/api/v1/orders/{order_id}") -async def api_market_order_by_id(order_id: str): - order = await get_market_order(order_id) - assert order - _order = order.dict() - _order["details"] = await get_market_order_details(order_id) - - return _order - - -@market_ext.post("/api/v1/orders") -async def api_market_order_create(data: createOrder): - ref = urlsafe_short_hash() - - payment_hash, payment_request = await create_invoice( - wallet_id=data.wallet, - amount=data.total, - memo="New order on Market", - extra={ - "tag": "market", - "reference": ref, - }, - ) - order_id = await create_market_order(invoiceid=payment_hash, data=data) - logger.debug(f"ORDER ID {order_id}") - logger.debug(f"PRODUCTS {data.products}") - await create_market_order_details(order_id=order_id, data=data.products) - return { - "payment_hash": payment_hash, - "payment_request": payment_request, - "order_reference": ref, - } - - -@market_ext.get("/api/v1/orders/payments/{payment_hash}") -async def api_market_check_payment(payment_hash: str): - order = await get_market_order_invoiceid(payment_hash) - if not order: - raise HTTPException( - status_code=HTTPStatus.NOT_FOUND, detail="Order does not exist." - ) - try: - status = await api_payment(payment_hash) - - except Exception as exc: - logger.error(exc) - return {"paid": False} - return status - - -@market_ext.delete("/api/v1/orders/{order_id}") -async def api_market_order_delete( - order_id: str, wallet: WalletTypeInfo = Depends(require_admin_key) -): - order = await get_market_order(order_id) - - if not order: - return {"message": "Order does not exist."} - - if order.wallet != wallet.wallet.id: - return {"message": "Not your Order."} - - await delete_market_order(order_id) - - raise HTTPException(status_code=HTTPStatus.NO_CONTENT) - - -# @market_ext.get("/api/v1/orders/paid/{order_id}") -# async def api_market_order_paid( -# order_id, wallet: WalletTypeInfo = Depends(require_admin_key) -# ): -# await db.execute( -# "UPDATE market.orders SET paid = ? WHERE id = ?", -# ( -# True, -# order_id, -# ), -# ) -# return "", HTTPStatus.OK - - -@market_ext.get("/api/v1/order/pubkey/{payment_hash}/{pubkey}") -async def api_market_order_pubkey(payment_hash: str, pubkey: str): - await set_market_order_pubkey(payment_hash, pubkey) - return "", HTTPStatus.OK - - -@market_ext.get("/api/v1/orders/shipped/{order_id}") -async def api_market_order_shipped( - order_id, shipped: bool = Query(...), wallet: WalletTypeInfo = Depends(get_key_type) -): - await db.execute( - "UPDATE market.orders SET shipped = ? WHERE id = ?", - ( - shipped, - order_id, - ), - ) - order = await db.fetchone("SELECT * FROM market.orders WHERE id = ?", (order_id,)) - - return order - - -###List products based on stall id - - -# @market_ext.get("/api/v1/stall/products/{stall_id}") -# async def api_market_stall_products( -# stall_id, wallet: WalletTypeInfo = Depends(get_key_type) -# ): - -# rows = await db.fetchone("SELECT * FROM market.stalls WHERE id = ?", (stall_id,)) -# if not rows: -# return {"message": "Stall does not exist."} - -# products = db.fetchone("SELECT * FROM market.products WHERE wallet = ?", (rows[1],)) -# if not products: -# return {"message": "No products"} - -# return [products.dict() for products in await get_market_products(rows[1])] - - -###Check a product has been shipped - - -# @market_ext.get("/api/v1/stall/checkshipped/{checking_id}") -# async def api_market_stall_checkshipped( -# checking_id, wallet: WalletTypeInfo = Depends(get_key_type) -# ): -# rows = await db.fetchone( -# "SELECT * FROM market.orders WHERE invoiceid = ?", (checking_id,) -# ) -# return {"shipped": rows["shipped"]} - - -## -# MARKETS -## - - -@market_ext.get("/api/v1/markets") -async def api_market_markets(wallet: WalletTypeInfo = Depends(get_key_type)): - # await get_market_market_stalls(market_id="FzpWnMyHQMcRppiGVua4eY") - try: - return [ - market.dict() for market in await get_market_markets(wallet.wallet.user) - ] - except: - return {"message": "We could not retrieve the markets."} - - -@market_ext.get("/api/v1/markets/{market_id}/stalls") -async def api_market_market_stalls(market_id: str): - stall_ids = await get_market_market_stalls(market_id) - return stall_ids - - -@market_ext.post("/api/v1/markets") -@market_ext.put("/api/v1/markets/{market_id}") -async def api_market_market_create( - data: CreateMarket, - market_id: Optional[str] = None, - wallet: WalletTypeInfo = Depends(require_invoice_key), -): - if market_id: - market = await get_market_market(market_id) - if not market: - return {"message": "Market does not exist."} - - if market.usr != wallet.wallet.user: - return {"message": "Not your market."} - - market = await update_market_market(market_id, data.name) - else: - market = await create_market_market(data=data) - - assert market - await create_market_market_stalls(market_id=market.id, data=data.stalls) - - return market.dict() - - -## MESSAGES/CHAT - - -@market_ext.get("/api/v1/chat/messages/merchant") -async def api_get_merchant_messages( - orders: str = Query(...), wallet: WalletTypeInfo = Depends(require_admin_key) -): - return [msg.dict() for msg in await get_market_chat_by_merchant(orders.split(","))] - - -@market_ext.get("/api/v1/chat/messages/{room_name}") -async def api_get_latest_chat_msg(room_name: str, all_messages: bool = Query(False)): - if all_messages: - messages = await get_market_chat_messages(room_name) - else: - messages = await get_market_latest_chat_messages(room_name) - - return messages - - -@market_ext.get("/api/v1/currencies") -async def api_list_currencies_available(): - return list(currencies.keys()) - - -@market_ext.get("/api/v1/settings") -async def api_get_settings(wallet: WalletTypeInfo = Depends(require_admin_key)): - user = wallet.wallet.user - - settings = await get_market_settings(user) - - return settings - - -@market_ext.post("/api/v1/settings") -@market_ext.put("/api/v1/settings/{usr}") -async def api_set_settings( - data: SetSettings, - usr: Optional[str] = None, - wallet: WalletTypeInfo = Depends(require_admin_key), -): - if usr: - if usr != wallet.wallet.user: - return {"message": "Not your Market."} - - settings = await get_market_settings(user=usr) - assert settings - - if settings.user != wallet.wallet.user: - return {"message": "Not your Market."} - - return await set_market_settings(usr, data) - - user = wallet.wallet.user - - return await create_market_settings(user, data)