From 6b2f3d05d51d7aabe8a00dac10f9dfc3d96134a9 Mon Sep 17 00:00:00 2001
From: benarc
Date: Thu, 27 Jan 2022 12:24:38 +0000
Subject: [PATCH 01/30] initial
---
lnbits/extensions/diagonalley/README.md | 9 +
lnbits/extensions/diagonalley/__init__.py | 16 +
lnbits/extensions/diagonalley/config.json | 6 +
lnbits/extensions/diagonalley/crud.py | 395 +++++++++
lnbits/extensions/diagonalley/migrations.py | 69 ++
lnbits/extensions/diagonalley/models.py | 57 ++
.../extensions/diagonalley/static/js/index.js | 824 ++++++++++++++++++
lnbits/extensions/diagonalley/tasks.py | 29 +
.../templates/diagonalley/_api_docs.html | 129 +++
.../templates/diagonalley/index.html | 634 ++++++++++++++
.../templates/diagonalley/stall.html | 9 +
lnbits/extensions/diagonalley/views.py | 44 +
lnbits/extensions/diagonalley/views_api.py | 348 ++++++++
13 files changed, 2569 insertions(+)
create mode 100644 lnbits/extensions/diagonalley/README.md
create mode 100644 lnbits/extensions/diagonalley/__init__.py
create mode 100644 lnbits/extensions/diagonalley/config.json
create mode 100644 lnbits/extensions/diagonalley/crud.py
create mode 100644 lnbits/extensions/diagonalley/migrations.py
create mode 100644 lnbits/extensions/diagonalley/models.py
create mode 100644 lnbits/extensions/diagonalley/static/js/index.js
create mode 100644 lnbits/extensions/diagonalley/tasks.py
create mode 100644 lnbits/extensions/diagonalley/templates/diagonalley/_api_docs.html
create mode 100644 lnbits/extensions/diagonalley/templates/diagonalley/index.html
create mode 100644 lnbits/extensions/diagonalley/templates/diagonalley/stall.html
create mode 100644 lnbits/extensions/diagonalley/views.py
create mode 100644 lnbits/extensions/diagonalley/views_api.py
diff --git a/lnbits/extensions/diagonalley/README.md b/lnbits/extensions/diagonalley/README.md
new file mode 100644
index 00000000..e8035b74
--- /dev/null
+++ b/lnbits/extensions/diagonalley/README.md
@@ -0,0 +1,9 @@
+Diagon Alley
+A movable market stand
+Make a list of products to sell, point the list to an relay (or many), stack sats.
+Diagon Alley is a movable market stand, for anon transactions. You then give permission for an relay to list those products. Delivery addresses are sent through the Lightning Network.
+
+
+API endpoints
+
+curl -X GET http://YOUR-TOR-ADDRESS
diff --git a/lnbits/extensions/diagonalley/__init__.py b/lnbits/extensions/diagonalley/__init__.py
new file mode 100644
index 00000000..720c55c8
--- /dev/null
+++ b/lnbits/extensions/diagonalley/__init__.py
@@ -0,0 +1,16 @@
+from quart import Blueprint
+from lnbits.db import Database
+
+db = Database("ext_diagonalley")
+
+diagonalley_ext: Blueprint = Blueprint(
+ "diagonalley", __name__, static_folder="static", template_folder="templates"
+)
+
+from .views_api import * # noqa
+from .views import * # noqa
+
+from .tasks import register_listeners
+from lnbits.tasks import record_async
+
+diagonalley_ext.record(record_async(register_listeners))
diff --git a/lnbits/extensions/diagonalley/config.json b/lnbits/extensions/diagonalley/config.json
new file mode 100644
index 00000000..99e92e9b
--- /dev/null
+++ b/lnbits/extensions/diagonalley/config.json
@@ -0,0 +1,6 @@
+{
+ "name": "Diagon Alley",
+ "short_description": "Movable anonymous market stand",
+ "icon": "add_shopping_cart",
+ "contributors": ["benarc","DeanH"]
+}
diff --git a/lnbits/extensions/diagonalley/crud.py b/lnbits/extensions/diagonalley/crud.py
new file mode 100644
index 00000000..c6ce8222
--- /dev/null
+++ b/lnbits/extensions/diagonalley/crud.py
@@ -0,0 +1,395 @@
+from base64 import urlsafe_b64encode
+from uuid import uuid4
+from typing import List, Optional, Union
+
+from lnbits.settings import WALLET
+
+# from lnbits.db import open_ext_db
+from lnbits.db import SQLITE
+from . import db
+from .models import Products, Orders, Stalls, Zones
+
+import httpx
+from lnbits.helpers import urlsafe_short_hash
+import re
+
+regex = re.compile(
+ r"^(?:http|ftp)s?://" # http:// or https://
+ r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|"
+ r"localhost|"
+ r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"
+ r"(?::\d+)?"
+ r"(?:/?|[/?]\S+)$",
+ re.IGNORECASE,
+)
+
+
+###Products
+
+
+async def create_diagonalley_product(
+ *,
+ stall_id: str,
+ product: str,
+ categories: str,
+ description: str,
+ image: Optional[str] = None,
+ price: int,
+ quantity: int,
+ shippingzones: str,
+) -> Products:
+ returning = "" if db.type == SQLITE else "RETURNING ID"
+ method = db.execute if db.type == SQLITE else db.fetchone
+ product_id = urlsafe_short_hash()
+ # with open_ext_db("diagonalley") as db:
+ result = await (method)(
+ f"""
+ INSERT INTO diagonalley.products (id, stall, product, categories, description, image, price, quantity, shippingzones)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
+ {returning}
+ """,
+ (
+ product_id,
+ stall_id,
+ product,
+ categories,
+ description,
+ image,
+ price,
+ quantity,
+ ),
+ )
+ product = await get_diagonalley_product(product_id)
+ assert product, "Newly created product couldn't be retrieved"
+ return product
+
+
+async def update_diagonalley_product(product_id: str, **kwargs) -> Optional[Stalls]:
+ q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+
+ # with open_ext_db("diagonalley") as db:
+ await db.execute(
+ f"UPDATE diagonalley.products SET {q} WHERE id = ?",
+ (*kwargs.values(), product_id),
+ )
+ row = await db.fetchone(
+ "SELECT * FROM diagonalley.products WHERE id = ?", (product_id,)
+ )
+
+ return get_diagonalley_stall(product_id)
+
+
+async def get_diagonalley_product(product_id: str) -> Optional[Products]:
+ row = await db.fetchone(
+ "SELECT * FROM diagonalley.products WHERE id = ?", (product_id,)
+ )
+ return Products.from_row(row) if row else None
+
+
+async def get_diagonalley_products(wallet_ids: Union[str, List[str]]) -> List[Products]:
+ if isinstance(wallet_ids, str):
+ wallet_ids = [wallet_ids]
+
+ # with open_ext_db("diagonalley") as db:
+ q = ",".join(["?"] * len(wallet_ids))
+ rows = await db.fetchall(
+ f"""
+ SELECT * FROM diagonalley.products WHERE stall IN ({q})
+ """,
+ (*wallet_ids,),
+ )
+ return [Products.from_row(row) for row in rows]
+
+
+async def delete_diagonalley_product(product_id: str) -> None:
+ await db.execute("DELETE FROM diagonalley.products WHERE id = ?", (product_id,))
+
+
+###zones
+
+
+async def create_diagonalley_zone(
+ *,
+ wallet: Optional[str] = None,
+ cost: Optional[int] = 0,
+ countries: Optional[str] = None,
+) -> Zones:
+
+ returning = "" if db.type == SQLITE else "RETURNING ID"
+ method = db.execute if db.type == SQLITE else db.fetchone
+
+ zone_id = urlsafe_short_hash()
+ result = await (method)(
+ f"""
+ INSERT INTO diagonalley.zones (
+ id,
+ wallet,
+ cost,
+ countries
+
+ )
+ VALUES (?, ?, ?, ?)
+ {returning}
+ """,
+ (zone_id, wallet, cost, countries),
+ )
+
+ zone = await get_diagonalley_zone(zone_id)
+ assert zone, "Newly created zone couldn't be retrieved"
+ return zone
+
+
+async def update_diagonalley_zone(zone_id: str, **kwargs) -> Optional[Zones]:
+ q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+ await db.execute(
+ f"UPDATE diagonalley.zones SET {q} WHERE id = ?",
+ (*kwargs.values(), zone_id),
+ )
+ row = await db.fetchone("SELECT * FROM diagonalley.zones WHERE id = ?", (zone_id,))
+ return Zones.from_row(row) if row else None
+
+
+async def get_diagonalley_zone(zone_id: str) -> Optional[Zones]:
+ row = await db.fetchone("SELECT * FROM diagonalley.zones WHERE id = ?", (zone_id,))
+ return Zones.from_row(row) if row else None
+
+
+async def get_diagonalley_zones(wallet_ids: Union[str, List[str]]) -> List[Zones]:
+ if isinstance(wallet_ids, str):
+ wallet_ids = [wallet_ids]
+ print(wallet_ids)
+
+ q = ",".join(["?"] * len(wallet_ids))
+ rows = await db.fetchall(
+ f"SELECT * FROM diagonalley.zones WHERE wallet IN ({q})", (*wallet_ids,)
+ )
+
+ for r in rows:
+ try:
+ x = httpx.get(r["zoneaddress"] + "/" + r["ratingkey"])
+ if x.status_code == 200:
+ await db.execute(
+ "UPDATE diagonalley.zones SET online = ? WHERE id = ?",
+ (
+ True,
+ r["id"],
+ ),
+ )
+ else:
+ await db.execute(
+ "UPDATE diagonalley.zones SET online = ? WHERE id = ?",
+ (
+ False,
+ r["id"],
+ ),
+ )
+ except:
+ print("An exception occurred")
+ q = ",".join(["?"] * len(wallet_ids))
+ rows = await db.fetchall(
+ f"SELECT * FROM diagonalley.zones WHERE wallet IN ({q})", (*wallet_ids,)
+ )
+ return [Zones.from_row(row) for row in rows]
+
+
+async def delete_diagonalley_zone(zone_id: str) -> None:
+ await db.execute("DELETE FROM diagonalley.zones WHERE id = ?", (zone_id,))
+
+
+###Stalls
+
+
+async def create_diagonalley_stall(
+ *,
+ wallet: str,
+ name: str,
+ publickey: str,
+ privatekey: str,
+ relays: str,
+ shippingzones: str,
+) -> Stalls:
+
+ returning = "" if db.type == SQLITE else "RETURNING ID"
+ method = db.execute if db.type == SQLITE else db.fetchone
+
+ stall_id = urlsafe_short_hash()
+ result = await (method)(
+ f"""
+ INSERT INTO diagonalley.stalls (
+ id,
+ wallet,
+ name,
+ publickey,
+ privatekey,
+ relays,
+ shippingzones
+ )
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ {returning}
+ """,
+ (stall_id, wallet, name, publickey, privatekey, relays, shippingzones),
+ )
+
+ stall = await get_diagonalley_stall(stall_id)
+ assert stall, "Newly created stall couldn't be retrieved"
+ return stall
+
+
+async def update_diagonalley_stall(stall_id: str, **kwargs) -> Optional[Stalls]:
+ q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+ await db.execute(
+ f"UPDATE diagonalley.stalls SET {q} WHERE id = ?",
+ (*kwargs.values(), stall_id),
+ )
+ row = await db.fetchone(
+ "SELECT * FROM diagonalley.stalls WHERE id = ?", (stall_id,)
+ )
+ return Stalls.from_row(row) if row else None
+
+
+async def get_diagonalley_stall(stall_id: str) -> Optional[Stalls]:
+ roww = await db.fetchone(
+ "SELECT * FROM diagonalley.stalls WHERE id = ?", (stall_id,)
+ )
+
+ try:
+ x = httpx.get(roww["stalladdress"] + "/" + roww["ratingkey"])
+ if x.status_code == 200:
+ await db.execute(
+ "UPDATE diagonalley.stalls SET online = ? WHERE id = ?",
+ (
+ True,
+ stall_id,
+ ),
+ )
+ else:
+ await db.execute(
+ "UPDATE diagonalley.stalls SET online = ? WHERE id = ?",
+ (
+ False,
+ stall_id,
+ ),
+ )
+ except:
+ print("An exception occurred")
+
+ # with open_ext_db("diagonalley") as db:
+ row = await db.fetchone(
+ "SELECT * FROM diagonalley.stalls WHERE id = ?", (stall_id,)
+ )
+ return Stalls.from_row(row) if row else None
+
+
+async def get_diagonalley_stalls(wallet_ids: Union[str, List[str]]) -> List[Stalls]:
+ if isinstance(wallet_ids, str):
+ wallet_ids = [wallet_ids]
+
+ q = ",".join(["?"] * len(wallet_ids))
+ rows = await db.fetchall(
+ f"SELECT * FROM diagonalley.stalls WHERE wallet IN ({q})", (*wallet_ids,)
+ )
+
+ for r in rows:
+ try:
+ x = httpx.get(r["stalladdress"] + "/" + r["ratingkey"])
+ if x.status_code == 200:
+ await db.execute(
+ "UPDATE diagonalley.stalls SET online = ? WHERE id = ?",
+ (
+ True,
+ r["id"],
+ ),
+ )
+ else:
+ await db.execute(
+ "UPDATE diagonalley.stalls SET online = ? WHERE id = ?",
+ (
+ False,
+ r["id"],
+ ),
+ )
+ except:
+ print("An exception occurred")
+ q = ",".join(["?"] * len(wallet_ids))
+ rows = await db.fetchall(
+ f"SELECT * FROM diagonalley.stalls WHERE wallet IN ({q})", (*wallet_ids,)
+ )
+ return [Stalls.from_row(row) for row in rows]
+
+
+async def delete_diagonalley_stall(stall_id: str) -> None:
+ await db.execute("DELETE FROM diagonalley.stalls WHERE id = ?", (stall_id,))
+
+
+###Orders
+
+
+async def create_diagonalley_order(
+ *,
+ productid: str,
+ wallet: str,
+ product: str,
+ quantity: int,
+ shippingzone: str,
+ address: str,
+ email: str,
+ invoiceid: str,
+ paid: bool,
+ shipped: bool,
+) -> Orders:
+ returning = "" if db.type == SQLITE else "RETURNING ID"
+ method = db.execute if db.type == SQLITE else db.fetchone
+
+ order_id = urlsafe_short_hash()
+ result = await (method)(
+ f"""
+ INSERT INTO diagonalley.orders (id, productid, wallet, product,
+ quantity, shippingzone, address, email, invoiceid, paid, shipped)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ {returning}
+ """,
+ (
+ order_id,
+ productid,
+ wallet,
+ product,
+ quantity,
+ shippingzone,
+ address,
+ email,
+ invoiceid,
+ False,
+ False,
+ ),
+ )
+ if db.type == SQLITE:
+ order_id = result._result_proxy.lastrowid
+ else:
+ order_id = result[0]
+
+ link = await get_diagonalley_order(order_id)
+ assert link, "Newly created link couldn't be retrieved"
+ return link
+
+
+async def get_diagonalley_order(order_id: str) -> Optional[Orders]:
+ row = await db.fetchone(
+ "SELECT * FROM diagonalley.orders WHERE id = ?", (order_id,)
+ )
+ return Orders.from_row(row) if row else None
+
+
+async def get_diagonalley_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 diagonalley.orders WHERE wallet IN ({q})", (*wallet_ids,)
+ )
+ #
+ return [Orders.from_row(row) for row in rows]
+
+
+async def delete_diagonalley_order(order_id: str) -> None:
+ await db.execute("DELETE FROM diagonalley.orders WHERE id = ?", (order_id,))
diff --git a/lnbits/extensions/diagonalley/migrations.py b/lnbits/extensions/diagonalley/migrations.py
new file mode 100644
index 00000000..1523f398
--- /dev/null
+++ b/lnbits/extensions/diagonalley/migrations.py
@@ -0,0 +1,69 @@
+async def m001_initial(db):
+ """
+ Initial products table.
+ """
+ await db.execute(
+ """
+ CREATE TABLE diagonalley.products (
+ id TEXT PRIMARY KEY,
+ stall TEXT NOT NULL,
+ product TEXT NOT NULL,
+ categories TEXT NOT NULL,
+ description TEXT NOT NULL,
+ image TEXT NOT NULL,
+ price INTEGER NOT NULL,
+ quantity INTEGER NOT NULL
+ );
+ """
+ )
+
+ """
+ Initial stalls table.
+ """
+ await db.execute(
+ """
+ CREATE TABLE diagonalley.stalls (
+ id TEXT PRIMARY KEY,
+ wallet TEXT NOT NULL,
+ name TEXT NOT NULL,
+ publickey TEXT NOT NULL,
+ privatekey TEXT NOT NULL,
+ relays TEXT NOT NULL
+ );
+ """
+ )
+
+ """
+ Initial zones table.
+ """
+ await db.execute(
+ """
+ CREATE TABLE diagonalley.zones (
+ id TEXT PRIMARY KEY,
+ wallet TEXT NOT NULL,
+ cost TEXT NOT NULL,
+ countries TEXT NOT NULL
+ );
+ """
+ )
+
+ """
+ Initial orders table.
+ """
+ await db.execute(
+ """
+ CREATE TABLE diagonalley.orders (
+ id TEXT PRIMARY KEY,
+ productid TEXT NOT NULL,
+ wallet TEXT NOT NULL,
+ product TEXT NOT NULL,
+ quantity INTEGER NOT NULL,
+ shippingzone INTEGER NOT NULL,
+ address TEXT NOT NULL,
+ email TEXT NOT NULL,
+ invoiceid TEXT NOT NULL,
+ paid BOOLEAN NOT NULL,
+ shipped BOOLEAN NOT NULL
+ );
+ """
+ )
diff --git a/lnbits/extensions/diagonalley/models.py b/lnbits/extensions/diagonalley/models.py
new file mode 100644
index 00000000..0f2a1d78
--- /dev/null
+++ b/lnbits/extensions/diagonalley/models.py
@@ -0,0 +1,57 @@
+from urllib.parse import urlparse, urlunparse, parse_qs, urlencode, ParseResult
+from starlette.requests import Request
+from fastapi.param_functions import Query
+from typing import Optional, Dict
+from lnbits.lnurl import encode as lnurl_encode # type: ignore
+from lnurl.types import LnurlPayMetadata # type: ignore
+from pydantic import BaseModel
+import json
+from sqlite3 import Row
+
+
+class Stalls(BaseModel):
+ id: str = Query(None)
+ wallet: str = Query(None)
+ name: str = Query(None)
+ publickey: str = Query(None)
+ privatekey: str = Query(None)
+ relays: str = Query(None)
+
+class createStalls(BaseModel):
+ wallet: str = Query(None)
+ name: str = Query(None)
+ publickey: str = Query(None)
+ privatekey: str = Query(None)
+ relays: str = Query(None)
+ shippingzones: str = Query(None)
+
+class Products(BaseModel):
+ id: str = Query(None)
+ stall: str = Query(None)
+ product: str = Query(None)
+ categories: str = Query(None)
+ description: str = Query(None)
+ image: str = Query(None)
+ price: int = Query(0)
+ quantity: int = Query(0)
+
+
+class Zones(BaseModel):
+ id: str = Query(None)
+ wallet: str = Query(None)
+ cost: str = Query(None)
+ countries: str = Query(None)
+
+
+class Orders(BaseModel):
+ id: str = Query(None)
+ productid: str = Query(None)
+ stall: str = Query(None)
+ product: str = Query(None)
+ quantity: int = Query(0)
+ shippingzone: int = Query(0)
+ address: str = Query(None)
+ email: str = Query(None)
+ invoiceid: str = Query(None)
+ paid: bool
+ shipped: bool
diff --git a/lnbits/extensions/diagonalley/static/js/index.js b/lnbits/extensions/diagonalley/static/js/index.js
new file mode 100644
index 00000000..1a25edaa
--- /dev/null
+++ b/lnbits/extensions/diagonalley/static/js/index.js
@@ -0,0 +1,824 @@
+/* globals Quasar, Vue, _, VueQrcode, windowMixin, LNbits, LOCALE */
+
+Vue.component(VueQrcode.name, VueQrcode)
+
+const pica = window.pica()
+
+new Vue({
+ el: '#vue',
+ mixins: [windowMixin],
+ data: function () {
+ return {
+ products: [],
+ orders: [],
+ stalls: [],
+ zones: [],
+ shippedModel: false,
+ shippingZoneOptions: [
+ '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)'
+ ],
+ relayOptions: [
+ 'wss://nostr-relay.herokuapp.com/ws',
+ 'wss://nostr-relay.bigsun.xyz/ws',
+ 'wss://freedom-relay.herokuapp.com/ws'
+ ],
+ label: '',
+ ordersTable: {
+ columns: [
+ {
+ name: 'product',
+ align: 'left',
+ label: 'Product',
+ field: 'product'
+ },
+ {
+ name: 'quantity',
+ align: 'left',
+ label: 'Quantity',
+ field: 'quantity'
+ },
+ {
+ name: 'address',
+ align: 'left',
+ label: 'Address',
+ field: 'address'
+ },
+ {
+ name: 'invoiceid',
+ align: 'left',
+ label: 'InvoiceID',
+ field: 'invoiceid'
+ },
+ {name: 'paid', align: 'left', label: 'Paid', field: 'paid'},
+ {name: 'shipped', align: 'left', label: 'Shipped', field: 'shipped'}
+ ],
+ pagination: {
+ rowsPerPage: 10
+ }
+ },
+ productsTable: {
+ columns: [
+ {
+ name: 'stall',
+ align: 'left',
+ label: 'Stall',
+ field: 'stall'
+ },
+ {
+ name: 'product',
+ align: 'left',
+ label: 'Product',
+ field: 'product'
+ },
+ {
+ name: 'description',
+ align: 'left',
+ label: 'Description',
+ field: 'description'
+ },
+ {
+ name: 'categories',
+ align: 'left',
+ label: 'Categories',
+ field: 'categories'
+ },
+ {name: 'price', align: 'left', label: 'Price', field: 'price'},
+ {
+ name: 'quantity',
+ align: 'left',
+ label: 'Quantity',
+ field: 'quantity'
+ },
+ {name: 'id', align: 'left', label: 'ID', field: 'id'}
+ ],
+ pagination: {
+ rowsPerPage: 10
+ }
+ },
+ stallTable: {
+ columns: [
+ {
+ name: 'id',
+ align: 'left',
+ label: 'ID',
+ field: 'id'
+ },
+ {
+ name: 'name',
+ align: 'left',
+ label: 'Name',
+ field: 'name'
+ },
+ {
+ name: 'wallet',
+ align: 'left',
+ label: 'Wallet',
+ field: 'wallet'
+ },
+ {
+ name: 'publickey',
+ align: 'left',
+ label: 'Public key',
+ field: 'publickey'
+ },
+ {
+ name: 'privatekey',
+ align: 'left',
+ label: 'Private key',
+ field: 'privatekey'
+ }
+ ],
+ pagination: {
+ rowsPerPage: 10
+ }
+ },
+ zonesTable: {
+ columns: [
+ {
+ name: 'id',
+ align: 'left',
+ label: 'ID',
+ field: 'id'
+ },
+ {
+ name: 'countries',
+ align: 'left',
+ label: 'Countries',
+ field: 'countries'
+ },
+ {
+ name: 'cost',
+ align: 'left',
+ label: 'Cost',
+ field: 'cost'
+ }
+ ],
+ pagination: {
+ rowsPerPage: 10
+ }
+ },
+ productDialog: {
+ show: false,
+ data: {}
+ },
+ stallDialog: {
+ show: false,
+ data: {}
+ },
+ zoneDialog: {
+ show: false,
+ data: {}
+ },
+ shopDialog: {
+ show: false,
+ data: {activate: false}
+ },
+ orderDialog: {
+ show: false,
+ data: {}
+ },
+ relayDialog: {
+ show: false,
+ data: {}
+ }
+ }
+ },
+ computed: {
+ categoryOther: function () {
+ cats = trim(this.productDialog.data.categories.split(','))
+ for (let i = 0; i < cats.length; i++) {
+ if (cats[i] == 'Others') {
+ return true
+ }
+ }
+ return false
+ }
+ },
+ methods: {
+ ////////////////////////////////////////
+ ////////////////STALLS//////////////////
+ ////////////////////////////////////////
+ getStalls: function () {
+ var self = this
+ LNbits.api
+ .request(
+ 'GET',
+ '/diagonalley/api/v1/stalls?all_wallets',
+ this.g.user.wallets[0].inkey
+ )
+ .then(function (response) {
+ self.stalls = response.data.map(function (obj) {
+ console.log(obj)
+ return mapDiagonAlley(obj)
+ })
+ })
+ },
+ openStallUpdateDialog: function (linkId) {
+ var self = this
+ var link = _.findWhere(self.stalls, {id: linkId})
+
+ this.stallDialog.data = _.clone(link._data)
+ this.stallDialog.show = true
+ },
+ sendStallFormData: function () {
+ if (this.stallDialog.data.id) {
+ } else {
+ var data = {
+ name: this.stallDialog.data.name,
+ wallet: this.stallDialog.data.wallet,
+ publickey: this.stallDialog.data.publickey,
+ privatekey: this.stallDialog.data.privatekey,
+ relays: this.stallDialog.data.relays
+ }
+ }
+
+ if (this.stallDialog.data.id) {
+ this.updateStall(this.stallDialog.data)
+ } else {
+ this.createStall(data)
+ }
+ },
+ updateStall: function (data) {
+ var self = this
+ LNbits.api
+ .request(
+ 'PUT',
+ '/diagonalley/api/v1/stalls' + data.id,
+ _.findWhere(self.g.user.wallets, {
+ id: self.stallDialog.data.wallet
+ }).inkey,
+ _.pick(data, 'name', 'wallet', 'publickey', 'privatekey')
+ )
+ .then(function (response) {
+ self.stalls = _.reject(self.stalls, function (obj) {
+ return obj.id == data.id
+ })
+ self.stalls.push(mapDiagonAlley(response.data))
+ self.stallDialog.show = false
+ self.stallDialog.data = {}
+ data = {}
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ createStall: function (data) {
+ var self = this
+ LNbits.api
+ .request(
+ 'POST',
+ '/diagonalley/api/v1/stalls',
+ _.findWhere(self.g.user.wallets, {
+ id: self.stallDialog.data.wallet
+ }).inkey,
+ data
+ )
+ .then(function (response) {
+ self.stalls.push(mapDiagonAlley(response.data))
+ self.stallDialog.show = false
+ self.stallDialog.data = {}
+ data = {}
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ deleteStall: function (stallId) {
+ var self = this
+ var stall = _.findWhere(self.stalls, {id: stallId})
+
+ LNbits.utils
+ .confirmDialog('Are you sure you want to delete this Stall link?')
+ .onOk(function () {
+ LNbits.api
+ .request(
+ 'DELETE',
+ '/diagonalley/api/v1/stalls/' + stallId,
+ _.findWhere(self.g.user.wallets, {id: stall.wallet}).inkey
+ )
+ .then(function (response) {
+ self.stalls = _.reject(self.stalls, function (obj) {
+ return obj.id == stallId
+ })
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ })
+ },
+ exportStallsCSV: function () {
+ LNbits.utils.exportCSV(this.stallsTable.columns, this.stalls)
+ },
+ ////////////////////////////////////////
+ ///////////////PRODUCTS/////////////////
+ ////////////////////////////////////////
+ getProducts: function () {
+ var self = this
+
+ LNbits.api
+ .request(
+ 'GET',
+ '/diagonalley/api/v1/products?all_stalls',
+ this.g.user.wallets[0].inkey
+ )
+ .then(function (response) {
+ self.products = response.data.map(function (obj) {
+ return mapDiagonAlley(obj)
+ })
+ })
+ },
+ openProductUpdateDialog: function (linkId) {
+ var self = this
+ var link = _.findWhere(self.products, {id: linkId})
+
+ self.productDialog.data = _.clone(link._data)
+ self.productDialog.show = true
+ },
+ sendProductFormData: function () {
+ if (this.productDialog.data.id) {
+ } else {
+ var data = {
+ product: this.productDialog.data.product,
+ categories:
+ this.productDialog.data.categories +
+ this.productDialog.categoriesextra,
+ description: this.productDialog.data.description,
+ image: this.productDialog.data.image,
+ price: this.productDialog.data.price,
+ quantity: this.productDialog.data.quantity
+ }
+ }
+ if (this.productDialog.data.id) {
+ this.updateProduct(this.productDialog.data)
+ } else {
+ this.createProduct(data)
+ }
+ },
+ imageAdded(file) {
+ let blobURL = URL.createObjectURL(file)
+ let image = new Image()
+ image.src = blobURL
+ image.onload = async () => {
+ let canvas = document.createElement('canvas')
+ canvas.setAttribute('width', 100)
+ canvas.setAttribute('height', 100)
+ await pica.resize(image, canvas, {
+ quality: 0,
+ alpha: true,
+ unsharpAmount: 95,
+ unsharpRadius: 0.9,
+ unsharpThreshold: 70
+ })
+ this.productDialog.data.image = canvas.toDataURL()
+ this.productDialog = {...this.productDialog}
+ }
+ },
+ imageCleared() {
+ this.productDialog.data.image = null
+ this.productDialog = {...this.productDialog}
+ },
+ updateProduct: function (data) {
+ var self = this
+ LNbits.api
+ .request(
+ 'PUT',
+ '/diagonalley/api/v1/products' + data.id,
+ _.findWhere(self.g.user.wallets, {
+ id: self.productDialog.data.wallet
+ }).inkey,
+ _.pick(
+ data,
+ 'shopname',
+ 'relayaddress',
+ 'shippingzone1',
+ 'zone1cost',
+ 'shippingzone2',
+ 'zone2cost',
+ 'email'
+ )
+ )
+ .then(function (response) {
+ self.products = _.reject(self.products, function (obj) {
+ return obj.id == data.id
+ })
+ self.products.push(mapDiagonAlley(response.data))
+ self.productDialog.show = false
+ self.productDialog.data = {}
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ createProduct: function (data) {
+ var self = this
+ LNbits.api
+ .request(
+ 'POST',
+ '/diagonalley/api/v1/products',
+ _.findWhere(self.g.user.wallets, {
+ id: self.productDialog.data.wallet
+ }).inkey,
+ data
+ )
+ .then(function (response) {
+ self.products.push(mapDiagonAlley(response.data))
+ self.productDialog.show = false
+ self.productDialog.data = {}
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ deleteProduct: function (productId) {
+ var self = this
+ var product = _.findWhere(this.products, {id: productId})
+
+ LNbits.utils
+ .confirmDialog('Are you sure you want to delete this products link?')
+ .onOk(function () {
+ LNbits.api
+ .request(
+ 'DELETE',
+ '/diagonalley/api/v1/products/' + productId,
+ _.findWhere(self.g.user.wallets, {id: product.wallet}).inkey
+ )
+ .then(function (response) {
+ self.products = _.reject(self.products, function (obj) {
+ return obj.id == productId
+ })
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ })
+ },
+ exportProductsCSV: function () {
+ LNbits.utils.exportCSV(this.productsTable.columns, this.products)
+ },
+ ////////////////////////////////////////
+ //////////////////ZONE//////////////////
+ ////////////////////////////////////////
+ getZones: function () {
+ var self = this
+
+ LNbits.api
+ .request(
+ 'GET',
+ '/diagonalley/api/v1/zones?all_wallets',
+ this.g.user.wallets[0].inkey
+ )
+ .then(function (response) {
+ self.zones = response.data.map(function (obj) {
+ return mapDiagonAlley(obj)
+ })
+ })
+ },
+ openZoneUpdateDialog: function (linkId) {
+ var self = this
+ var link = _.findWhere(self.zones, {id: linkId})
+
+ this.zoneDialog.data = _.clone(link._data)
+ this.zoneDialog.show = true
+ },
+ sendZoneFormData: function () {
+ if (this.zoneDialog.data.id) {
+ } else {
+ var data = {
+ countries: toString(this.zoneDialog.data.countries),
+ cost: parseInt(this.zoneDialog.data.cost)
+ }
+ }
+
+ if (this.zoneDialog.data.id) {
+ this.updateZone(this.zoneDialog.data)
+ } else {
+ this.createZone(data)
+ }
+ },
+ updateZone: function (data) {
+ var self = this
+ LNbits.api
+ .request(
+ 'PUT',
+ '/diagonalley/api/v1/zones' + data.id,
+ _.findWhere(self.g.user.wallets, {
+ id: self.zoneDialog.data.wallet
+ }).inkey,
+ _.pick(data, 'countries', 'cost')
+ )
+ .then(function (response) {
+ self.zones = _.reject(self.zones, function (obj) {
+ return obj.id == data.id
+ })
+ self.zones.push(mapDiagonAlley(response.data))
+ self.zoneDialog.show = false
+ self.zoneDialog.data = {}
+ data = {}
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ createZone: function (data) {
+ var self = this
+ console.log(self.g.user.wallets[0])
+ console.log(data)
+ LNbits.api
+ .request(
+ 'POST',
+ '/diagonalley/api/v1/zones',
+ self.g.user.wallets[0].inkey,
+ data
+ )
+ .then(function (response) {
+ self.zones.push(mapDiagonAlley(response.data))
+ self.zoneDialog.show = false
+ self.zoneDialog.data = {}
+ data = {}
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ deleteZone: function (zoneId) {
+ var self = this
+ var zone = _.findWhere(self.zones, {id: zoneId})
+
+ LNbits.utils
+ .confirmDialog('Are you sure you want to delete this Zone link?')
+ .onOk(function () {
+ LNbits.api
+ .request(
+ 'DELETE',
+ '/diagonalley/api/v1/zones/' + zoneId,
+ _.findWhere(self.g.user.wallets, {id: zone.wallet}).inkey
+ )
+ .then(function (response) {
+ self.zones = _.reject(self.zones, function (obj) {
+ return obj.id == zoneId
+ })
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ })
+ },
+ exportZonesCSV: function () {
+ LNbits.utils.exportCSV(this.zonesTable.columns, this.zones)
+ },
+ ////////////////////////////////////////
+ //////////////////SHOP//////////////////
+ ////////////////////////////////////////
+ getShops: function () {
+ var self = this
+
+ LNbits.api
+ .request(
+ 'GET',
+ '/diagonalley/api/v1/shops?all_wallets',
+ this.g.user.wallets[0].inkey
+ )
+ .then(function (response) {
+ self.shops = response.data.map(function (obj) {
+ return mapDiagonAlley(obj)
+ })
+ })
+ },
+ openShopUpdateDialog: function (linkId) {
+ var self = this
+ var link = _.findWhere(self.shops, {id: linkId})
+
+ this.shopDialog.data = _.clone(link._data)
+ this.shopDialog.show = true
+ },
+ sendShopFormData: function () {
+ if (this.shopDialog.data.id) {
+ } else {
+ var data = {
+ countries: this.shopDialog.data.countries,
+ cost: this.shopDialog.data.cost
+ }
+ }
+
+ if (this.shopDialog.data.id) {
+ this.updateZone(this.shopDialog.data)
+ } else {
+ this.createZone(data)
+ }
+ },
+ updateShop: function (data) {
+ var self = this
+ LNbits.api
+ .request(
+ 'PUT',
+ '/diagonalley/api/v1/shops' + data.id,
+ _.findWhere(self.g.user.wallets, {
+ id: self.shopDialog.data.wallet
+ }).inkey,
+ _.pick(data, 'countries', 'cost')
+ )
+ .then(function (response) {
+ self.shops = _.reject(self.shops, function (obj) {
+ return obj.id == data.id
+ })
+ self.shops.push(mapDiagonAlley(response.data))
+ self.shopDialog.show = false
+ self.shopDialog.data = {}
+ data = {}
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ createShop: function (data) {
+ var self = this
+ console.log('cuntywoo')
+ LNbits.api
+ .request(
+ 'POST',
+ '/diagonalley/api/v1/shops',
+ _.findWhere(self.g.user.wallets, {
+ id: self.shopDialog.data.wallet
+ }).inkey,
+ data
+ )
+ .then(function (response) {
+ self.shops.push(mapDiagonAlley(response.data))
+ self.shopDialog.show = false
+ self.shopDialog.data = {}
+ data = {}
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ deleteShop: function (shopId) {
+ var self = this
+ var shop = _.findWhere(self.shops, {id: shopId})
+
+ LNbits.utils
+ .confirmDialog('Are you sure you want to delete this Shop link?')
+ .onOk(function () {
+ LNbits.api
+ .request(
+ 'DELETE',
+ '/diagonalley/api/v1/shops/' + shopId,
+ _.findWhere(self.g.user.wallets, {id: shop.wallet}).inkey
+ )
+ .then(function (response) {
+ self.shops = _.reject(self.shops, function (obj) {
+ return obj.id == shopId
+ })
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ })
+ },
+ exportShopsCSV: function () {
+ LNbits.utils.exportCSV(this.shopsTable.columns, this.shops)
+ },
+ ////////////////////////////////////////
+ ////////////////ORDERS//////////////////
+ ////////////////////////////////////////
+ getOrders: function () {
+ var self = this
+
+ LNbits.api
+ .request(
+ 'GET',
+ '/diagonalley/api/v1/orders?all_wallets',
+ this.g.user.wallets[0].inkey
+ )
+ .then(function (response) {
+ self.orders = response.data.map(function (obj) {
+ return mapDiagonAlley(obj)
+ })
+ })
+ },
+ createOrder: function () {
+ var data = {
+ address: this.orderDialog.data.address,
+ email: this.orderDialog.data.email,
+ quantity: this.orderDialog.data.quantity,
+ shippingzone: this.orderDialog.data.shippingzone
+ }
+ var self = this
+
+ LNbits.api
+ .request(
+ 'POST',
+ '/diagonalley/api/v1/orders',
+ _.findWhere(self.g.user.wallets, {id: self.orderDialog.data.wallet})
+ .inkey,
+ data
+ )
+ .then(function (response) {
+ self.orders.push(mapDiagonAlley(response.data))
+ self.orderDialog.show = false
+ self.orderDialog.data = {}
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ deleteOrder: function (orderId) {
+ var self = this
+ var order = _.findWhere(self.orders, {id: orderId})
+
+ LNbits.utils
+ .confirmDialog('Are you sure you want to delete this order link?')
+ .onOk(function () {
+ LNbits.api
+ .request(
+ 'DELETE',
+ '/diagonalley/api/v1/orders/' + orderId,
+ _.findWhere(self.g.user.wallets, {id: order.wallet}).inkey
+ )
+ .then(function (response) {
+ self.orders = _.reject(self.orders, function (obj) {
+ return obj.id == orderId
+ })
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ })
+ },
+ shipOrder: function (order_id) {
+ var self = this
+
+ LNbits.api
+ .request(
+ 'GET',
+ '/diagonalley/api/v1/orders/shipped/' + order_id,
+ this.g.user.wallets[0].inkey
+ )
+ .then(function (response) {
+ self.orders = response.data.map(function (obj) {
+ return mapDiagonAlley(obj)
+ })
+ })
+ },
+ exportOrdersCSV: function () {
+ LNbits.utils.exportCSV(this.ordersTable.columns, this.orders)
+ }
+ },
+ created: function () {
+ if (this.g.user.wallets.length) {
+ this.getStalls()
+ this.getProducts()
+ this.getZones()
+ this.getOrders()
+ }
+ }
+})
diff --git a/lnbits/extensions/diagonalley/tasks.py b/lnbits/extensions/diagonalley/tasks.py
new file mode 100644
index 00000000..3fee63d9
--- /dev/null
+++ b/lnbits/extensions/diagonalley/tasks.py
@@ -0,0 +1,29 @@
+import asyncio
+
+from lnbits.core.models import Payment
+from lnbits.tasks import register_invoice_listener
+
+from .crud import get_ticket, set_ticket_paid
+
+
+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 "lnticket" != payment.extra.get("tag"):
+ # not a lnticket invoice
+ return
+
+ ticket = await get_ticket(payment.checking_id)
+ if not ticket:
+ print("this should never happen", payment)
+ return
+
+ await payment.set_pending(False)
+ await set_ticket_paid(payment.payment_hash)
\ No newline at end of file
diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/_api_docs.html b/lnbits/extensions/diagonalley/templates/diagonalley/_api_docs.html
new file mode 100644
index 00000000..530c89e8
--- /dev/null
+++ b/lnbits/extensions/diagonalley/templates/diagonalley/_api_docs.html
@@ -0,0 +1,129 @@
+
+
+
+
+ Diagon Alley: Decentralised Market-Stalls
+
+
+ Each Stall has its own keys!
+
+ - Create Shipping Zones you're willing to ship to
+ - Create a Stall to list yiur products on
+ - Create products to put on the Stall
+ - List stalls on a simple frontend shop page, or point at Nostr shop client key
+
+ Make a list of products to sell, point your list of products at a public
+ relay. Buyers browse your products on the relay, and pay you directly.
+ Ratings are managed by the relay. Your stall can be listed in multiple
+ relays, even over TOR, if you wish to be anonymous.
+ More information on the
+ Diagon Alley Protocol
+
+ Created by, Ben Arc
+
+
+
+
+
+
+
+
+
+ GET
+ /diagonalley/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
+ /diagonalley/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
+ /diagonalley/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/diagonalley/templates/diagonalley/index.html b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
new file mode 100644
index 00000000..98405f6d
--- /dev/null
+++ b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
@@ -0,0 +1,634 @@
+{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
+%} {% block page %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Update Product
+
+ Create Product
+
+ Cancel
+
+
+
+
+
+
+
+
+
+
+
+ Update Shipping Zone
+ Create Shipping Zone
+
+ Cancel
+
+
+
+
+
+
+
+
+
+
+
+
+ Update Relay
+ Launch
+
+ Cancel
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Generate keys
+
+
+ Restore keys
+
+
+
+
+
+
+
+
+
+
+ Update Stall
+ Create Stall
+ Cancel
+
+
+
+
+
+
+
+
+ + Product List a product
+ + Shipping Zone Create a shipping zone
+ + Stall
+ Create a stall to list products on
+ Launch frontend shop (not Nostr)
+ Makes a simple frontend shop for your stalls
+
+
+
+
+
+
+
+
Orders
+
+
+ Export to CSV
+
+
+
+ {% raw %}
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+
+ {{ col.value }}
+
+
+
+
+ Product shipped?
+
+
+
+
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+
+
+
Products
+
+
+ Export to CSV
+
+
+
+ {% raw %}
+
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+
+
+ Link to pass to stall relay
+
+
+ {{ col.value }}
+
+
+
+
+
+
+
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+
+
+
Stalls
+
+
+ Export to CSV
+
+
+
+ {% raw %}
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+
+ {{ col.value }}
+
+
+
+
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+
+
+
Shipping Zones
+
+
+ Export to CSV
+
+
+
+ {% raw %}
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+
+ {{ col.value }}
+
+
+
+
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+
+
+
+ LNbits Diagon Alley Extension, powered by Nostr
+
+
+
+
+ {% include "diagonalley/_api_docs.html" %}
+
+
+
+
+ Messages (example)
+
+
+
+
+
+
+ OrderID:87h87h
KJBIBYBUYBUF90898....
+ OrderID:NIUHB7
79867KJGJHGVFYFV....
+
+
+
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(user) }}
+
+
+{% endblock %}
diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/stall.html b/lnbits/extensions/diagonalley/templates/diagonalley/stall.html
new file mode 100644
index 00000000..768bedfe
--- /dev/null
+++ b/lnbits/extensions/diagonalley/templates/diagonalley/stall.html
@@ -0,0 +1,9 @@
+
+
+
diff --git a/lnbits/extensions/diagonalley/views.py b/lnbits/extensions/diagonalley/views.py
new file mode 100644
index 00000000..2deed72b
--- /dev/null
+++ b/lnbits/extensions/diagonalley/views.py
@@ -0,0 +1,44 @@
+
+from typing import List
+
+from fastapi import Request, WebSocket, WebSocketDisconnect
+from fastapi.params import Depends
+from fastapi.templating import Jinja2Templates
+from starlette.responses import HTMLResponse # type: ignore
+
+from http import HTTPStatus
+import json
+from lnbits.decorators import check_user_exists, validate_uuids
+from lnbits.extensions.diagonalley import diagonalley_ext
+
+from .crud import (
+ create_diagonalley_product,
+ get_diagonalley_product,
+ get_diagonalley_products,
+ delete_diagonalley_product,
+ create_diagonalley_order,
+ get_diagonalley_order,
+ get_diagonalley_orders,
+ update_diagonalley_product,
+)
+
+
+@diagonalley_ext.get("/", response_class=HTMLResponse)
+@validate_uuids(["usr"], required=True)
+@check_user_exists(request: Request)
+async def index():
+ return await render_template("diagonalley/index.html", user=g.user)
+
+
+@diagonalley_ext.get("/", response_class=HTMLResponse)
+async def display(request: Request, stall_id):
+ product = await get_diagonalley_products(stall_id)
+ if not product:
+ abort(HTTPStatus.NOT_FOUND, "Stall does not exist.")
+
+ return await render_template(
+ "diagonalley/stall.html",
+ stall=json.dumps(
+ [product._asdict() for product in await get_diagonalley_products(stall_id)]
+ ),
+ )
diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py
new file mode 100644
index 00000000..4de2799e
--- /dev/null
+++ b/lnbits/extensions/diagonalley/views_api.py
@@ -0,0 +1,348 @@
+from http import HTTPStatus
+
+from fastapi import Request
+from fastapi.param_functions import Query
+from fastapi.params import Depends
+from starlette.exceptions import HTTPException
+
+from lnbits.core.crud import get_user
+from lnbits.decorators import api_check_wallet_key, api_validate_post_request
+
+from . import diagonalley_ext
+from .crud import (
+ create_diagonalley_product,
+ get_diagonalley_product,
+ get_diagonalley_products,
+ delete_diagonalley_product,
+ create_diagonalley_zone,
+ update_diagonalley_zone,
+ get_diagonalley_zone,
+ get_diagonalley_zones,
+ delete_diagonalley_zone,
+ create_diagonalley_stall,
+ update_diagonalley_stall,
+ get_diagonalley_stall,
+ get_diagonalley_stalls,
+ delete_diagonalley_stall,
+ create_diagonalley_order,
+ get_diagonalley_order,
+ get_diagonalley_orders,
+ update_diagonalley_product,
+ delete_diagonalley_order,
+)
+from lnbits.core.services import create_invoice
+from base64 import urlsafe_b64encode
+from uuid import uuid4
+
+# from lnbits.db import open_ext_db
+
+from . import db
+from .models import Products, Orders, Stalls
+
+### Products
+
+@copilot_ext.get("/api/v1/copilot/{copilot_id}")
+async def api_copilot_retrieve(
+ req: Request,
+ copilot_id: str = Query(None),
+ wallet: WalletTypeInfo = Depends(get_key_type),
+):
+ copilot = await get_copilot(copilot_id)
+ if not copilot:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="Copilot not found"
+ )
+ if not copilot.lnurl_toggle:
+ return copilot.dict()
+ return {**copilot.dict(), **{"lnurl": copilot.lnurl(req)}}
+
+
+@diagonalley_ext.get("/api/v1/products")
+async def api_diagonalley_products(
+ req: Request,
+ wallet: WalletTypeInfo = Depends(get_key_type),
+):
+ wallet_ids = [wallet.wallet.id]
+
+ if "all_stalls" in request.args:
+ wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
+
+ return ([product._asdict() for product in await get_diagonalley_products(wallet_ids)])
+
+
+@diagonalley_ext.post("/api/v1/products")
+@diagonalley_ext.put("/api/v1/products/{product_id}")
+async def api_diagonalley_product_create(
+ data: Products,
+ product_id=: str = Query(None),
+ wallet: WalletTypeInfo = Depends(get_key_type)
+ ):
+
+ if product_id:
+ product = await get_diagonalley_product(product_id)
+
+ if not product:
+ return ({"message": "Withdraw product does not exist."}))
+
+ if product.wallet != wallet.wallet.id:
+ return ({"message": "Not your withdraw product."}))
+
+ product = await update_diagonalley_product(product_id, data)
+ else:
+ product = await create_diagonalley_product(wallet_id=wallet.wallet.id, data)
+
+ return ({**product._asdict()}))
+
+
+@diagonalley_ext.route("/api/v1/products/{product_id}")
+async def api_diagonalley_products_delete(product_id, wallet: WalletTypeInfo = Depends(require_admin_key)):
+ product = await get_diagonalley_product(product_id)
+
+ if not product:
+ return ({"message": "Product does not exist."})
+
+ if product.wallet != wallet.wallet.id:
+ return ({"message": "Not your Diagon Alley."})
+
+ await delete_diagonalley_product(product_id)
+
+ raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
+
+
+# # # Shippingzones
+
+
+@diagonalley_ext.get("/api/v1/zones")
+async def api_diagonalley_zones(wallet: WalletTypeInfo = Depends(get_key_type)):
+ wallet_ids = [wallet.wallet.id]
+
+ if "all_wallets" in request.args:
+ wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
+
+ return ([zone._asdict() for zone in await get_diagonalley_zones(wallet_ids)]))
+
+
+@diagonalley_ext.post("/api/v1/zones")
+@diagonalley_ext.put("/api/v1/zones/{zone_id}")
+async def api_diagonalley_zone_create(
+ data: Zones,
+ zone_id: str = Query(None),
+ wallet: WalletTypeInfo = Depends(get_key_type)
+ ):
+ if zone_id:
+ zone = await get_diagonalley_zone(zone_id)
+
+ if not zone:
+ return ({"message": "Zone does not exist."}))
+
+ if zone.wallet != walley.wallet.id:
+ return ({"message": "Not your record."}))
+
+ zone = await update_diagonalley_zone(zone_id, data)
+ else:
+ zone = await create_diagonalley_zone(wallet=wallet.wallet.id, data)
+
+ return ({**zone._asdict()}))
+
+
+@diagonalley_ext.delete("/api/v1/zones/{zone_id}")
+async def api_diagonalley_zone_delete(zone_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key)):
+ zone = await get_diagonalley_zone(zone_id)
+
+ if not zone:
+ return ({"message": "zone does not exist."})
+
+ if zone.wallet != wallet.wallet.id:
+ return ({"message": "Not your zone."})
+
+ await delete_diagonalley_zone(zone_id)
+
+ raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
+
+
+# # # Stalls
+
+
+@diagonalley_ext.get("/api/v1/stalls")
+async def api_diagonalley_stalls(wallet: WalletTypeInfo = Depends(get_key_type)):
+ wallet_ids = [wallet.wallet.id]
+
+ if "all_wallets" in request.args:
+ wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
+
+ return ([stall._asdict() for stall in await get_diagonalley_stalls(wallet_ids)])
+
+
+@diagonalley_ext.post("/api/v1/stalls")
+@diagonalley_ext.put("/api/v1/stalls/{stall_id}")
+async def api_diagonalley_stall_create(data: createStalls, stall_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)):
+
+ if stall_id:
+ stall = await get_diagonalley_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_diagonalley_stall(stall_id, data)
+ else:
+ stall = await create_diagonalley_stall(wallet_id=wallet.wallet.id, data)
+
+ return ({**stall._asdict()}))
+
+
+@diagonalley_ext.delete("/api/v1/stalls/{stall_id}")
+async def api_diagonalley_stall_delete(stall_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key)):
+ stall = await get_diagonalley_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_diagonalley_stall(stall_id)
+
+ raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
+
+
+###Orders
+
+
+@diagonalley_ext.get("/api/v1/orders")
+async def api_diagonalley_orders(wallet: WalletTypeInfo = Depends(get_key_type)):
+ wallet_ids = [wallet.wallet.id]
+
+ if "all_wallets" in request.args:
+ wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
+
+ try:
+ return ([order._asdict() for order in await get_diagonalley_orders(wallet_ids)])
+ except:
+ return ({"message": "We could not retrieve the orders."}))
+
+
+@diagonalley_ext.post("/api/v1/orders")
+
+async def api_diagonalley_order_create(data: createOrders, wallet: WalletTypeInfo = Depends(get_key_type)):
+ order = await create_diagonalley_order(wallet_id=wallet.wallet.id, data)
+ return ({**order._asdict()})
+
+
+@diagonalley_ext.delete("/api/v1/orders/{order_id}")
+async def api_diagonalley_order_delete(order_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)):
+ order = await get_diagonalley_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_diagonalley_order(order_id)
+
+ raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
+
+
+@diagonalley_ext.get("/api/v1/orders/paid/{order_id}")
+async def api_diagonalley_order_paid(order_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key)):
+ await db.execute(
+ "UPDATE diagonalley.orders SET paid = ? WHERE id = ?",
+ (
+ True,
+ order_id,
+ ),
+ )
+ return "", HTTPStatus.OK
+
+
+@diagonalley_ext.get("/api/v1/orders/shipped/{order_id}")
+async def api_diagonalley_order_shipped(order_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)):
+ await db.execute(
+ "UPDATE diagonalley.orders SET shipped = ? WHERE id = ?",
+ (
+ True,
+ order_id,
+ ),
+ )
+ order = await db.fetchone(
+ "SELECT * FROM diagonalley.orders WHERE id = ?", (order_id,)
+ )
+
+ return ([order._asdict() for order in get_diagonalley_orders(order["wallet"])]))
+
+
+###List products based on stall id
+
+
+@diagonalley_ext.get("/api/v1/stall/products/{stall_id}")
+async def api_diagonalley_stall_products(stall_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)):
+
+ rows = await db.fetchone(
+ "SELECT * FROM diagonalley.stalls WHERE id = ?", (stall_id,)
+ )
+ print(rows[1])
+ if not rows:
+ return ({"message": "Stall does not exist."})
+
+ products = db.fetchone(
+ "SELECT * FROM diagonalley.products WHERE wallet = ?", (rows[1],)
+ )
+ if not products:
+ return ({"message": "No products"})
+
+ return ([products._asdict() for products in await get_diagonalley_products(rows[1])])
+
+
+###Check a product has been shipped
+
+
+@diagonalley_ext.get("/api/v1/stall/checkshipped/{checking_id}")
+async def api_diagonalley_stall_checkshipped(checking_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)):
+ rows = await db.fetchone(
+ "SELECT * FROM diagonalley.orders WHERE invoiceid = ?", (checking_id,)
+ )
+ return ({"shipped": rows["shipped"]})
+
+
+###Place order
+
+
+@diagonalley_ext.post("/api/v1/stall/order/{stall_id}")
+async def api_diagonalley_stall_order(data:createOrders, wallet: WalletTypeInfo = Depends(get_key_type)):
+ product = await get_diagonalley_product(data.id)
+ shipping = await get_diagonalley_stall(stall_id)
+
+ if data.shippingzone == 1:
+ shippingcost = shipping.zone1cost
+ else:
+ shippingcost = shipping.zone2cost
+
+ checking_id, payment_request = await create_invoice(
+ wallet_id=product.wallet,
+ amount=shippingcost + (data.quantity * product.price),
+ memo=data.id,
+ )
+ selling_id = urlsafe_b64encode(uuid4().bytes_le).decode("utf-8")
+ await db.execute(
+ """
+ INSERT INTO diagonalley.orders (id, productid, wallet, product, quantity, shippingzone, address, email, invoiceid, paid, shipped)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ """,
+ (
+ selling_id,
+ data.id,
+ product.wallet,
+ product.product,
+ data.quantity,
+ data.shippingzone,
+ data.address,
+ data.email,
+ checking_id,
+ False,
+ False,
+ ),
+ )
+ return ({"checking_id": checking_id, "payment_request": payment_request}))
From 903e0d5b0b2f2f4ead59a3a769585c91bf8354c2 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 27 Jan 2022 14:58:58 +0000
Subject: [PATCH 02/30] products
---
lnbits/extensions/diagonalley/views_api.py | 75 ++++++++++++----------
1 file changed, 40 insertions(+), 35 deletions(-)
diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py
index 4de2799e..9df2c1d8 100644
--- a/lnbits/extensions/diagonalley/views_api.py
+++ b/lnbits/extensions/diagonalley/views_api.py
@@ -1,4 +1,6 @@
+from base64 import urlsafe_b64encode
from http import HTTPStatus
+from uuid import uuid4
from fastapi import Request
from fastapi.param_functions import Query
@@ -6,41 +8,44 @@ from fastapi.params import Depends
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
-from lnbits.decorators import api_check_wallet_key, api_validate_post_request
+from lnbits.core.services import create_invoice
+from lnbits.decorators import (
+ WalletTypeInfo,
+ api_check_wallet_key,
+ api_validate_post_request,
+ get_key_type,
+ require_admin_key,
+)
-from . import diagonalley_ext
+from . import db, diagonalley_ext
from .crud import (
- create_diagonalley_product,
- get_diagonalley_product,
- get_diagonalley_products,
- delete_diagonalley_product,
- create_diagonalley_zone,
- update_diagonalley_zone,
- get_diagonalley_zone,
- get_diagonalley_zones,
- delete_diagonalley_zone,
- create_diagonalley_stall,
- update_diagonalley_stall,
- get_diagonalley_stall,
- get_diagonalley_stalls,
- delete_diagonalley_stall,
create_diagonalley_order,
+ create_diagonalley_product,
+ create_diagonalley_stall,
+ create_diagonalley_zone,
+ delete_diagonalley_order,
+ delete_diagonalley_product,
+ delete_diagonalley_stall,
+ delete_diagonalley_zone,
get_diagonalley_order,
get_diagonalley_orders,
+ get_diagonalley_product,
+ get_diagonalley_products,
+ get_diagonalley_stall,
+ get_diagonalley_stalls,
+ get_diagonalley_zone,
+ get_diagonalley_zones,
update_diagonalley_product,
- delete_diagonalley_order,
+ update_diagonalley_stall,
+ update_diagonalley_zone,
)
-from lnbits.core.services import create_invoice
-from base64 import urlsafe_b64encode
-from uuid import uuid4
+from .models import Orders, Products, Stalls
# from lnbits.db import open_ext_db
-from . import db
-from .models import Products, Orders, Stalls
### Products
-
+"""
@copilot_ext.get("/api/v1/copilot/{copilot_id}")
async def api_copilot_retrieve(
req: Request,
@@ -55,26 +60,27 @@ async def api_copilot_retrieve(
if not copilot.lnurl_toggle:
return copilot.dict()
return {**copilot.dict(), **{"lnurl": copilot.lnurl(req)}}
-
+"""
@diagonalley_ext.get("/api/v1/products")
async def api_diagonalley_products(
req: Request,
wallet: WalletTypeInfo = Depends(get_key_type),
+ all_stalls: bool = Query(False)
):
wallet_ids = [wallet.wallet.id]
- if "all_stalls" in request.args:
+ if all_stalls:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
- return ([product._asdict() for product in await get_diagonalley_products(wallet_ids)])
+ return ([product.dict() for product in await get_diagonalley_products(wallet_ids)])
@diagonalley_ext.post("/api/v1/products")
@diagonalley_ext.put("/api/v1/products/{product_id}")
async def api_diagonalley_product_create(
data: Products,
- product_id=: str = Query(None),
+ product_id: str = Query(None),
wallet: WalletTypeInfo = Depends(get_key_type)
):
@@ -82,19 +88,19 @@ async def api_diagonalley_product_create(
product = await get_diagonalley_product(product_id)
if not product:
- return ({"message": "Withdraw product does not exist."}))
+ return ({"message": "Withdraw product does not exist."})
if product.wallet != wallet.wallet.id:
- return ({"message": "Not your withdraw product."}))
+ return ({"message": "Not your withdraw product."})
product = await update_diagonalley_product(product_id, data)
else:
product = await create_diagonalley_product(wallet_id=wallet.wallet.id, data)
- return ({**product._asdict()}))
+ return product.dict()
-@diagonalley_ext.route("/api/v1/products/{product_id}")
+@diagonalley_ext.delete("/api/v1/products/{product_id}")
async def api_diagonalley_products_delete(product_id, wallet: WalletTypeInfo = Depends(require_admin_key)):
product = await get_diagonalley_product(product_id)
@@ -105,7 +111,6 @@ async def api_diagonalley_products_delete(product_id, wallet: WalletTypeInfo = D
return ({"message": "Not your Diagon Alley."})
await delete_diagonalley_product(product_id)
-
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
@@ -113,13 +118,13 @@ async def api_diagonalley_products_delete(product_id, wallet: WalletTypeInfo = D
@diagonalley_ext.get("/api/v1/zones")
-async def api_diagonalley_zones(wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_diagonalley_zones(wallet: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False)):
wallet_ids = [wallet.wallet.id]
- if "all_wallets" in request.args:
+ if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
- return ([zone._asdict() for zone in await get_diagonalley_zones(wallet_ids)]))
+ return ([zone.dict() for zone in await get_diagonalley_zones(wallet_ids)])
@diagonalley_ext.post("/api/v1/zones")
From 8db457bd423b000d123e91f0c031b02bea0e8d69 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 27 Jan 2022 15:26:55 +0000
Subject: [PATCH 03/30] zones
---
lnbits/extensions/diagonalley/crud.py | 63 +++++++++-------------
lnbits/extensions/diagonalley/models.py | 41 +++++++++-----
lnbits/extensions/diagonalley/views_api.py | 26 +++++----
3 files changed, 64 insertions(+), 66 deletions(-)
diff --git a/lnbits/extensions/diagonalley/crud.py b/lnbits/extensions/diagonalley/crud.py
index c6ce8222..7dc02cd4 100644
--- a/lnbits/extensions/diagonalley/crud.py
+++ b/lnbits/extensions/diagonalley/crud.py
@@ -1,17 +1,17 @@
+import re
from base64 import urlsafe_b64encode
-from uuid import uuid4
from typing import List, Optional, Union
+from uuid import uuid4
-from lnbits.settings import WALLET
+import httpx
# from lnbits.db import open_ext_db
from lnbits.db import SQLITE
-from . import db
-from .models import Products, Orders, Stalls, Zones
-
-import httpx
from lnbits.helpers import urlsafe_short_hash
-import re
+from lnbits.settings import WALLET
+
+from . import db
+from .models import Orders, Products, Stalls, Zones, createProduct, createZones
regex = re.compile(
r"^(?:http|ftp)s?://" # http:// or https://
@@ -28,35 +28,27 @@ regex = re.compile(
async def create_diagonalley_product(
- *,
- stall_id: str,
- product: str,
- categories: str,
- description: str,
- image: Optional[str] = None,
- price: int,
- quantity: int,
- shippingzones: str,
+ data: createProduct
) -> Products:
- returning = "" if db.type == SQLITE else "RETURNING ID"
- method = db.execute if db.type == SQLITE else db.fetchone
+ # returning = "" if db.type == SQLITE else "RETURNING ID"
+ # method = db.execute if db.type == SQLITE else db.fetchone
product_id = urlsafe_short_hash()
# with open_ext_db("diagonalley") as db:
- result = await (method)(
+ # result = await (method)(
+ await db.execute(
f"""
- INSERT INTO diagonalley.products (id, stall, product, categories, description, image, price, quantity, shippingzones)
+ INSERT INTO diagonalley.products (id, stall, product, categories, description, image, price, quantity)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
- {returning}
""",
(
product_id,
- stall_id,
- product,
- categories,
- description,
- image,
- price,
- quantity,
+ data.stall,
+ data.product,
+ data.categories,
+ data.description,
+ data.image,
+ data.price,
+ data.quantity,
),
)
product = await get_diagonalley_product(product_id)
@@ -109,17 +101,11 @@ async def delete_diagonalley_product(product_id: str) -> None:
async def create_diagonalley_zone(
- *,
- wallet: Optional[str] = None,
- cost: Optional[int] = 0,
- countries: Optional[str] = None,
+ wallet,
+ data: createZones
) -> Zones:
-
- returning = "" if db.type == SQLITE else "RETURNING ID"
- method = db.execute if db.type == SQLITE else db.fetchone
-
zone_id = urlsafe_short_hash()
- result = await (method)(
+ await db.execute(
f"""
INSERT INTO diagonalley.zones (
id,
@@ -129,9 +115,8 @@ async def create_diagonalley_zone(
)
VALUES (?, ?, ?, ?)
- {returning}
""",
- (zone_id, wallet, cost, countries),
+ (zone_id, wallet, data.cost, data.countries),
)
zone = await get_diagonalley_zone(zone_id)
diff --git a/lnbits/extensions/diagonalley/models.py b/lnbits/extensions/diagonalley/models.py
index 0f2a1d78..bd667a2f 100644
--- a/lnbits/extensions/diagonalley/models.py
+++ b/lnbits/extensions/diagonalley/models.py
@@ -1,12 +1,15 @@
-from urllib.parse import urlparse, urlunparse, parse_qs, urlencode, ParseResult
-from starlette.requests import Request
+import json
+from lib2to3.pytree import Base
+from sqlite3 import Row
+from typing import Dict, Optional
+from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
+
from fastapi.param_functions import Query
-from typing import Optional, Dict
-from lnbits.lnurl import encode as lnurl_encode # type: ignore
from lnurl.types import LnurlPayMetadata # type: ignore
from pydantic import BaseModel
-import json
-from sqlite3 import Row
+from starlette.requests import Request
+
+from lnbits.lnurl import encode as lnurl_encode # type: ignore
class Stalls(BaseModel):
@@ -25,23 +28,35 @@ class createStalls(BaseModel):
relays: str = Query(None)
shippingzones: str = Query(None)
-class Products(BaseModel):
- id: str = Query(None)
+class createProduct(BaseModel):
stall: str = Query(None)
product: str = Query(None)
categories: str = Query(None)
description: str = Query(None)
image: str = Query(None)
- price: int = Query(0)
- quantity: int = Query(0)
+ price: int = Query(0, ge=0)
+ quantity: int = Query(0, ge=0)
+class Products(BaseModel):
+ id: str
+ stall: str
+ product: str
+ categories: str
+ description: str
+ image: str
+ price: int
+ quantity: int
-class Zones(BaseModel):
- id: str = Query(None)
- wallet: str = Query(None)
+class createZones(BaseModel):
cost: str = Query(None)
countries: str = Query(None)
+class Zones(BaseModel):
+ id: str
+ wallet: str
+ cost: str
+ countries: str
+
class Orders(BaseModel):
id: str = Query(None)
diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py
index 9df2c1d8..1ad83936 100644
--- a/lnbits/extensions/diagonalley/views_api.py
+++ b/lnbits/extensions/diagonalley/views_api.py
@@ -39,7 +39,7 @@ from .crud import (
update_diagonalley_stall,
update_diagonalley_zone,
)
-from .models import Orders, Products, Stalls
+from .models import Orders, Products, Stalls, Zones, createProduct, createZones
# from lnbits.db import open_ext_db
@@ -79,7 +79,7 @@ async def api_diagonalley_products(
@diagonalley_ext.post("/api/v1/products")
@diagonalley_ext.put("/api/v1/products/{product_id}")
async def api_diagonalley_product_create(
- data: Products,
+ data: createProduct,
product_id: str = Query(None),
wallet: WalletTypeInfo = Depends(get_key_type)
):
@@ -93,9 +93,9 @@ async def api_diagonalley_product_create(
if product.wallet != wallet.wallet.id:
return ({"message": "Not your withdraw product."})
- product = await update_diagonalley_product(product_id, data)
+ product = await update_diagonalley_product(product_id, **data.dict())
else:
- product = await create_diagonalley_product(wallet_id=wallet.wallet.id, data)
+ product = await create_diagonalley_product(data=data)
return product.dict()
@@ -126,11 +126,10 @@ async def api_diagonalley_zones(wallet: WalletTypeInfo = Depends(get_key_type),
return ([zone.dict() for zone in await get_diagonalley_zones(wallet_ids)])
-
@diagonalley_ext.post("/api/v1/zones")
@diagonalley_ext.put("/api/v1/zones/{zone_id}")
async def api_diagonalley_zone_create(
- data: Zones,
+ data: createZones,
zone_id: str = Query(None),
wallet: WalletTypeInfo = Depends(get_key_type)
):
@@ -138,20 +137,20 @@ async def api_diagonalley_zone_create(
zone = await get_diagonalley_zone(zone_id)
if not zone:
- return ({"message": "Zone does not exist."}))
+ return ({"message": "Zone does not exist."})
- if zone.wallet != walley.wallet.id:
- return ({"message": "Not your record."}))
+ if zone.wallet != wallet.wallet.id:
+ return ({"message": "Not your record."})
- zone = await update_diagonalley_zone(zone_id, data)
+ zone = await update_diagonalley_zone(zone_id, **data.dict())
else:
- zone = await create_diagonalley_zone(wallet=wallet.wallet.id, data)
+ zone = await create_diagonalley_zone(wallet=wallet.wallet.id, data=data)
- return ({**zone._asdict()}))
+ return zone.dict()
@diagonalley_ext.delete("/api/v1/zones/{zone_id}")
-async def api_diagonalley_zone_delete(zone_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key)):
+async def api_diagonalley_zone_delete(zone_id, wallet: WalletTypeInfo = Depends(require_admin_key)):
zone = await get_diagonalley_zone(zone_id)
if not zone:
@@ -161,7 +160,6 @@ async def api_diagonalley_zone_delete(zone_id: str = Query(None), wallet: Walle
return ({"message": "Not your zone."})
await delete_diagonalley_zone(zone_id)
-
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
From d6d33c6a22c1e5ed9c336ec1337018407eb1562e Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Thu, 27 Jan 2022 16:18:12 +0000
Subject: [PATCH 04/30] stalls
---
lnbits/extensions/diagonalley/crud.py | 34 ++++++++++++----------
lnbits/extensions/diagonalley/models.py | 25 ++++++++--------
lnbits/extensions/diagonalley/views_api.py | 29 +++++++++++-------
3 files changed, 50 insertions(+), 38 deletions(-)
diff --git a/lnbits/extensions/diagonalley/crud.py b/lnbits/extensions/diagonalley/crud.py
index 7dc02cd4..3b58b129 100644
--- a/lnbits/extensions/diagonalley/crud.py
+++ b/lnbits/extensions/diagonalley/crud.py
@@ -11,7 +11,15 @@ from lnbits.helpers import urlsafe_short_hash
from lnbits.settings import WALLET
from . import db
-from .models import Orders, Products, Stalls, Zones, createProduct, createZones
+from .models import (
+ Orders,
+ Products,
+ Stalls,
+ Zones,
+ createProduct,
+ createStalls,
+ createZones,
+)
regex = re.compile(
r"^(?:http|ftp)s?://" # http:// or https://
@@ -185,20 +193,10 @@ async def delete_diagonalley_zone(zone_id: str) -> None:
async def create_diagonalley_stall(
- *,
- wallet: str,
- name: str,
- publickey: str,
- privatekey: str,
- relays: str,
- shippingzones: str,
+ data: createStalls
) -> Stalls:
-
- returning = "" if db.type == SQLITE else "RETURNING ID"
- method = db.execute if db.type == SQLITE else db.fetchone
-
stall_id = urlsafe_short_hash()
- result = await (method)(
+ await db.execute(
f"""
INSERT INTO diagonalley.stalls (
id,
@@ -210,9 +208,15 @@ async def create_diagonalley_stall(
shippingzones
)
VALUES (?, ?, ?, ?, ?, ?, ?)
- {returning}
""",
- (stall_id, wallet, name, publickey, privatekey, relays, shippingzones),
+ (
+ stall_id,
+ data.wallet,
+ data.name,
+ data.publickey,
+ data.privatekey,
+ data.relays,
+ data.shippingzones),
)
stall = await get_diagonalley_stall(stall_id)
diff --git a/lnbits/extensions/diagonalley/models.py b/lnbits/extensions/diagonalley/models.py
index bd667a2f..4c674c8a 100644
--- a/lnbits/extensions/diagonalley/models.py
+++ b/lnbits/extensions/diagonalley/models.py
@@ -13,20 +13,21 @@ from lnbits.lnurl import encode as lnurl_encode # type: ignore
class Stalls(BaseModel):
- id: str = Query(None)
- wallet: str = Query(None)
- name: str = Query(None)
- publickey: str = Query(None)
- privatekey: str = Query(None)
- relays: str = Query(None)
+ id: str
+ wallet: str
+ name: str
+ publickey: str
+ privatekey: str
+ relays: str
+ shippingzones: str
class createStalls(BaseModel):
- wallet: str = Query(None)
- name: str = Query(None)
- publickey: str = Query(None)
- privatekey: str = Query(None)
- relays: str = Query(None)
- shippingzones: str = Query(None)
+ wallet: str = Query(...)
+ name: str = Query(...)
+ publickey: str = Query(...)
+ privatekey: str = Query(...)
+ relays: str = Query(...)
+ shippingzones: str = Query(...)
class createProduct(BaseModel):
stall: str = Query(None)
diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py
index 1ad83936..ccac9b24 100644
--- a/lnbits/extensions/diagonalley/views_api.py
+++ b/lnbits/extensions/diagonalley/views_api.py
@@ -39,7 +39,15 @@ from .crud import (
update_diagonalley_stall,
update_diagonalley_zone,
)
-from .models import Orders, Products, Stalls, Zones, createProduct, createZones
+from .models import (
+ Orders,
+ Products,
+ Stalls,
+ Zones,
+ createProduct,
+ createStalls,
+ createZones,
+)
# from lnbits.db import open_ext_db
@@ -167,33 +175,33 @@ async def api_diagonalley_zone_delete(zone_id, wallet: WalletTypeInfo = Depends(
@diagonalley_ext.get("/api/v1/stalls")
-async def api_diagonalley_stalls(wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_diagonalley_stalls(wallet: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False)):
wallet_ids = [wallet.wallet.id]
- if "all_wallets" in request.args:
+ if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
- return ([stall._asdict() for stall in await get_diagonalley_stalls(wallet_ids)])
+ return ([stall.dict() for stall in await get_diagonalley_stalls(wallet_ids)])
@diagonalley_ext.post("/api/v1/stalls")
@diagonalley_ext.put("/api/v1/stalls/{stall_id}")
-async def api_diagonalley_stall_create(data: createStalls, stall_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_diagonalley_stall_create(data: createStalls, stall_id = None, wallet: WalletTypeInfo = Depends(get_key_type)):
if stall_id:
stall = await get_diagonalley_stall(stall_id)
if not stall:
- return ({"message": "Withdraw stall does not exist."}))
+ return ({"message": "Withdraw stall does not exist."})
if stall.wallet != wallet.wallet.id:
- return ({"message": "Not your withdraw stall."}))
+ return ({"message": "Not your withdraw stall."})
- stall = await update_diagonalley_stall(stall_id, data)
+ stall = await update_diagonalley_stall(stall_id, **data.dict())
else:
- stall = await create_diagonalley_stall(wallet_id=wallet.wallet.id, data)
+ stall = await create_diagonalley_stall(data=data)
- return ({**stall._asdict()}))
+ return stall.dict()
@diagonalley_ext.delete("/api/v1/stalls/{stall_id}")
@@ -207,7 +215,6 @@ async def api_diagonalley_stall_delete(stall_id: str = Query(None), wallet: Wall
return ({"message": "Not your Stall."})
await delete_diagonalley_stall(stall_id)
-
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
From cad896f909645e2dedfd31e45f5494648aebb7e3 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Fri, 28 Jan 2022 15:11:31 +0000
Subject: [PATCH 05/30] orders
---
lnbits/extensions/diagonalley/crud.py | 62 +++++++++-------------
lnbits/extensions/diagonalley/models.py | 28 ++++++----
lnbits/extensions/diagonalley/views_api.py | 42 +++++++--------
3 files changed, 65 insertions(+), 67 deletions(-)
diff --git a/lnbits/extensions/diagonalley/crud.py b/lnbits/extensions/diagonalley/crud.py
index 3b58b129..4cf14014 100644
--- a/lnbits/extensions/diagonalley/crud.py
+++ b/lnbits/extensions/diagonalley/crud.py
@@ -16,6 +16,7 @@ from .models import (
Products,
Stalls,
Zones,
+ createOrder,
createProduct,
createStalls,
createZones,
@@ -83,7 +84,7 @@ async def get_diagonalley_product(product_id: str) -> Optional[Products]:
row = await db.fetchone(
"SELECT * FROM diagonalley.products WHERE id = ?", (product_id,)
)
- return Products.from_row(row) if row else None
+ return Products(**row) if row else None
async def get_diagonalley_products(wallet_ids: Union[str, List[str]]) -> List[Products]:
@@ -98,7 +99,7 @@ async def get_diagonalley_products(wallet_ids: Union[str, List[str]]) -> List[Pr
""",
(*wallet_ids,),
)
- return [Products.from_row(row) for row in rows]
+ return [Products(**row) for row in rows]
async def delete_diagonalley_product(product_id: str) -> None:
@@ -139,12 +140,12 @@ async def update_diagonalley_zone(zone_id: str, **kwargs) -> Optional[Zones]:
(*kwargs.values(), zone_id),
)
row = await db.fetchone("SELECT * FROM diagonalley.zones WHERE id = ?", (zone_id,))
- return Zones.from_row(row) if row else None
+ return Zones(**row) if row else None
async def get_diagonalley_zone(zone_id: str) -> Optional[Zones]:
row = await db.fetchone("SELECT * FROM diagonalley.zones WHERE id = ?", (zone_id,))
- return Zones.from_row(row) if row else None
+ return Zones(**row) if row else None
async def get_diagonalley_zones(wallet_ids: Union[str, List[str]]) -> List[Zones]:
@@ -182,7 +183,7 @@ async def get_diagonalley_zones(wallet_ids: Union[str, List[str]]) -> List[Zones
rows = await db.fetchall(
f"SELECT * FROM diagonalley.zones WHERE wallet IN ({q})", (*wallet_ids,)
)
- return [Zones.from_row(row) for row in rows]
+ return [Zones(**row) for row in rows]
async def delete_diagonalley_zone(zone_id: str) -> None:
@@ -233,7 +234,7 @@ async def update_diagonalley_stall(stall_id: str, **kwargs) -> Optional[Stalls]:
row = await db.fetchone(
"SELECT * FROM diagonalley.stalls WHERE id = ?", (stall_id,)
)
- return Stalls.from_row(row) if row else None
+ return Stalls(**row) if row else None
async def get_diagonalley_stall(stall_id: str) -> Optional[Stalls]:
@@ -266,7 +267,7 @@ async def get_diagonalley_stall(stall_id: str) -> Optional[Stalls]:
row = await db.fetchone(
"SELECT * FROM diagonalley.stalls WHERE id = ?", (stall_id,)
)
- return Stalls.from_row(row) if row else None
+ return Stalls(**row) if row else None
async def get_diagonalley_stalls(wallet_ids: Union[str, List[str]]) -> List[Stalls]:
@@ -303,7 +304,7 @@ async def get_diagonalley_stalls(wallet_ids: Union[str, List[str]]) -> List[Stal
rows = await db.fetchall(
f"SELECT * FROM diagonalley.stalls WHERE wallet IN ({q})", (*wallet_ids,)
)
- return [Stalls.from_row(row) for row in rows]
+ return [Stalls(**row) for row in rows]
async def delete_diagonalley_stall(stall_id: str) -> None:
@@ -314,47 +315,34 @@ async def delete_diagonalley_stall(stall_id: str) -> None:
async def create_diagonalley_order(
- *,
- productid: str,
- wallet: str,
- product: str,
- quantity: int,
- shippingzone: str,
- address: str,
- email: str,
- invoiceid: str,
- paid: bool,
- shipped: bool,
+ data: createOrder
) -> Orders:
- returning = "" if db.type == SQLITE else "RETURNING ID"
- method = db.execute if db.type == SQLITE else db.fetchone
order_id = urlsafe_short_hash()
- result = await (method)(
+ await db.execute(
f"""
INSERT INTO diagonalley.orders (id, productid, wallet, product,
quantity, shippingzone, address, email, invoiceid, paid, shipped)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- {returning}
""",
(
order_id,
- productid,
- wallet,
- product,
- quantity,
- shippingzone,
- address,
- email,
- invoiceid,
+ data.productid,
+ data.wallet,
+ data.product,
+ data.quantity,
+ data.shippingzone,
+ data.address,
+ data.email,
+ data.invoiceid,
False,
False,
),
)
- if db.type == SQLITE:
- order_id = result._result_proxy.lastrowid
- else:
- order_id = result[0]
+ # if db.type == SQLITE:
+ # order_id = result._result_proxy.lastrowid
+ # else:
+ # order_id = result[0]
link = await get_diagonalley_order(order_id)
assert link, "Newly created link couldn't be retrieved"
@@ -365,7 +353,7 @@ async def get_diagonalley_order(order_id: str) -> Optional[Orders]:
row = await db.fetchone(
"SELECT * FROM diagonalley.orders WHERE id = ?", (order_id,)
)
- return Orders.from_row(row) if row else None
+ return Orders(**row) if row else None
async def get_diagonalley_orders(wallet_ids: Union[str, List[str]]) -> List[Orders]:
@@ -377,7 +365,7 @@ async def get_diagonalley_orders(wallet_ids: Union[str, List[str]]) -> List[Orde
f"SELECT * FROM diagonalley.orders WHERE wallet IN ({q})", (*wallet_ids,)
)
#
- return [Orders.from_row(row) for row in rows]
+ return [Orders(**row) for row in rows]
async def delete_diagonalley_order(order_id: str) -> None:
diff --git a/lnbits/extensions/diagonalley/models.py b/lnbits/extensions/diagonalley/models.py
index 4c674c8a..1a975e10 100644
--- a/lnbits/extensions/diagonalley/models.py
+++ b/lnbits/extensions/diagonalley/models.py
@@ -59,15 +59,25 @@ class Zones(BaseModel):
countries: str
+class createOrder(BaseModel):
+ productid: str = Query(...)
+ stall: str = Query(...)
+ product: str = Query(...)
+ quantity: int = Query(..., ge=1)
+ shippingzone: int = Query(...)
+ address: str = Query(...)
+ email: str = Query(...)
+ invoiceid: str = Query(...)
+
class Orders(BaseModel):
- id: str = Query(None)
- productid: str = Query(None)
- stall: str = Query(None)
- product: str = Query(None)
- quantity: int = Query(0)
- shippingzone: int = Query(0)
- address: str = Query(None)
- email: str = Query(None)
- invoiceid: str = Query(None)
+ id: str
+ productid: str
+ stall: str
+ product: str
+ quantity: int
+ shippingzone: int
+ address: str
+ email: str
+ invoiceid: str
paid: bool
shipped: bool
diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py
index ccac9b24..165d2a0c 100644
--- a/lnbits/extensions/diagonalley/views_api.py
+++ b/lnbits/extensions/diagonalley/views_api.py
@@ -44,6 +44,7 @@ from .models import (
Products,
Stalls,
Zones,
+ createOrder,
createProduct,
createStalls,
createZones,
@@ -87,8 +88,8 @@ async def api_diagonalley_products(
@diagonalley_ext.post("/api/v1/products")
@diagonalley_ext.put("/api/v1/products/{product_id}")
async def api_diagonalley_product_create(
+ product_id,
data: createProduct,
- product_id: str = Query(None),
wallet: WalletTypeInfo = Depends(get_key_type)
):
@@ -186,7 +187,7 @@ async def api_diagonalley_stalls(wallet: WalletTypeInfo = Depends(get_key_type),
@diagonalley_ext.post("/api/v1/stalls")
@diagonalley_ext.put("/api/v1/stalls/{stall_id}")
-async def api_diagonalley_stall_create(data: createStalls, stall_id = None, wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_diagonalley_stall_create(data: createStalls, stall_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)):
if stall_id:
stall = await get_diagonalley_stall(stall_id)
@@ -222,23 +223,22 @@ async def api_diagonalley_stall_delete(stall_id: str = Query(None), wallet: Wall
@diagonalley_ext.get("/api/v1/orders")
-async def api_diagonalley_orders(wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_diagonalley_orders(wallet: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False)):
wallet_ids = [wallet.wallet.id]
- if "all_wallets" in request.args:
+ if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
try:
- return ([order._asdict() for order in await get_diagonalley_orders(wallet_ids)])
+ return ([order.dict() for order in await get_diagonalley_orders(wallet_ids)])
except:
- return ({"message": "We could not retrieve the orders."}))
+ return ({"message": "We could not retrieve the orders."})
@diagonalley_ext.post("/api/v1/orders")
-
-async def api_diagonalley_order_create(data: createOrders, wallet: WalletTypeInfo = Depends(get_key_type)):
- order = await create_diagonalley_order(wallet_id=wallet.wallet.id, data)
- return ({**order._asdict()})
+async def api_diagonalley_order_create(data: createOrder, wallet: WalletTypeInfo = Depends(get_key_type)):
+ order = await create_diagonalley_order(wallet_id=wallet.wallet.id, data=data)
+ return order.dict()
@diagonalley_ext.delete("/api/v1/orders/{order_id}")
@@ -281,7 +281,7 @@ async def api_diagonalley_order_shipped(order_id: str = Query(None), wallet: Wal
"SELECT * FROM diagonalley.orders WHERE id = ?", (order_id,)
)
- return ([order._asdict() for order in get_diagonalley_orders(order["wallet"])]))
+ return ([order.dict() for order in get_diagonalley_orders(order["wallet"])])
###List products based on stall id
@@ -303,14 +303,14 @@ async def api_diagonalley_stall_products(stall_id: str = Query(None), wallet: Wa
if not products:
return ({"message": "No products"})
- return ([products._asdict() for products in await get_diagonalley_products(rows[1])])
+ return ([products.dict() for products in await get_diagonalley_products(rows[1])])
###Check a product has been shipped
@diagonalley_ext.get("/api/v1/stall/checkshipped/{checking_id}")
-async def api_diagonalley_stall_checkshipped(checking_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_diagonalley_stall_checkshipped(checking_id, wallet: WalletTypeInfo = Depends(get_key_type)):
rows = await db.fetchone(
"SELECT * FROM diagonalley.orders WHERE invoiceid = ?", (checking_id,)
)
@@ -321,19 +321,19 @@ async def api_diagonalley_stall_checkshipped(checking_id: str = Query(None), wal
@diagonalley_ext.post("/api/v1/stall/order/{stall_id}")
-async def api_diagonalley_stall_order(data:createOrders, wallet: WalletTypeInfo = Depends(get_key_type)):
- product = await get_diagonalley_product(data.id)
+async def api_diagonalley_stall_order(stall_id, data: createOrder, wallet: WalletTypeInfo = Depends(get_key_type)):
+ product = await get_diagonalley_product(data.productid)
shipping = await get_diagonalley_stall(stall_id)
if data.shippingzone == 1:
- shippingcost = shipping.zone1cost
+ shippingcost = shipping.zone1cost #missing in model
else:
- shippingcost = shipping.zone2cost
+ shippingcost = shipping.zone2cost #missing in model
checking_id, payment_request = await create_invoice(
wallet_id=product.wallet,
amount=shippingcost + (data.quantity * product.price),
- memo=data.id,
+ memo=shipping.wallet,
)
selling_id = urlsafe_b64encode(uuid4().bytes_le).decode("utf-8")
await db.execute(
@@ -343,8 +343,8 @@ async def api_diagonalley_stall_order(data:createOrders, wallet: WalletTypeInfo
""",
(
selling_id,
- data.id,
- product.wallet,
+ data.productid,
+ product.wallet, #doesn't exist in model
product.product,
data.quantity,
data.shippingzone,
@@ -355,4 +355,4 @@ async def api_diagonalley_stall_order(data:createOrders, wallet: WalletTypeInfo
False,
),
)
- return ({"checking_id": checking_id, "payment_request": payment_request}))
+ return ({"checking_id": checking_id, "payment_request": payment_request})
From 5048080ff671799a363e19c055ffa443c23c8f03 Mon Sep 17 00:00:00 2001
From: Tiago vasconcelos
Date: Fri, 28 Jan 2022 16:22:54 +0000
Subject: [PATCH 06/30] UI fires up
---
lnbits/extensions/diagonalley/__init__.py | 35 +++++++++++---
lnbits/extensions/diagonalley/tasks.py | 6 +--
lnbits/extensions/diagonalley/views.py | 55 +++++++++-------------
lnbits/extensions/diagonalley/views_api.py | 8 +---
4 files changed, 55 insertions(+), 49 deletions(-)
diff --git a/lnbits/extensions/diagonalley/__init__.py b/lnbits/extensions/diagonalley/__init__.py
index 720c55c8..cab65685 100644
--- a/lnbits/extensions/diagonalley/__init__.py
+++ b/lnbits/extensions/diagonalley/__init__.py
@@ -1,16 +1,37 @@
-from quart import Blueprint
+import asyncio
+
+from fastapi import APIRouter
+from fastapi.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_diagonalley")
-diagonalley_ext: Blueprint = Blueprint(
- "diagonalley", __name__, static_folder="static", template_folder="templates"
+diagonalley_static_files = [
+ {
+ "path": "/diagonalley/static",
+ "app": StaticFiles(directory="lnbits/extensions/diagonalley/static"),
+ "name": "diagonalley_static",
+ }
+]
+
+diagonalley_ext: APIRouter = APIRouter(
+ prefix="/diagonalley", tags=["diagonalley"]
+ # "diagonalley", __name__, static_folder="static", template_folder="templates"
)
-from .views_api import * # noqa
+def diagonalley_renderer():
+ return template_renderer(["lnbits/extensions/diagonalley/templates"])
+
+
+from .tasks import wait_for_paid_invoices
from .views import * # noqa
+from .views_api import * # noqa
-from .tasks import register_listeners
-from lnbits.tasks import record_async
-diagonalley_ext.record(record_async(register_listeners))
+def diagonalley_start():
+ loop = asyncio.get_event_loop()
+ loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
+
diff --git a/lnbits/extensions/diagonalley/tasks.py b/lnbits/extensions/diagonalley/tasks.py
index 3fee63d9..bcbb7025 100644
--- a/lnbits/extensions/diagonalley/tasks.py
+++ b/lnbits/extensions/diagonalley/tasks.py
@@ -3,8 +3,6 @@ import asyncio
from lnbits.core.models import Payment
from lnbits.tasks import register_invoice_listener
-from .crud import get_ticket, set_ticket_paid
-
async def wait_for_paid_invoices():
invoice_queue = asyncio.Queue()
@@ -16,6 +14,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None:
+ """
if "lnticket" != payment.extra.get("tag"):
# not a lnticket invoice
return
@@ -26,4 +25,5 @@ async def on_invoice_paid(payment: Payment) -> None:
return
await payment.set_pending(False)
- await set_ticket_paid(payment.payment_hash)
\ No newline at end of file
+ await set_ticket_paid(payment.payment_hash)
+ """
diff --git a/lnbits/extensions/diagonalley/views.py b/lnbits/extensions/diagonalley/views.py
index 2deed72b..ae0899ca 100644
--- a/lnbits/extensions/diagonalley/views.py
+++ b/lnbits/extensions/diagonalley/views.py
@@ -1,44 +1,35 @@
-from typing import List
-
-from fastapi import Request, WebSocket, WebSocketDisconnect
-from fastapi.params import Depends
-from fastapi.templating import Jinja2Templates
-from starlette.responses import HTMLResponse # type: ignore
-
from http import HTTPStatus
-import json
-from lnbits.decorators import check_user_exists, validate_uuids
-from lnbits.extensions.diagonalley import diagonalley_ext
-from .crud import (
- create_diagonalley_product,
- get_diagonalley_product,
- get_diagonalley_products,
- delete_diagonalley_product,
- create_diagonalley_order,
- get_diagonalley_order,
- get_diagonalley_orders,
- update_diagonalley_product,
-)
+from fastapi import Request
+from fastapi.params import Depends
+from fastapi.templating import Jinja2Templates
+from starlette.exceptions import HTTPException
+from starlette.responses import HTMLResponse
+from lnbits.core.models import User
+from lnbits.decorators import check_user_exists # type: ignore
+from lnbits.extensions.diagonalley import diagonalley_ext, diagonalley_renderer
+
+from .crud import get_diagonalley_products
+
+templates = Jinja2Templates(directory="templates")
@diagonalley_ext.get("/", response_class=HTMLResponse)
-@validate_uuids(["usr"], required=True)
-@check_user_exists(request: Request)
-async def index():
- return await render_template("diagonalley/index.html", user=g.user)
+async def index(request: Request, user: User = Depends(check_user_exists)):
+ return diagonalley_renderer().TemplateResponse(
+ "diagonalley/index.html", {"request": request, "user": user.dict()}
+ )
-
-@diagonalley_ext.get("/", response_class=HTMLResponse)
+@diagonalley_ext.get("/{stall_id}", response_class=HTMLResponse)
async def display(request: Request, stall_id):
product = await get_diagonalley_products(stall_id)
+
if not product:
- abort(HTTPStatus.NOT_FOUND, "Stall does not exist.")
-
- return await render_template(
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="Stall does not exist."
+ )
+ return diagonalley_renderer().TemplateResponse(
"diagonalley/stall.html",
- stall=json.dumps(
- [product._asdict() for product in await get_diagonalley_products(stall_id)]
- ),
+ {"stall": [product.dict() for product in await get_diagonalley_products(stall_id)]}
)
diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py
index 165d2a0c..43232841 100644
--- a/lnbits/extensions/diagonalley/views_api.py
+++ b/lnbits/extensions/diagonalley/views_api.py
@@ -9,13 +9,7 @@ from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
from lnbits.core.services import create_invoice
-from lnbits.decorators import (
- WalletTypeInfo,
- api_check_wallet_key,
- api_validate_post_request,
- get_key_type,
- require_admin_key,
-)
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import db, diagonalley_ext
from .crud import (
From 301135d0064d378843ad997124fcfbb169520e0c Mon Sep 17 00:00:00 2001
From: benarc
Date: Thu, 3 Feb 2022 21:19:24 +0000
Subject: [PATCH 07/30] Message ui working
---
.../extensions/diagonalley/static/js/index.js | 83 ++++++++++++-------
.../templates/diagonalley/index.html | 47 ++++++-----
2 files changed, 78 insertions(+), 52 deletions(-)
diff --git a/lnbits/extensions/diagonalley/static/js/index.js b/lnbits/extensions/diagonalley/static/js/index.js
index 1a25edaa..af81db2a 100644
--- a/lnbits/extensions/diagonalley/static/js/index.js
+++ b/lnbits/extensions/diagonalley/static/js/index.js
@@ -2,7 +2,24 @@
Vue.component(VueQrcode.name, VueQrcode)
-const pica = window.pica()
+//const pica = window.pica()
+
+var mapStalls = obj => {
+ obj._data = _.clone(obj)
+ return obj
+}
+var mapProducts = obj => {
+ obj._data = _.clone(obj)
+ return obj
+}
+var mapZone = obj => {
+ obj._data = _.clone(obj)
+ return obj
+}
+var mapOrders = obj => {
+ obj._data = _.clone(obj)
+ return obj
+}
new Vue({
el: '#vue',
@@ -13,6 +30,9 @@ new Vue({
orders: [],
stalls: [],
zones: [],
+ customerKeys: [],
+ customerKey: '',
+ customerMessages: {},
shippedModel: false,
shippingZoneOptions: [
'Australia',
@@ -64,7 +84,8 @@ new Vue({
'Groceries (Food and Drink)',
'Technology (Phones and Computers)',
'Home (furniture and accessories)',
- 'Gifts (flowers, cards, etc)'
+ 'Gifts (flowers, cards, etc)',
+ 'Adult'
],
relayOptions: [
'wss://nostr-relay.herokuapp.com/ws',
@@ -244,6 +265,17 @@ new Vue({
}
},
methods: {
+ ////////////////////////////////////////
+ ///////////SUPPORT MESSAGES/////////////
+ ////////////////////////////////////////
+ getMessages: function (customerKey) {
+ var self = this
+ console.log('fuck')
+ messages = []
+ messages.push(['in', 'blah blah'])
+ messages.push(['out', 'blah blah'])
+ self.customerMessages = messages
+ },
////////////////////////////////////////
////////////////STALLS//////////////////
////////////////////////////////////////
@@ -256,10 +288,7 @@ new Vue({
this.g.user.wallets[0].inkey
)
.then(function (response) {
- self.stalls = response.data.map(function (obj) {
- console.log(obj)
- return mapDiagonAlley(obj)
- })
+ self.stalls.push(mapStalls(response.data))
})
},
openStallUpdateDialog: function (linkId) {
@@ -302,7 +331,7 @@ new Vue({
self.stalls = _.reject(self.stalls, function (obj) {
return obj.id == data.id
})
- self.stalls.push(mapDiagonAlley(response.data))
+ self.stalls.push(mapStalls(response.data))
self.stallDialog.show = false
self.stallDialog.data = {}
data = {}
@@ -323,7 +352,7 @@ new Vue({
data
)
.then(function (response) {
- self.stalls.push(mapDiagonAlley(response.data))
+ self.stalls.push(mapStalls(response.data))
self.stallDialog.show = false
self.stallDialog.data = {}
data = {}
@@ -371,9 +400,7 @@ new Vue({
this.g.user.wallets[0].inkey
)
.then(function (response) {
- self.products = response.data.map(function (obj) {
- return mapDiagonAlley(obj)
- })
+ self.products.push(mapProducts(response.data))
})
},
openProductUpdateDialog: function (linkId) {
@@ -450,7 +477,7 @@ new Vue({
self.products = _.reject(self.products, function (obj) {
return obj.id == data.id
})
- self.products.push(mapDiagonAlley(response.data))
+ self.products.push(mapProducts(response.data))
self.productDialog.show = false
self.productDialog.data = {}
})
@@ -470,7 +497,7 @@ new Vue({
data
)
.then(function (response) {
- self.products.push(mapDiagonAlley(response.data))
+ self.products.push(mapProducts(response.data))
self.productDialog.show = false
self.productDialog.data = {}
})
@@ -517,9 +544,7 @@ new Vue({
this.g.user.wallets[0].inkey
)
.then(function (response) {
- self.zones = response.data.map(function (obj) {
- return mapDiagonAlley(obj)
- })
+ self.zones.push(mapZone(response.data))
})
},
openZoneUpdateDialog: function (linkId) {
@@ -559,7 +584,7 @@ new Vue({
self.zones = _.reject(self.zones, function (obj) {
return obj.id == data.id
})
- self.zones.push(mapDiagonAlley(response.data))
+ self.zones.push(mapZone(response.data))
self.zoneDialog.show = false
self.zoneDialog.data = {}
data = {}
@@ -580,7 +605,7 @@ new Vue({
data
)
.then(function (response) {
- self.zones.push(mapDiagonAlley(response.data))
+ self.zones.push(mapZone(response.data))
self.zoneDialog.show = false
self.zoneDialog.data = {}
data = {}
@@ -628,9 +653,7 @@ new Vue({
this.g.user.wallets[0].inkey
)
.then(function (response) {
- self.shops = response.data.map(function (obj) {
- return mapDiagonAlley(obj)
- })
+ self.shops.push(mapShops(response.data))
})
},
openShopUpdateDialog: function (linkId) {
@@ -670,7 +693,7 @@ new Vue({
self.shops = _.reject(self.shops, function (obj) {
return obj.id == data.id
})
- self.shops.push(mapDiagonAlley(response.data))
+ self.shops.push(mapShops(response.data))
self.shopDialog.show = false
self.shopDialog.data = {}
data = {}
@@ -692,7 +715,7 @@ new Vue({
data
)
.then(function (response) {
- self.shops.push(mapDiagonAlley(response.data))
+ self.shops.push(mapShops(response.data))
self.shopDialog.show = false
self.shopDialog.data = {}
data = {}
@@ -740,9 +763,7 @@ new Vue({
this.g.user.wallets[0].inkey
)
.then(function (response) {
- self.orders = response.data.map(function (obj) {
- return mapDiagonAlley(obj)
- })
+ self.orders.push(mapOrders(response.data))
})
},
createOrder: function () {
@@ -763,7 +784,7 @@ new Vue({
data
)
.then(function (response) {
- self.orders.push(mapDiagonAlley(response.data))
+ self.orders.push(mapOrders(response.data))
self.orderDialog.show = false
self.orderDialog.data = {}
})
@@ -804,9 +825,7 @@ new Vue({
this.g.user.wallets[0].inkey
)
.then(function (response) {
- self.orders = response.data.map(function (obj) {
- return mapDiagonAlley(obj)
- })
+ self.orders.push(mapOrders(response.data))
})
},
exportOrdersCSV: function () {
@@ -819,6 +838,10 @@ new Vue({
this.getProducts()
this.getZones()
this.getOrders()
+ this.customerKeys = [
+ 'cb4c0164fe03fcdadcbfb4f76611c71620790944c24f21a1cd119395cdedfe1b',
+ 'a9c17358a6dc4ceb3bb4d883eb87967a66b3453a0f3199f0b1c8eef8070c6a07'
+ ]
}
}
})
diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/index.html b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
index 98405f6d..a89c8b5e 100644
--- a/lnbits/extensions/diagonalley/templates/diagonalley/index.html
+++ b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
@@ -594,34 +594,37 @@
- Messages (example)
+ Messages
-
-
-
OrderID:87h87h
KJBIBYBUYBUF90898....
-
OrderID:NIUHB7
79867KJGJHGVFYFV....
+
+
+
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
From ecaea51a1ce5f98c585b58caf2b0c16f2eb8a75b Mon Sep 17 00:00:00 2001
From: benarc
Date: Thu, 3 Feb 2022 22:30:53 +0000
Subject: [PATCH 08/30] Added error messages
---
lnbits/extensions/diagonalley/static/js/index.js | 6 ++++++
.../diagonalley/templates/diagonalley/index.html | 13 ++++++++++---
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/lnbits/extensions/diagonalley/static/js/index.js b/lnbits/extensions/diagonalley/static/js/index.js
index af81db2a..d101bfcf 100644
--- a/lnbits/extensions/diagonalley/static/js/index.js
+++ b/lnbits/extensions/diagonalley/static/js/index.js
@@ -265,6 +265,12 @@ new Vue({
}
},
methods: {
+ errorMessage: function (error) {
+ this.$q.notify({
+ color: 'primary',
+ message: error
+ })
+ },
////////////////////////////////////////
///////////SUPPORT MESSAGES/////////////
////////////////////////////////////////
diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/index.html b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
index a89c8b5e..d14d7cee 100644
--- a/lnbits/extensions/diagonalley/templates/diagonalley/index.html
+++ b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
@@ -300,13 +300,20 @@
- + Product List a product
+ + Product List a product
+ Shipping Zone Create a shipping zone
- + Stall
+ Create a stall to list products on
+ + Stall
Create a stall to list products on
@@ -618,7 +625,7 @@
>
-
+
From 44f7fae2d4035f9757d9f6836e59be68b09c987c Mon Sep 17 00:00:00 2001
From: benarc
Date: Fri, 4 Feb 2022 13:05:48 +0000
Subject: [PATCH 09/30] Zones are saving/updating
---
lnbits/extensions/diagonalley/__init__.py | 11 +-
lnbits/extensions/diagonalley/crud.py | 116 +--
lnbits/extensions/diagonalley/migrations.py | 8 +-
lnbits/extensions/diagonalley/models.py | 7 +-
.../extensions/diagonalley/static/js/index.js | 853 -----------------
.../templates/diagonalley/index.html | 895 +++++++++++++++++-
lnbits/extensions/diagonalley/views_api.py | 40 +-
7 files changed, 924 insertions(+), 1006 deletions(-)
delete mode 100644 lnbits/extensions/diagonalley/static/js/index.js
diff --git a/lnbits/extensions/diagonalley/__init__.py b/lnbits/extensions/diagonalley/__init__.py
index cab65685..388c08db 100644
--- a/lnbits/extensions/diagonalley/__init__.py
+++ b/lnbits/extensions/diagonalley/__init__.py
@@ -9,17 +9,9 @@ from lnbits.tasks import catch_everything_and_restart
db = Database("ext_diagonalley")
-diagonalley_static_files = [
- {
- "path": "/diagonalley/static",
- "app": StaticFiles(directory="lnbits/extensions/diagonalley/static"),
- "name": "diagonalley_static",
- }
-]
diagonalley_ext: APIRouter = APIRouter(
prefix="/diagonalley", tags=["diagonalley"]
- # "diagonalley", __name__, static_folder="static", template_folder="templates"
)
def diagonalley_renderer():
@@ -33,5 +25,4 @@ from .views_api import * # noqa
def diagonalley_start():
loop = asyncio.get_event_loop()
- loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
-
+ loop.create_task(catch_everything_and_restart(wait_for_paid_invoices))
\ No newline at end of file
diff --git a/lnbits/extensions/diagonalley/crud.py b/lnbits/extensions/diagonalley/crud.py
index 4cf14014..d2df427c 100644
--- a/lnbits/extensions/diagonalley/crud.py
+++ b/lnbits/extensions/diagonalley/crud.py
@@ -22,28 +22,12 @@ from .models import (
createZones,
)
-regex = re.compile(
- r"^(?:http|ftp)s?://" # http:// or https://
- r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|"
- r"localhost|"
- r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"
- r"(?::\d+)?"
- r"(?:/?|[/?]\S+)$",
- re.IGNORECASE,
-)
-
-
###Products
-
async def create_diagonalley_product(
data: createProduct
) -> Products:
- # returning = "" if db.type == SQLITE else "RETURNING ID"
- # method = db.execute if db.type == SQLITE else db.fetchone
product_id = urlsafe_short_hash()
- # with open_ext_db("diagonalley") as db:
- # result = await (method)(
await db.execute(
f"""
INSERT INTO diagonalley.products (id, stall, product, categories, description, image, price, quantity)
@@ -110,7 +94,7 @@ async def delete_diagonalley_product(product_id: str) -> None:
async def create_diagonalley_zone(
- wallet,
+ user,
data: createZones
) -> Zones:
zone_id = urlsafe_short_hash()
@@ -118,14 +102,14 @@ async def create_diagonalley_zone(
f"""
INSERT INTO diagonalley.zones (
id,
- wallet,
+ user,
cost,
countries
)
VALUES (?, ?, ?, ?)
""",
- (zone_id, wallet, data.cost, data.countries),
+ (zone_id, user, data.cost, data.countries.lower()),
)
zone = await get_diagonalley_zone(zone_id)
@@ -148,41 +132,8 @@ async def get_diagonalley_zone(zone_id: str) -> Optional[Zones]:
return Zones(**row) if row else None
-async def get_diagonalley_zones(wallet_ids: Union[str, List[str]]) -> List[Zones]:
- if isinstance(wallet_ids, str):
- wallet_ids = [wallet_ids]
- print(wallet_ids)
-
- q = ",".join(["?"] * len(wallet_ids))
- rows = await db.fetchall(
- f"SELECT * FROM diagonalley.zones WHERE wallet IN ({q})", (*wallet_ids,)
- )
-
- for r in rows:
- try:
- x = httpx.get(r["zoneaddress"] + "/" + r["ratingkey"])
- if x.status_code == 200:
- await db.execute(
- "UPDATE diagonalley.zones SET online = ? WHERE id = ?",
- (
- True,
- r["id"],
- ),
- )
- else:
- await db.execute(
- "UPDATE diagonalley.zones SET online = ? WHERE id = ?",
- (
- False,
- r["id"],
- ),
- )
- except:
- print("An exception occurred")
- q = ",".join(["?"] * len(wallet_ids))
- rows = await db.fetchall(
- f"SELECT * FROM diagonalley.zones WHERE wallet IN ({q})", (*wallet_ids,)
- )
+async def get_diagonalley_zones(user: str) -> List[Zones]:
+ rows = await db.fetchall("SELECT * FROM diagonalley.zones WHERE user = ?", (user,))
return [Zones(**row) for row in rows]
@@ -217,7 +168,7 @@ async def create_diagonalley_stall(
data.publickey,
data.privatekey,
data.relays,
- data.shippingzones),
+ repr(data.shippingzones)),
)
stall = await get_diagonalley_stall(stall_id)
@@ -238,32 +189,6 @@ async def update_diagonalley_stall(stall_id: str, **kwargs) -> Optional[Stalls]:
async def get_diagonalley_stall(stall_id: str) -> Optional[Stalls]:
- roww = await db.fetchone(
- "SELECT * FROM diagonalley.stalls WHERE id = ?", (stall_id,)
- )
-
- try:
- x = httpx.get(roww["stalladdress"] + "/" + roww["ratingkey"])
- if x.status_code == 200:
- await db.execute(
- "UPDATE diagonalley.stalls SET online = ? WHERE id = ?",
- (
- True,
- stall_id,
- ),
- )
- else:
- await db.execute(
- "UPDATE diagonalley.stalls SET online = ? WHERE id = ?",
- (
- False,
- stall_id,
- ),
- )
- except:
- print("An exception occurred")
-
- # with open_ext_db("diagonalley") as db:
row = await db.fetchone(
"SELECT * FROM diagonalley.stalls WHERE id = ?", (stall_id,)
)
@@ -271,35 +196,6 @@ async def get_diagonalley_stall(stall_id: str) -> Optional[Stalls]:
async def get_diagonalley_stalls(wallet_ids: Union[str, List[str]]) -> List[Stalls]:
- if isinstance(wallet_ids, str):
- wallet_ids = [wallet_ids]
-
- q = ",".join(["?"] * len(wallet_ids))
- rows = await db.fetchall(
- f"SELECT * FROM diagonalley.stalls WHERE wallet IN ({q})", (*wallet_ids,)
- )
-
- for r in rows:
- try:
- x = httpx.get(r["stalladdress"] + "/" + r["ratingkey"])
- if x.status_code == 200:
- await db.execute(
- "UPDATE diagonalley.stalls SET online = ? WHERE id = ?",
- (
- True,
- r["id"],
- ),
- )
- else:
- await db.execute(
- "UPDATE diagonalley.stalls SET online = ? WHERE id = ?",
- (
- False,
- r["id"],
- ),
- )
- except:
- print("An exception occurred")
q = ",".join(["?"] * len(wallet_ids))
rows = await db.fetchall(
f"SELECT * FROM diagonalley.stalls WHERE wallet IN ({q})", (*wallet_ids,)
diff --git a/lnbits/extensions/diagonalley/migrations.py b/lnbits/extensions/diagonalley/migrations.py
index 1523f398..29a84419 100644
--- a/lnbits/extensions/diagonalley/migrations.py
+++ b/lnbits/extensions/diagonalley/migrations.py
@@ -28,7 +28,8 @@ async def m001_initial(db):
name TEXT NOT NULL,
publickey TEXT NOT NULL,
privatekey TEXT NOT NULL,
- relays TEXT NOT NULL
+ relays TEXT NOT NULL,
+ shippingzones TEXT NOT NULL
);
"""
)
@@ -40,7 +41,7 @@ async def m001_initial(db):
"""
CREATE TABLE diagonalley.zones (
id TEXT PRIMARY KEY,
- wallet TEXT NOT NULL,
+ user TEXT NOT NULL,
cost TEXT NOT NULL,
countries TEXT NOT NULL
);
@@ -55,7 +56,8 @@ async def m001_initial(db):
CREATE TABLE diagonalley.orders (
id TEXT PRIMARY KEY,
productid TEXT NOT NULL,
- wallet TEXT NOT NULL,
+ usr TEXT NOT NULL,
+ pubkey TEXT NOT NULL,
product TEXT NOT NULL,
quantity INTEGER NOT NULL,
shippingzone INTEGER NOT NULL,
diff --git a/lnbits/extensions/diagonalley/models.py b/lnbits/extensions/diagonalley/models.py
index 1a975e10..743c2068 100644
--- a/lnbits/extensions/diagonalley/models.py
+++ b/lnbits/extensions/diagonalley/models.py
@@ -49,13 +49,13 @@ class Products(BaseModel):
quantity: int
class createZones(BaseModel):
- cost: str = Query(None)
+ cost: int = Query(0, ge=0)
countries: str = Query(None)
class Zones(BaseModel):
id: str
- wallet: str
- cost: str
+ user: str
+ cost: int
countries: str
@@ -73,6 +73,7 @@ class Orders(BaseModel):
id: str
productid: str
stall: str
+ pubkey: str
product: str
quantity: int
shippingzone: int
diff --git a/lnbits/extensions/diagonalley/static/js/index.js b/lnbits/extensions/diagonalley/static/js/index.js
deleted file mode 100644
index d101bfcf..00000000
--- a/lnbits/extensions/diagonalley/static/js/index.js
+++ /dev/null
@@ -1,853 +0,0 @@
-/* globals Quasar, Vue, _, VueQrcode, windowMixin, LNbits, LOCALE */
-
-Vue.component(VueQrcode.name, VueQrcode)
-
-//const pica = window.pica()
-
-var mapStalls = obj => {
- obj._data = _.clone(obj)
- return obj
-}
-var mapProducts = obj => {
- obj._data = _.clone(obj)
- return obj
-}
-var mapZone = obj => {
- obj._data = _.clone(obj)
- return obj
-}
-var mapOrders = obj => {
- obj._data = _.clone(obj)
- return obj
-}
-
-new Vue({
- el: '#vue',
- mixins: [windowMixin],
- data: function () {
- return {
- products: [],
- orders: [],
- stalls: [],
- zones: [],
- customerKeys: [],
- customerKey: '',
- customerMessages: {},
- shippedModel: false,
- shippingZoneOptions: [
- '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'
- ],
- relayOptions: [
- 'wss://nostr-relay.herokuapp.com/ws',
- 'wss://nostr-relay.bigsun.xyz/ws',
- 'wss://freedom-relay.herokuapp.com/ws'
- ],
- label: '',
- ordersTable: {
- columns: [
- {
- name: 'product',
- align: 'left',
- label: 'Product',
- field: 'product'
- },
- {
- name: 'quantity',
- align: 'left',
- label: 'Quantity',
- field: 'quantity'
- },
- {
- name: 'address',
- align: 'left',
- label: 'Address',
- field: 'address'
- },
- {
- name: 'invoiceid',
- align: 'left',
- label: 'InvoiceID',
- field: 'invoiceid'
- },
- {name: 'paid', align: 'left', label: 'Paid', field: 'paid'},
- {name: 'shipped', align: 'left', label: 'Shipped', field: 'shipped'}
- ],
- pagination: {
- rowsPerPage: 10
- }
- },
- productsTable: {
- columns: [
- {
- name: 'stall',
- align: 'left',
- label: 'Stall',
- field: 'stall'
- },
- {
- name: 'product',
- align: 'left',
- label: 'Product',
- field: 'product'
- },
- {
- name: 'description',
- align: 'left',
- label: 'Description',
- field: 'description'
- },
- {
- name: 'categories',
- align: 'left',
- label: 'Categories',
- field: 'categories'
- },
- {name: 'price', align: 'left', label: 'Price', field: 'price'},
- {
- name: 'quantity',
- align: 'left',
- label: 'Quantity',
- field: 'quantity'
- },
- {name: 'id', align: 'left', label: 'ID', field: 'id'}
- ],
- pagination: {
- rowsPerPage: 10
- }
- },
- stallTable: {
- columns: [
- {
- name: 'id',
- align: 'left',
- label: 'ID',
- field: 'id'
- },
- {
- name: 'name',
- align: 'left',
- label: 'Name',
- field: 'name'
- },
- {
- name: 'wallet',
- align: 'left',
- label: 'Wallet',
- field: 'wallet'
- },
- {
- name: 'publickey',
- align: 'left',
- label: 'Public key',
- field: 'publickey'
- },
- {
- name: 'privatekey',
- align: 'left',
- label: 'Private key',
- field: 'privatekey'
- }
- ],
- pagination: {
- rowsPerPage: 10
- }
- },
- zonesTable: {
- columns: [
- {
- name: 'id',
- align: 'left',
- label: 'ID',
- field: 'id'
- },
- {
- name: 'countries',
- align: 'left',
- label: 'Countries',
- field: 'countries'
- },
- {
- name: 'cost',
- align: 'left',
- label: 'Cost',
- field: 'cost'
- }
- ],
- pagination: {
- rowsPerPage: 10
- }
- },
- productDialog: {
- show: false,
- data: {}
- },
- stallDialog: {
- show: false,
- data: {}
- },
- zoneDialog: {
- show: false,
- data: {}
- },
- shopDialog: {
- show: false,
- data: {activate: false}
- },
- orderDialog: {
- show: false,
- data: {}
- },
- relayDialog: {
- show: false,
- data: {}
- }
- }
- },
- computed: {
- categoryOther: function () {
- cats = trim(this.productDialog.data.categories.split(','))
- for (let i = 0; i < cats.length; i++) {
- if (cats[i] == 'Others') {
- return true
- }
- }
- return false
- }
- },
- methods: {
- errorMessage: function (error) {
- this.$q.notify({
- color: 'primary',
- message: error
- })
- },
- ////////////////////////////////////////
- ///////////SUPPORT MESSAGES/////////////
- ////////////////////////////////////////
- getMessages: function (customerKey) {
- var self = this
- console.log('fuck')
- messages = []
- messages.push(['in', 'blah blah'])
- messages.push(['out', 'blah blah'])
- self.customerMessages = messages
- },
- ////////////////////////////////////////
- ////////////////STALLS//////////////////
- ////////////////////////////////////////
- getStalls: function () {
- var self = this
- LNbits.api
- .request(
- 'GET',
- '/diagonalley/api/v1/stalls?all_wallets',
- this.g.user.wallets[0].inkey
- )
- .then(function (response) {
- self.stalls.push(mapStalls(response.data))
- })
- },
- openStallUpdateDialog: function (linkId) {
- var self = this
- var link = _.findWhere(self.stalls, {id: linkId})
-
- this.stallDialog.data = _.clone(link._data)
- this.stallDialog.show = true
- },
- sendStallFormData: function () {
- if (this.stallDialog.data.id) {
- } else {
- var data = {
- name: this.stallDialog.data.name,
- wallet: this.stallDialog.data.wallet,
- publickey: this.stallDialog.data.publickey,
- privatekey: this.stallDialog.data.privatekey,
- relays: this.stallDialog.data.relays
- }
- }
-
- if (this.stallDialog.data.id) {
- this.updateStall(this.stallDialog.data)
- } else {
- this.createStall(data)
- }
- },
- updateStall: function (data) {
- var self = this
- LNbits.api
- .request(
- 'PUT',
- '/diagonalley/api/v1/stalls' + data.id,
- _.findWhere(self.g.user.wallets, {
- id: self.stallDialog.data.wallet
- }).inkey,
- _.pick(data, 'name', 'wallet', 'publickey', 'privatekey')
- )
- .then(function (response) {
- self.stalls = _.reject(self.stalls, function (obj) {
- return obj.id == data.id
- })
- self.stalls.push(mapStalls(response.data))
- self.stallDialog.show = false
- self.stallDialog.data = {}
- data = {}
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- },
- createStall: function (data) {
- var self = this
- LNbits.api
- .request(
- 'POST',
- '/diagonalley/api/v1/stalls',
- _.findWhere(self.g.user.wallets, {
- id: self.stallDialog.data.wallet
- }).inkey,
- data
- )
- .then(function (response) {
- self.stalls.push(mapStalls(response.data))
- self.stallDialog.show = false
- self.stallDialog.data = {}
- data = {}
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- },
- deleteStall: function (stallId) {
- var self = this
- var stall = _.findWhere(self.stalls, {id: stallId})
-
- LNbits.utils
- .confirmDialog('Are you sure you want to delete this Stall link?')
- .onOk(function () {
- LNbits.api
- .request(
- 'DELETE',
- '/diagonalley/api/v1/stalls/' + stallId,
- _.findWhere(self.g.user.wallets, {id: stall.wallet}).inkey
- )
- .then(function (response) {
- self.stalls = _.reject(self.stalls, function (obj) {
- return obj.id == stallId
- })
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- })
- },
- exportStallsCSV: function () {
- LNbits.utils.exportCSV(this.stallsTable.columns, this.stalls)
- },
- ////////////////////////////////////////
- ///////////////PRODUCTS/////////////////
- ////////////////////////////////////////
- getProducts: function () {
- var self = this
-
- LNbits.api
- .request(
- 'GET',
- '/diagonalley/api/v1/products?all_stalls',
- this.g.user.wallets[0].inkey
- )
- .then(function (response) {
- self.products.push(mapProducts(response.data))
- })
- },
- openProductUpdateDialog: function (linkId) {
- var self = this
- var link = _.findWhere(self.products, {id: linkId})
-
- self.productDialog.data = _.clone(link._data)
- self.productDialog.show = true
- },
- sendProductFormData: function () {
- if (this.productDialog.data.id) {
- } else {
- var data = {
- product: this.productDialog.data.product,
- categories:
- this.productDialog.data.categories +
- this.productDialog.categoriesextra,
- description: this.productDialog.data.description,
- image: this.productDialog.data.image,
- price: this.productDialog.data.price,
- quantity: this.productDialog.data.quantity
- }
- }
- if (this.productDialog.data.id) {
- this.updateProduct(this.productDialog.data)
- } else {
- this.createProduct(data)
- }
- },
- imageAdded(file) {
- let blobURL = URL.createObjectURL(file)
- let image = new Image()
- image.src = blobURL
- image.onload = async () => {
- let canvas = document.createElement('canvas')
- canvas.setAttribute('width', 100)
- canvas.setAttribute('height', 100)
- await pica.resize(image, canvas, {
- quality: 0,
- alpha: true,
- unsharpAmount: 95,
- unsharpRadius: 0.9,
- unsharpThreshold: 70
- })
- this.productDialog.data.image = canvas.toDataURL()
- this.productDialog = {...this.productDialog}
- }
- },
- imageCleared() {
- this.productDialog.data.image = null
- this.productDialog = {...this.productDialog}
- },
- updateProduct: function (data) {
- var self = this
- LNbits.api
- .request(
- 'PUT',
- '/diagonalley/api/v1/products' + data.id,
- _.findWhere(self.g.user.wallets, {
- id: self.productDialog.data.wallet
- }).inkey,
- _.pick(
- data,
- 'shopname',
- 'relayaddress',
- 'shippingzone1',
- 'zone1cost',
- 'shippingzone2',
- 'zone2cost',
- 'email'
- )
- )
- .then(function (response) {
- self.products = _.reject(self.products, function (obj) {
- return obj.id == data.id
- })
- self.products.push(mapProducts(response.data))
- self.productDialog.show = false
- self.productDialog.data = {}
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- },
- createProduct: function (data) {
- var self = this
- LNbits.api
- .request(
- 'POST',
- '/diagonalley/api/v1/products',
- _.findWhere(self.g.user.wallets, {
- id: self.productDialog.data.wallet
- }).inkey,
- data
- )
- .then(function (response) {
- self.products.push(mapProducts(response.data))
- self.productDialog.show = false
- self.productDialog.data = {}
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- },
- deleteProduct: function (productId) {
- var self = this
- var product = _.findWhere(this.products, {id: productId})
-
- LNbits.utils
- .confirmDialog('Are you sure you want to delete this products link?')
- .onOk(function () {
- LNbits.api
- .request(
- 'DELETE',
- '/diagonalley/api/v1/products/' + productId,
- _.findWhere(self.g.user.wallets, {id: product.wallet}).inkey
- )
- .then(function (response) {
- self.products = _.reject(self.products, function (obj) {
- return obj.id == productId
- })
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- })
- },
- exportProductsCSV: function () {
- LNbits.utils.exportCSV(this.productsTable.columns, this.products)
- },
- ////////////////////////////////////////
- //////////////////ZONE//////////////////
- ////////////////////////////////////////
- getZones: function () {
- var self = this
-
- LNbits.api
- .request(
- 'GET',
- '/diagonalley/api/v1/zones?all_wallets',
- this.g.user.wallets[0].inkey
- )
- .then(function (response) {
- self.zones.push(mapZone(response.data))
- })
- },
- openZoneUpdateDialog: function (linkId) {
- var self = this
- var link = _.findWhere(self.zones, {id: linkId})
-
- this.zoneDialog.data = _.clone(link._data)
- this.zoneDialog.show = true
- },
- sendZoneFormData: function () {
- if (this.zoneDialog.data.id) {
- } else {
- var data = {
- countries: toString(this.zoneDialog.data.countries),
- cost: parseInt(this.zoneDialog.data.cost)
- }
- }
-
- if (this.zoneDialog.data.id) {
- this.updateZone(this.zoneDialog.data)
- } else {
- this.createZone(data)
- }
- },
- updateZone: function (data) {
- var self = this
- LNbits.api
- .request(
- 'PUT',
- '/diagonalley/api/v1/zones' + data.id,
- _.findWhere(self.g.user.wallets, {
- id: self.zoneDialog.data.wallet
- }).inkey,
- _.pick(data, 'countries', 'cost')
- )
- .then(function (response) {
- self.zones = _.reject(self.zones, function (obj) {
- return obj.id == data.id
- })
- self.zones.push(mapZone(response.data))
- self.zoneDialog.show = false
- self.zoneDialog.data = {}
- data = {}
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- },
- createZone: function (data) {
- var self = this
- console.log(self.g.user.wallets[0])
- console.log(data)
- LNbits.api
- .request(
- 'POST',
- '/diagonalley/api/v1/zones',
- self.g.user.wallets[0].inkey,
- data
- )
- .then(function (response) {
- self.zones.push(mapZone(response.data))
- self.zoneDialog.show = false
- self.zoneDialog.data = {}
- data = {}
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- },
- deleteZone: function (zoneId) {
- var self = this
- var zone = _.findWhere(self.zones, {id: zoneId})
-
- LNbits.utils
- .confirmDialog('Are you sure you want to delete this Zone link?')
- .onOk(function () {
- LNbits.api
- .request(
- 'DELETE',
- '/diagonalley/api/v1/zones/' + zoneId,
- _.findWhere(self.g.user.wallets, {id: zone.wallet}).inkey
- )
- .then(function (response) {
- self.zones = _.reject(self.zones, function (obj) {
- return obj.id == zoneId
- })
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- })
- },
- exportZonesCSV: function () {
- LNbits.utils.exportCSV(this.zonesTable.columns, this.zones)
- },
- ////////////////////////////////////////
- //////////////////SHOP//////////////////
- ////////////////////////////////////////
- getShops: function () {
- var self = this
-
- LNbits.api
- .request(
- 'GET',
- '/diagonalley/api/v1/shops?all_wallets',
- this.g.user.wallets[0].inkey
- )
- .then(function (response) {
- self.shops.push(mapShops(response.data))
- })
- },
- openShopUpdateDialog: function (linkId) {
- var self = this
- var link = _.findWhere(self.shops, {id: linkId})
-
- this.shopDialog.data = _.clone(link._data)
- this.shopDialog.show = true
- },
- sendShopFormData: function () {
- if (this.shopDialog.data.id) {
- } else {
- var data = {
- countries: this.shopDialog.data.countries,
- cost: this.shopDialog.data.cost
- }
- }
-
- if (this.shopDialog.data.id) {
- this.updateZone(this.shopDialog.data)
- } else {
- this.createZone(data)
- }
- },
- updateShop: function (data) {
- var self = this
- LNbits.api
- .request(
- 'PUT',
- '/diagonalley/api/v1/shops' + data.id,
- _.findWhere(self.g.user.wallets, {
- id: self.shopDialog.data.wallet
- }).inkey,
- _.pick(data, 'countries', 'cost')
- )
- .then(function (response) {
- self.shops = _.reject(self.shops, function (obj) {
- return obj.id == data.id
- })
- self.shops.push(mapShops(response.data))
- self.shopDialog.show = false
- self.shopDialog.data = {}
- data = {}
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- },
- createShop: function (data) {
- var self = this
- console.log('cuntywoo')
- LNbits.api
- .request(
- 'POST',
- '/diagonalley/api/v1/shops',
- _.findWhere(self.g.user.wallets, {
- id: self.shopDialog.data.wallet
- }).inkey,
- data
- )
- .then(function (response) {
- self.shops.push(mapShops(response.data))
- self.shopDialog.show = false
- self.shopDialog.data = {}
- data = {}
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- },
- deleteShop: function (shopId) {
- var self = this
- var shop = _.findWhere(self.shops, {id: shopId})
-
- LNbits.utils
- .confirmDialog('Are you sure you want to delete this Shop link?')
- .onOk(function () {
- LNbits.api
- .request(
- 'DELETE',
- '/diagonalley/api/v1/shops/' + shopId,
- _.findWhere(self.g.user.wallets, {id: shop.wallet}).inkey
- )
- .then(function (response) {
- self.shops = _.reject(self.shops, function (obj) {
- return obj.id == shopId
- })
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- })
- },
- exportShopsCSV: function () {
- LNbits.utils.exportCSV(this.shopsTable.columns, this.shops)
- },
- ////////////////////////////////////////
- ////////////////ORDERS//////////////////
- ////////////////////////////////////////
- getOrders: function () {
- var self = this
-
- LNbits.api
- .request(
- 'GET',
- '/diagonalley/api/v1/orders?all_wallets',
- this.g.user.wallets[0].inkey
- )
- .then(function (response) {
- self.orders.push(mapOrders(response.data))
- })
- },
- createOrder: function () {
- var data = {
- address: this.orderDialog.data.address,
- email: this.orderDialog.data.email,
- quantity: this.orderDialog.data.quantity,
- shippingzone: this.orderDialog.data.shippingzone
- }
- var self = this
-
- LNbits.api
- .request(
- 'POST',
- '/diagonalley/api/v1/orders',
- _.findWhere(self.g.user.wallets, {id: self.orderDialog.data.wallet})
- .inkey,
- data
- )
- .then(function (response) {
- self.orders.push(mapOrders(response.data))
- self.orderDialog.show = false
- self.orderDialog.data = {}
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- },
- deleteOrder: function (orderId) {
- var self = this
- var order = _.findWhere(self.orders, {id: orderId})
-
- LNbits.utils
- .confirmDialog('Are you sure you want to delete this order link?')
- .onOk(function () {
- LNbits.api
- .request(
- 'DELETE',
- '/diagonalley/api/v1/orders/' + orderId,
- _.findWhere(self.g.user.wallets, {id: order.wallet}).inkey
- )
- .then(function (response) {
- self.orders = _.reject(self.orders, function (obj) {
- return obj.id == orderId
- })
- })
- .catch(function (error) {
- LNbits.utils.notifyApiError(error)
- })
- })
- },
- shipOrder: function (order_id) {
- var self = this
-
- LNbits.api
- .request(
- 'GET',
- '/diagonalley/api/v1/orders/shipped/' + order_id,
- this.g.user.wallets[0].inkey
- )
- .then(function (response) {
- self.orders.push(mapOrders(response.data))
- })
- },
- exportOrdersCSV: function () {
- LNbits.utils.exportCSV(this.ordersTable.columns, this.orders)
- }
- },
- created: function () {
- if (this.g.user.wallets.length) {
- this.getStalls()
- this.getProducts()
- this.getZones()
- this.getOrders()
- this.customerKeys = [
- 'cb4c0164fe03fcdadcbfb4f76611c71620790944c24f21a1cd119395cdedfe1b',
- 'a9c17358a6dc4ceb3bb4d883eb87967a66b3453a0f3199f0b1c8eef8070c6a07'
- ]
- }
- }
-})
diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/index.html b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
index d14d7cee..7b8f8e6a 100644
--- a/lnbits/extensions/diagonalley/templates/diagonalley/index.html
+++ b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
@@ -300,8 +300,9 @@
- + Product List a product + Product List a produc
+ Product List a product + Shipping Zone Create a shipping zone
- + Stall
Create a stall to list products on
@@ -321,6 +322,7 @@
>Launch frontend shop (not Nostr)
Makes a simple frontend shop for your stalls
+
@@ -638,7 +640,890 @@
+
{% endblock %} {% block scripts %} {{ window_vars(user) }}
-
-
+
+
{% endblock %}
+
diff --git a/lnbits/extensions/diagonalley/views_api.py b/lnbits/extensions/diagonalley/views_api.py
index 43232841..6363dc97 100644
--- a/lnbits/extensions/diagonalley/views_api.py
+++ b/lnbits/extensions/diagonalley/views_api.py
@@ -121,36 +121,32 @@ async def api_diagonalley_products_delete(product_id, wallet: WalletTypeInfo = D
@diagonalley_ext.get("/api/v1/zones")
-async def api_diagonalley_zones(wallet: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False)):
- wallet_ids = [wallet.wallet.id]
+async def api_diagonalley_zones(wallet: WalletTypeInfo = Depends(get_key_type)):
- if all_wallets:
- wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
-
- return ([zone.dict() for zone in await get_diagonalley_zones(wallet_ids)])
+ return await get_diagonalley_zones(wallet.wallet.user)
@diagonalley_ext.post("/api/v1/zones")
-@diagonalley_ext.put("/api/v1/zones/{zone_id}")
async def api_diagonalley_zone_create(
data: createZones,
- zone_id: str = Query(None),
wallet: WalletTypeInfo = Depends(get_key_type)
):
- if zone_id:
- zone = await get_diagonalley_zone(zone_id)
-
- if not zone:
- return ({"message": "Zone does not exist."})
-
- if zone.wallet != wallet.wallet.id:
- return ({"message": "Not your record."})
-
- zone = await update_diagonalley_zone(zone_id, **data.dict())
- else:
- zone = await create_diagonalley_zone(wallet=wallet.wallet.id, data=data)
-
+ zone = await create_diagonalley_zone(user=wallet.wallet.user, data=data)
return zone.dict()
+@diagonalley_ext.post("/api/v1/zones/{zone_id}")
+async def api_diagonalley_zone_update(
+ data: createZones,
+ zone_id: str = Query(None),
+ wallet: WalletTypeInfo = Depends(require_admin_key)
+ ):
+ zone = await get_diagonalley_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_diagonalley_zone(zone_id, **data.dict())
+ return zone
+
@diagonalley_ext.delete("/api/v1/zones/{zone_id}")
async def api_diagonalley_zone_delete(zone_id, wallet: WalletTypeInfo = Depends(require_admin_key)):
@@ -159,7 +155,7 @@ async def api_diagonalley_zone_delete(zone_id, wallet: WalletTypeInfo = Depends(
if not zone:
return ({"message": "zone does not exist."})
- if zone.wallet != wallet.wallet.id:
+ if zone.user != wallet.wallet.user:
return ({"message": "Not your zone."})
await delete_diagonalley_zone(zone_id)
From 80b73e7aba1b1dff80693265dadd96946b27375e Mon Sep 17 00:00:00 2001
From: benarc
Date: Sun, 6 Feb 2022 22:40:51 +0000
Subject: [PATCH 10/30] small fix
---
.../templates/diagonalley/index.html | 25 +++++++++++++------
1 file changed, 18 insertions(+), 7 deletions(-)
diff --git a/lnbits/extensions/diagonalley/templates/diagonalley/index.html b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
index 7b8f8e6a..6d6ea872 100644
--- a/lnbits/extensions/diagonalley/templates/diagonalley/index.html
+++ b/lnbits/extensions/diagonalley/templates/diagonalley/index.html
@@ -244,7 +244,7 @@
label="Private Key"
>
@@ -270,7 +270,7 @@
filled
dense
v-model.trim="stallDialog.data.nostrShops"
- label="Stall public keys (seperate by comma)"
+ label="Nostr shop public keys (seperate by comma)"
>
- + Product List a produc 1" color="primary" @click="productDialog.show = true"
+ >+ Product List a product
+ Product List a product + Shipping Zone
Create a shipping zone
-
+ Stall
Create a stall to list products on
@@ -675,6 +675,7 @@ new Vue({
orders: [],
stalls: [],
zones: [],
+ zoneOptions: [],
customerKeys: [],
customerKey: '',
customerMessages: {},
@@ -949,10 +950,19 @@ new Vue({
LNbits.utils.notifyApiError(error)
})
},
+ openStallDialog: function () {
+ console.log(this.zones[0]['id'])
+ for(let i = 0; i < this.zones.length; i ++){
+ this.zoneOptions.push(this.zones[i]['id'])
+ }
+ this.stallDialog.show = true
+ },
openStallUpdateDialog: function (linkId) {
var self = this
var link = _.findWhere(self.stalls, {id: linkId})
-
+ for(let i = 0; i < this.zones.length; i ++){
+ this.zoneOptions.push(this.stalls[i][0])
+ }
this.stallDialog.data = _.clone(link._data)
this.stallDialog.show = true
},
@@ -1210,6 +1220,7 @@ new Vue({
if (response.data) {
console.log(response)
self.zones = response.data.map(mapZone)
+
}
})
.catch(function (error) {
From f38492a9209f27cf587946c0359c5ae047d9463c Mon Sep 17 00:00:00 2001
From: benarc
Date: Mon, 7 Feb 2022 13:22:43 +0000
Subject: [PATCH 11/30] Added nostr ext
---
lnbits/extensions/nostr/README.md | 3 +
lnbits/extensions/nostr/__init__.py | 17 +
lnbits/extensions/nostr/config.json | 6 +
lnbits/extensions/nostr/crud.py | 134 +++++
lnbits/extensions/nostr/migrations.py | 47 ++
lnbits/extensions/nostr/models.py | 34 ++
.../templates/lnurldevice/_api_docs.html | 169 ++++++
.../nostr/templates/lnurldevice/error.html | 34 ++
.../nostr/templates/lnurldevice/index.html | 534 ++++++++++++++++++
.../nostr/templates/lnurldevice/paid.html | 27 +
lnbits/extensions/nostr/views.py | 24 +
lnbits/extensions/nostr/views_api.py | 48 ++
12 files changed, 1077 insertions(+)
create mode 100644 lnbits/extensions/nostr/README.md
create mode 100644 lnbits/extensions/nostr/__init__.py
create mode 100644 lnbits/extensions/nostr/config.json
create mode 100644 lnbits/extensions/nostr/crud.py
create mode 100644 lnbits/extensions/nostr/migrations.py
create mode 100644 lnbits/extensions/nostr/models.py
create mode 100644 lnbits/extensions/nostr/templates/lnurldevice/_api_docs.html
create mode 100644 lnbits/extensions/nostr/templates/lnurldevice/error.html
create mode 100644 lnbits/extensions/nostr/templates/lnurldevice/index.html
create mode 100644 lnbits/extensions/nostr/templates/lnurldevice/paid.html
create mode 100644 lnbits/extensions/nostr/views.py
create mode 100644 lnbits/extensions/nostr/views_api.py
diff --git a/lnbits/extensions/nostr/README.md b/lnbits/extensions/nostr/README.md
new file mode 100644
index 00000000..596cce9d
--- /dev/null
+++ b/lnbits/extensions/nostr/README.md
@@ -0,0 +1,3 @@
+# Nostr
+
+Opens a Nostr daemon
diff --git a/lnbits/extensions/nostr/__init__.py b/lnbits/extensions/nostr/__init__.py
new file mode 100644
index 00000000..775960e3
--- /dev/null
+++ b/lnbits/extensions/nostr/__init__.py
@@ -0,0 +1,17 @@
+from fastapi import APIRouter
+
+from lnbits.db import Database
+from lnbits.helpers import template_renderer
+
+db = Database("ext_nostr")
+
+nostr_ext: APIRouter = APIRouter(prefix="/nostr", tags=["nostr"])
+
+
+def nostr_renderer():
+ return template_renderer(["lnbits/extensions/nostr/templates"])
+
+
+from .lnurl import * # noqa
+from .views import * # noqa
+from .views_api import * # noqa
diff --git a/lnbits/extensions/nostr/config.json b/lnbits/extensions/nostr/config.json
new file mode 100644
index 00000000..a32e39a1
--- /dev/null
+++ b/lnbits/extensions/nostr/config.json
@@ -0,0 +1,6 @@
+{
+ "name": "Nostr",
+ "short_description": "Daemon for Nostr",
+ "icon": "swap_horizontal_circle",
+ "contributors": ["arcbtc"]
+}
diff --git a/lnbits/extensions/nostr/crud.py b/lnbits/extensions/nostr/crud.py
new file mode 100644
index 00000000..55e99ec6
--- /dev/null
+++ b/lnbits/extensions/nostr/crud.py
@@ -0,0 +1,134 @@
+from typing import List, Optional, Union
+
+from lnbits.helpers import urlsafe_short_hash
+
+from . import db
+from .models import nostrKeys, nostrNotes, nostrRelays, nostrConnections
+
+###############KEYS##################
+
+async def create_nostrkeys(
+ data: nostrKeys
+) -> nostrKeys:
+ nostrkey_id = urlsafe_short_hash()
+ await db.execute(
+ """
+ INSERT INTO nostr.keys (
+ id,
+ pubkey,
+ privkey
+ )
+ VALUES (?, ?, ?)
+ """,
+ (nostrkey_id, data.pubkey, data.privkey),
+ )
+ return await get_nostrkeys(nostrkey_id)
+
+async def get_nostrkeys(nostrkey_id: str) -> nostrKeys:
+ row = await db.fetchone(
+ "SELECT * FROM nostr.keys WHERE id = ?",
+ (lnurldevicepayment_id,),
+ )
+ return nostrKeys(**row) if row else None
+
+
+###############NOTES##################
+
+async def create_nostrnotes(
+ data: nostrNotes
+) -> nostrNotes:
+ await db.execute(
+ """
+ INSERT INTO nostr.notes (
+ id,
+ pubkey,
+ created_at,
+ kind,
+ tags,
+ content,
+ sig
+ )
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ """,
+ (data.id, data.pubkey, data.created_at, data.kind, data.tags, data.content, data.sig),
+ )
+ return await get_nostrnotes(data.id)
+
+async def get_nostrnotes(nostrnote_id: str) -> nostrNotes:
+ row = await db.fetchone(
+ "SELECT * FROM nostr.notes WHERE id = ?",
+ (nostrnote_id,),
+ )
+ return nostrNotes(**row) if row else None
+
+###############RELAYS##################
+
+async def create_nostrrelays(
+ relay: str
+) -> nostrRelays:
+ nostrrelay_id = urlsafe_short_hash()
+ await db.execute(
+ """
+ INSERT INTO nostr.relays (
+ id,
+ relay
+ )
+ VALUES (?, ?)
+ """,
+ (nostrrelay_id, relay),
+ )
+ return await get_nostrnotes(nostrrelay_id)
+
+async def get_nostrrelays(nostrrelay_id: str) -> nostrRelays:
+ row = await db.fetchone(
+ "SELECT * FROM nostr.relays WHERE id = ?",
+ (nostrnote_id,),
+ )
+ return nostrRelays(**row) if row else None
+
+
+###############CONNECTIONS##################
+
+async def create_nostrconnections(
+ data: nostrNotes
+) -> nostrNotes:
+ nostrkey_id = urlsafe_short_hash()
+ await db.execute(
+ """
+ INSERT INTO nostr.notes (
+ id,
+ pubkey,
+ created_at,
+ kind,
+ tags,
+ content,
+ sig
+ )
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ """,
+ (data.id, data.pubkey, data.created_at, data.kind, data.tags, data.content, data.sig),
+ )
+ return await get_nostrnotes(data.id)
+
+async def get_nostrnotes(nostrnote_id: str) -> nostrNotes:
+ row = await db.fetchone(
+ "SELECT * FROM nostr.notes WHERE id = ?",
+ (nostrnote_id,),
+ )
+ return nostrNotes(**row) if row else None
+
+
+
+async def update_lnurldevicepayment(
+ lnurldevicepayment_id: str, **kwargs
+) -> Optional[lnurldevicepayment]:
+ q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+ await db.execute(
+ f"UPDATE lnurldevice.lnurldevicepayment SET {q} WHERE id = ?",
+ (*kwargs.values(), lnurldevicepayment_id),
+ )
+ row = await db.fetchone(
+ "SELECT * FROM lnurldevice.lnurldevicepayment WHERE id = ?",
+ (lnurldevicepayment_id,),
+ )
+ return lnurldevicepayment(**row) if row else None
\ No newline at end of file
diff --git a/lnbits/extensions/nostr/migrations.py b/lnbits/extensions/nostr/migrations.py
new file mode 100644
index 00000000..de32a578
--- /dev/null
+++ b/lnbits/extensions/nostr/migrations.py
@@ -0,0 +1,47 @@
+from lnbits.db import Database
+
+db2 = Database("ext_nostr")
+
+
+async def m001_initial(db):
+ """
+ Initial nostr table.
+ """
+ await db.execute(
+ f"""
+ CREATE TABLE nostr.keys (
+ pubkey TEXT NOT NULL PRIMARY KEY,
+ privkey TEXT NOT NULL
+ );
+ """
+ )
+ await db.execute(
+ f"""
+ CREATE TABLE nostr.notes (
+ id TEXT NOT NULL PRIMARY KEY,
+ pubkey TEXT NOT NULL,
+ created_at TEXT NOT NULL,
+ kind INT NOT NULL,
+ tags TEXT NOT NULL,
+ content TEXT NOT NULL,
+ sig TEXT NOT NULL,
+ );
+ """
+ )
+ await db.execute(
+ f"""
+ CREATE TABLE nostr.relays (
+ id TEXT NOT NULL PRIMARY KEY,
+ relay TEXT NOT NULL
+ );
+ """
+ )
+ await db.execute(
+ f"""
+ CREATE TABLE nostr.connections (
+ id TEXT NOT NULL PRIMARY KEY,
+ publickey TEXT NOT NULL,
+ relayid TEXT NOT NULL
+ );
+ """
+ )
\ No newline at end of file
diff --git a/lnbits/extensions/nostr/models.py b/lnbits/extensions/nostr/models.py
new file mode 100644
index 00000000..ba0c87a5
--- /dev/null
+++ b/lnbits/extensions/nostr/models.py
@@ -0,0 +1,34 @@
+import json
+from sqlite3 import Row
+from typing import Optional
+
+from fastapi import Request
+from lnurl import Lnurl
+from lnurl import encode as lnurl_encode # type: ignore
+from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore
+from lnurl.types import LnurlPayMetadata # type: ignore
+from pydantic import BaseModel
+from pydantic.main import BaseModel
+
+class nostrKeys(BaseModel):
+ id: str
+ pubkey: str
+ privkey: str
+
+class nostrNotes(BaseModel):
+ id: str
+ pubkey: str
+ created_at: str
+ kind: int
+ tags: str
+ content: str
+ sig: str
+
+class nostrRelays(BaseModel):
+ id: str
+ relay: str
+
+class nostrConnections(BaseModel):
+ id: str
+ pubkey: str
+ relayid: str
\ No newline at end of file
diff --git a/lnbits/extensions/nostr/templates/lnurldevice/_api_docs.html b/lnbits/extensions/nostr/templates/lnurldevice/_api_docs.html
new file mode 100644
index 00000000..af69b76e
--- /dev/null
+++ b/lnbits/extensions/nostr/templates/lnurldevice/_api_docs.html
@@ -0,0 +1,169 @@
+
+
+
+ Register LNURLDevice devices to receive payments in your LNbits wallet.
+ Build your own here
+ https://github.com/arcbtc/bitcoinpos
+
+ Created by, Ben Arc
+
+
+
+
+
+
+ /lnurldevice/api/v1/lnurlpos
+ Headers
+ {"X-Api-Key": <admin_key>}
+
+ Body (application/json)
+
+
+ Returns 200 OK (application/json)
+
+ [<lnurldevice_object>, ...]
+ Curl example
+ curl -X POST {{ request.base_url }}api/v1/lnurldevice -d '{"title":
+ <string>, "message":<string>, "currency":
+ <integer>}' -H "Content-type: application/json" -H "X-Api-Key:
+ {{user.wallets[0].adminkey }}"
+
+
+
+
+
+
+
+ PUT
+ /lnurldevice/api/v1/lnurlpos/<lnurldevice_id>
+ Headers
+ {"X-Api-Key": <admin_key>}
+
+ Body (application/json)
+
+
+ Returns 200 OK (application/json)
+
+ [<lnurldevice_object>, ...]
+ Curl example
+ curl -X POST {{ request.base_url
+ }}api/v1/lnurlpos/<lnurldevice_id> -d ''{"title":
+ <string>, "message":<string>, "currency":
+ <integer>} -H "Content-type: application/json" -H "X-Api-Key:
+ {{user.wallets[0].adminkey }}"
+
+
+
+
+
+
+
+
+ GET
+ /lnurldevice/api/v1/lnurlpos/<lnurldevice_id>
+ Headers
+ {"X-Api-Key": <invoice_key>}
+
+ Body (application/json)
+
+
+ Returns 200 OK (application/json)
+
+ [<lnurldevice_object>, ...]
+ Curl example
+ curl -X GET {{ request.base_url
+ }}api/v1/lnurlpos/<lnurldevice_id> -H "X-Api-Key: {{
+ user.wallets[0].inkey }}"
+
+
+
+
+
+
+
+ GET
+ /lnurldevice/api/v1/lnurlposs
+ Headers
+ {"X-Api-Key": <invoice_key>}
+
+ Body (application/json)
+
+
+ Returns 200 OK (application/json)
+
+ [<lnurldevice_object>, ...]
+ Curl example
+ curl -X GET {{ request.base_url }}api/v1/lnurldevices -H
+ "X-Api-Key: {{ user.wallets[0].inkey }}"
+
+
+
+
+
+
+
+ DELETE
+ /lnurldevice/api/v1/lnurlpos/<lnurldevice_id>
+ Headers
+ {"X-Api-Key": <admin_key>}
+ Returns 204 NO CONTENT
+
+ Curl example
+ curl -X DELETE {{ request.base_url
+ }}api/v1/lnurlpos/<lnurldevice_id> -H "X-Api-Key: {{
+ user.wallets[0].adminkey }}"
+
+
+
+
+
+
diff --git a/lnbits/extensions/nostr/templates/lnurldevice/error.html b/lnbits/extensions/nostr/templates/lnurldevice/error.html
new file mode 100644
index 00000000..d8e41832
--- /dev/null
+++ b/lnbits/extensions/nostr/templates/lnurldevice/error.html
@@ -0,0 +1,34 @@
+{% extends "public.html" %} {% block page %}
+
+
+
+
+
+ LNURL-pay not paid
+
+
+
+
+
+
+
+
+
+ {% endblock %} {% block scripts %}
+
+
+
+ {% endblock %}
+
diff --git a/lnbits/extensions/nostr/templates/lnurldevice/index.html b/lnbits/extensions/nostr/templates/lnurldevice/index.html
new file mode 100644
index 00000000..b51e2556
--- /dev/null
+++ b/lnbits/extensions/nostr/templates/lnurldevice/index.html
@@ -0,0 +1,534 @@
+{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
+%} {% block page %}
+
+
+
+
+ {% raw %}
+ New LNURLDevice instance
+
+
+
+
+
+
+
+
+
lNURLdevice
+
+
+
+
+
+
+
+
+ Export to CSV
+
+
+
+
+
+
+
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+
+
+
+ Delete LNURLDevice
+
+
+
+
+ LNURLDevice Settings
+
+
+
+
+ {{ col.value }}
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+
+
+
+ {{SITE_TITLE}} LNURLDevice Extension
+
+
+
+
+ {% include "lnurldevice/_api_docs.html" %}
+
+
+
+
+
+
+ LNURLDevice device string
+ {% raw
+ %}{{location}}/lnurldevice/api/v1/lnurl/{{settingsDialog.data.id}},
+ {{settingsDialog.data.key}}, {{settingsDialog.data.currency}}{% endraw
+ %} Click to copy URL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Update lnurldevice
+ Create lnurldevice
+ Cancel
+
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(user) }}
+
+
+{% endblock %}
diff --git a/lnbits/extensions/nostr/templates/lnurldevice/paid.html b/lnbits/extensions/nostr/templates/lnurldevice/paid.html
new file mode 100644
index 00000000..c185ecce
--- /dev/null
+++ b/lnbits/extensions/nostr/templates/lnurldevice/paid.html
@@ -0,0 +1,27 @@
+{% extends "public.html" %} {% block page %}
+
+
+
+
+
+ {{ pin }}
+
+
+
+
+
+
+ {% endblock %} {% block scripts %}
+
+
+
+ {% endblock %}
+
diff --git a/lnbits/extensions/nostr/views.py b/lnbits/extensions/nostr/views.py
new file mode 100644
index 00000000..1dfc07da
--- /dev/null
+++ b/lnbits/extensions/nostr/views.py
@@ -0,0 +1,24 @@
+from http import HTTPStatus
+
+from fastapi import Request
+from fastapi.param_functions import Query
+from fastapi.params import Depends
+from fastapi.templating import Jinja2Templates
+from starlette.exceptions import HTTPException
+from starlette.responses import HTMLResponse
+
+from lnbits.core.crud import update_payment_status
+from lnbits.core.models import User
+from lnbits.core.views.api import api_payment
+from lnbits.decorators import check_user_exists
+
+from . import nostr_ext, nostr_renderer
+
+templates = Jinja2Templates(directory="templates")
+
+
+@nostr_ext.get("/", response_class=HTMLResponse)
+async def index(request: Request, user: User = Depends(check_user_exists)):
+ return nostr_renderer().TemplateResponse(
+ "nostr/index.html", {"request": request, "user": user.dict()}
+ )
diff --git a/lnbits/extensions/nostr/views_api.py b/lnbits/extensions/nostr/views_api.py
new file mode 100644
index 00000000..a479cad8
--- /dev/null
+++ b/lnbits/extensions/nostr/views_api.py
@@ -0,0 +1,48 @@
+from http import HTTPStatus
+
+from fastapi import Request
+from fastapi.param_functions import Query
+from fastapi.params import Depends
+from starlette.exceptions import HTTPException
+
+from lnbits.core.crud import get_user
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
+from lnbits.extensions.lnurldevice import lnurldevice_ext
+from lnbits.utils.exchange_rates import currencies
+
+from . import lnurldevice_ext
+from .crud import (
+ create_lnurldevice,
+ delete_lnurldevice,
+ get_lnurldevice,
+ get_lnurldevices,
+ update_lnurldevice,
+)
+from .models import createLnurldevice
+
+
+@nostr_ext.get("/api/v1/lnurlpos")
+async def api_check_daemon(wallet: WalletTypeInfo = Depends(get_key_type)):
+ wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
+ try:
+ return [
+ {**lnurldevice.dict()} for lnurldevice in await get_lnurldevices(wallet_ids)
+ ]
+ except:
+ return ""
+
+@nostr_ext.delete("/api/v1/lnurlpos/{lnurldevice_id}")
+async def api_lnurldevice_delete(
+ wallet: WalletTypeInfo = Depends(require_admin_key),
+ lnurldevice_id: str = Query(None),
+):
+ lnurldevice = await get_lnurldevice(lnurldevice_id)
+
+ if not lnurldevice:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="Wallet link does not exist."
+ )
+
+ await delete_lnurldevice(lnurldevice_id)
+
+ return "", HTTPStatus.NO_CONTENT
\ No newline at end of file
From da1025a625b75dc842efb2baf7582cfb5682a191 Mon Sep 17 00:00:00 2001
From: benarc
Date: Mon, 7 Feb 2022 20:01:01 +0000
Subject: [PATCH 12/30] extension loading
---
lnbits/extensions/nostr/__init__.py | 2 -
lnbits/extensions/nostr/crud.py | 71 +--
lnbits/extensions/nostr/migrations.py | 2 +-
lnbits/extensions/nostr/models.py | 12 +-
.../templates/lnurldevice/_api_docs.html | 169 ------
.../nostr/templates/lnurldevice/error.html | 34 --
.../nostr/templates/lnurldevice/index.html | 534 ------------------
.../nostr/templates/lnurldevice/paid.html | 27 -
.../nostr/templates/nostr/index.html | 159 ++++++
lnbits/extensions/nostr/views_api.py | 46 +-
10 files changed, 204 insertions(+), 852 deletions(-)
delete mode 100644 lnbits/extensions/nostr/templates/lnurldevice/_api_docs.html
delete mode 100644 lnbits/extensions/nostr/templates/lnurldevice/error.html
delete mode 100644 lnbits/extensions/nostr/templates/lnurldevice/index.html
delete mode 100644 lnbits/extensions/nostr/templates/lnurldevice/paid.html
create mode 100644 lnbits/extensions/nostr/templates/nostr/index.html
diff --git a/lnbits/extensions/nostr/__init__.py b/lnbits/extensions/nostr/__init__.py
index 775960e3..9774a279 100644
--- a/lnbits/extensions/nostr/__init__.py
+++ b/lnbits/extensions/nostr/__init__.py
@@ -11,7 +11,5 @@ nostr_ext: APIRouter = APIRouter(prefix="/nostr", tags=["nostr"])
def nostr_renderer():
return template_renderer(["lnbits/extensions/nostr/templates"])
-
-from .lnurl import * # noqa
from .views import * # noqa
from .views_api import * # noqa
diff --git a/lnbits/extensions/nostr/crud.py b/lnbits/extensions/nostr/crud.py
index 55e99ec6..0b30bc9a 100644
--- a/lnbits/extensions/nostr/crud.py
+++ b/lnbits/extensions/nostr/crud.py
@@ -1,33 +1,31 @@
from typing import List, Optional, Union
from lnbits.helpers import urlsafe_short_hash
-
+import shortuuid
from . import db
-from .models import nostrKeys, nostrNotes, nostrRelays, nostrConnections
+from .models import nostrKeys, nostrNotes, nostrCreateRelays, nostrRelays, nostrConnections, nostrCreateConnections
###############KEYS##################
async def create_nostrkeys(
data: nostrKeys
) -> nostrKeys:
- nostrkey_id = urlsafe_short_hash()
await db.execute(
"""
INSERT INTO nostr.keys (
- id,
pubkey,
privkey
)
- VALUES (?, ?, ?)
+ VALUES (?, ?)
""",
- (nostrkey_id, data.pubkey, data.privkey),
+ (data.pubkey, data.privkey),
)
return await get_nostrkeys(nostrkey_id)
-async def get_nostrkeys(nostrkey_id: str) -> nostrKeys:
+async def get_nostrkeys(pubkey: str) -> nostrKeys:
row = await db.fetchone(
- "SELECT * FROM nostr.keys WHERE id = ?",
- (lnurldevicepayment_id,),
+ "SELECT * FROM nostr.keys WHERE pubkey = ?",
+ (pubkey,),
)
return nostrKeys(**row) if row else None
@@ -64,9 +62,12 @@ async def get_nostrnotes(nostrnote_id: str) -> nostrNotes:
###############RELAYS##################
async def create_nostrrelays(
- relay: str
-) -> nostrRelays:
- nostrrelay_id = urlsafe_short_hash()
+ data: nostrCreateRelays
+) -> nostrCreateRelays:
+ nostrrelay_id = shortuuid.uuid(name=relay)
+
+ if await get_nostrrelays(nostrrelay_id):
+ return "error"
await db.execute(
"""
INSERT INTO nostr.relays (
@@ -75,7 +76,7 @@ async def create_nostrrelays(
)
VALUES (?, ?)
""",
- (nostrrelay_id, relay),
+ (nostrrelay_id, data.relay),
)
return await get_nostrnotes(nostrrelay_id)
@@ -90,45 +91,25 @@ async def get_nostrrelays(nostrrelay_id: str) -> nostrRelays:
###############CONNECTIONS##################
async def create_nostrconnections(
- data: nostrNotes
-) -> nostrNotes:
- nostrkey_id = urlsafe_short_hash()
+ data: nostrCreateConnections
+) -> nostrCreateConnections:
+ nostrrelay_id = shortuuid.uuid(name=data.relayid + data.pubkey)
await db.execute(
"""
- INSERT INTO nostr.notes (
+ INSERT INTO nostr.connections (
id,
pubkey,
- created_at,
- kind,
- tags,
- content,
- sig
+ relayid
)
- VALUES (?, ?, ?, ?, ?, ?, ?)
+ VALUES (?, ?, ?)
""",
- (data.id, data.pubkey, data.created_at, data.kind, data.tags, data.content, data.sig),
+ (data.id, data.pubkey, data.relayid),
)
- return await get_nostrnotes(data.id)
+ return await get_nostrconnections(data.id)
-async def get_nostrnotes(nostrnote_id: str) -> nostrNotes:
+async def get_nostrconnections(nostrconnections_id: str) -> nostrConnections:
row = await db.fetchone(
- "SELECT * FROM nostr.notes WHERE id = ?",
- (nostrnote_id,),
+ "SELECT * FROM nostr.connections WHERE id = ?",
+ (nostrconnections_id,),
)
- return nostrNotes(**row) if row else None
-
-
-
-async def update_lnurldevicepayment(
- lnurldevicepayment_id: str, **kwargs
-) -> Optional[lnurldevicepayment]:
- q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
- await db.execute(
- f"UPDATE lnurldevice.lnurldevicepayment SET {q} WHERE id = ?",
- (*kwargs.values(), lnurldevicepayment_id),
- )
- row = await db.fetchone(
- "SELECT * FROM lnurldevice.lnurldevicepayment WHERE id = ?",
- (lnurldevicepayment_id,),
- )
- return lnurldevicepayment(**row) if row else None
\ No newline at end of file
+ return nostrConnections(**row) if row else None
\ No newline at end of file
diff --git a/lnbits/extensions/nostr/migrations.py b/lnbits/extensions/nostr/migrations.py
index de32a578..0c616110 100644
--- a/lnbits/extensions/nostr/migrations.py
+++ b/lnbits/extensions/nostr/migrations.py
@@ -24,7 +24,7 @@ async def m001_initial(db):
kind INT NOT NULL,
tags TEXT NOT NULL,
content TEXT NOT NULL,
- sig TEXT NOT NULL,
+ sig TEXT NOT NULL
);
"""
)
diff --git a/lnbits/extensions/nostr/models.py b/lnbits/extensions/nostr/models.py
index ba0c87a5..e21e5d76 100644
--- a/lnbits/extensions/nostr/models.py
+++ b/lnbits/extensions/nostr/models.py
@@ -3,15 +3,10 @@ from sqlite3 import Row
from typing import Optional
from fastapi import Request
-from lnurl import Lnurl
-from lnurl import encode as lnurl_encode # type: ignore
-from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore
-from lnurl.types import LnurlPayMetadata # type: ignore
from pydantic import BaseModel
from pydantic.main import BaseModel
class nostrKeys(BaseModel):
- id: str
pubkey: str
privkey: str
@@ -24,6 +19,13 @@ class nostrNotes(BaseModel):
content: str
sig: str
+class nostrCreateRelays(BaseModel):
+ relay: str
+
+class nostrCreateConnections(BaseModel):
+ pubkey: str
+ relayid: str
+
class nostrRelays(BaseModel):
id: str
relay: str
diff --git a/lnbits/extensions/nostr/templates/lnurldevice/_api_docs.html b/lnbits/extensions/nostr/templates/lnurldevice/_api_docs.html
deleted file mode 100644
index af69b76e..00000000
--- a/lnbits/extensions/nostr/templates/lnurldevice/_api_docs.html
+++ /dev/null
@@ -1,169 +0,0 @@
-
-
-
- Register LNURLDevice devices to receive payments in your LNbits wallet.
- Build your own here
- https://github.com/arcbtc/bitcoinpos
-
- Created by, Ben Arc
-
-
-
-
-
-
- /lnurldevice/api/v1/lnurlpos
- Headers
- {"X-Api-Key": <admin_key>}
-
- Body (application/json)
-
-
- Returns 200 OK (application/json)
-
- [<lnurldevice_object>, ...]
- Curl example
- curl -X POST {{ request.base_url }}api/v1/lnurldevice -d '{"title":
- <string>, "message":<string>, "currency":
- <integer>}' -H "Content-type: application/json" -H "X-Api-Key:
- {{user.wallets[0].adminkey }}"
-
-
-
-
-
-
-
- PUT
- /lnurldevice/api/v1/lnurlpos/<lnurldevice_id>
- Headers
- {"X-Api-Key": <admin_key>}
-
- Body (application/json)
-
-
- Returns 200 OK (application/json)
-
- [<lnurldevice_object>, ...]
- Curl example
- curl -X POST {{ request.base_url
- }}api/v1/lnurlpos/<lnurldevice_id> -d ''{"title":
- <string>, "message":<string>, "currency":
- <integer>} -H "Content-type: application/json" -H "X-Api-Key:
- {{user.wallets[0].adminkey }}"
-
-
-
-
-
-
-
-
- GET
- /lnurldevice/api/v1/lnurlpos/<lnurldevice_id>
- Headers
- {"X-Api-Key": <invoice_key>}
-
- Body (application/json)
-
-
- Returns 200 OK (application/json)
-
- [<lnurldevice_object>, ...]
- Curl example
- curl -X GET {{ request.base_url
- }}api/v1/lnurlpos/<lnurldevice_id> -H "X-Api-Key: {{
- user.wallets[0].inkey }}"
-
-
-
-
-
-
-
- GET
- /lnurldevice/api/v1/lnurlposs
- Headers
- {"X-Api-Key": <invoice_key>}
-
- Body (application/json)
-
-
- Returns 200 OK (application/json)
-
- [<lnurldevice_object>, ...]
- Curl example
- curl -X GET {{ request.base_url }}api/v1/lnurldevices -H
- "X-Api-Key: {{ user.wallets[0].inkey }}"
-
-
-
-
-
-
-
- DELETE
- /lnurldevice/api/v1/lnurlpos/<lnurldevice_id>
- Headers
- {"X-Api-Key": <admin_key>}
- Returns 204 NO CONTENT
-
- Curl example
- curl -X DELETE {{ request.base_url
- }}api/v1/lnurlpos/<lnurldevice_id> -H "X-Api-Key: {{
- user.wallets[0].adminkey }}"
-
-
-
-
-
-
diff --git a/lnbits/extensions/nostr/templates/lnurldevice/error.html b/lnbits/extensions/nostr/templates/lnurldevice/error.html
deleted file mode 100644
index d8e41832..00000000
--- a/lnbits/extensions/nostr/templates/lnurldevice/error.html
+++ /dev/null
@@ -1,34 +0,0 @@
-{% extends "public.html" %} {% block page %}
-
-
-
-
-
- LNURL-pay not paid
-
-
-
-
-
-
-
-
-
- {% endblock %} {% block scripts %}
-
-
-
- {% endblock %}
-
diff --git a/lnbits/extensions/nostr/templates/lnurldevice/index.html b/lnbits/extensions/nostr/templates/lnurldevice/index.html
deleted file mode 100644
index b51e2556..00000000
--- a/lnbits/extensions/nostr/templates/lnurldevice/index.html
+++ /dev/null
@@ -1,534 +0,0 @@
-{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
-%} {% block page %}
-
-
-
-
- {% raw %}
- New LNURLDevice instance
-
-
-
-
-
-
-
-
-
lNURLdevice
-
-
-
-
-
-
-
-
- Export to CSV
-
-
-
-
-
-
-
-
-
-
- {{ col.label }}
-
-
-
-
-
-
-
-
-
- Delete LNURLDevice
-
-
-
-
- LNURLDevice Settings
-
-
-
-
- {{ col.value }}
-
-
-
- {% endraw %}
-
-
-
-
-
-
-
-
-
- {{SITE_TITLE}} LNURLDevice Extension
-
-
-
-
- {% include "lnurldevice/_api_docs.html" %}
-
-
-
-
-
-
- LNURLDevice device string
- {% raw
- %}{{location}}/lnurldevice/api/v1/lnurl/{{settingsDialog.data.id}},
- {{settingsDialog.data.key}}, {{settingsDialog.data.currency}}{% endraw
- %} Click to copy URL
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Update lnurldevice
- Create lnurldevice
- Cancel
-
-
-
-
-
-{% endblock %} {% block scripts %} {{ window_vars(user) }}
-
-
-{% endblock %}
diff --git a/lnbits/extensions/nostr/templates/lnurldevice/paid.html b/lnbits/extensions/nostr/templates/lnurldevice/paid.html
deleted file mode 100644
index c185ecce..00000000
--- a/lnbits/extensions/nostr/templates/lnurldevice/paid.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{% extends "public.html" %} {% block page %}
-
-
-
-
-
- {{ pin }}
-
-
-
-
-
-
- {% endblock %} {% block scripts %}
-
-
-
- {% endblock %}
-
diff --git a/lnbits/extensions/nostr/templates/nostr/index.html b/lnbits/extensions/nostr/templates/nostr/index.html
new file mode 100644
index 00000000..f17d0243
--- /dev/null
+++ b/lnbits/extensions/nostr/templates/nostr/index.html
@@ -0,0 +1,159 @@
+{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
+%} {% block page %}
+
+
+
+
+
+
+
Nostr
+
+
+
+
+
+
+
+
+ Export to CSV
+
+
+
+ {% raw %}
+
+
+
+
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+
+
+
+ {{ col.value }}
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+
+
+ {{SITE_TITLE}} Nostr Extension
+
+ Okay
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(user) }}
+
+
+{% endblock %}
diff --git a/lnbits/extensions/nostr/views_api.py b/lnbits/extensions/nostr/views_api.py
index a479cad8..9e7ccfff 100644
--- a/lnbits/extensions/nostr/views_api.py
+++ b/lnbits/extensions/nostr/views_api.py
@@ -7,42 +7,18 @@ from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
-from lnbits.extensions.lnurldevice import lnurldevice_ext
+from lnbits.extensions.nostr import nostr_ext
from lnbits.utils.exchange_rates import currencies
-from . import lnurldevice_ext
+from . import nostr_ext
from .crud import (
- create_lnurldevice,
- delete_lnurldevice,
- get_lnurldevice,
- get_lnurldevices,
- update_lnurldevice,
+ create_nostrkeys,
+ get_nostrkeys,
+ create_nostrnotes,
+ get_nostrnotes,
+ create_nostrrelays,
+ get_nostrrelays,
+ create_nostrconnections,
+ get_nostrconnections,
)
-from .models import createLnurldevice
-
-
-@nostr_ext.get("/api/v1/lnurlpos")
-async def api_check_daemon(wallet: WalletTypeInfo = Depends(get_key_type)):
- wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
- try:
- return [
- {**lnurldevice.dict()} for lnurldevice in await get_lnurldevices(wallet_ids)
- ]
- except:
- return ""
-
-@nostr_ext.delete("/api/v1/lnurlpos/{lnurldevice_id}")
-async def api_lnurldevice_delete(
- wallet: WalletTypeInfo = Depends(require_admin_key),
- lnurldevice_id: str = Query(None),
-):
- lnurldevice = await get_lnurldevice(lnurldevice_id)
-
- if not lnurldevice:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="Wallet link does not exist."
- )
-
- await delete_lnurldevice(lnurldevice_id)
-
- return "", HTTPStatus.NO_CONTENT
\ No newline at end of file
+from .models import nostrKeys
\ No newline at end of file
From 29bd8d9ce963b4a9876120fbe4354121680ca32e Mon Sep 17 00:00:00 2001
From: benarc
Date: Tue, 8 Feb 2022 10:35:20 +0000
Subject: [PATCH 13/30] Chnaged nostr to nostradmin
---
.env.example | 2 +-
lnbits/extensions/nostr/config.json | 6 -
lnbits/extensions/nostr/crud.py | 115 -------------
lnbits/extensions/nostr/views_api.py | 24 ---
.../{nostr => nostradmin}/README.md | 0
.../{nostr => nostradmin}/__init__.py | 7 +-
lnbits/extensions/nostradmin/config.json | 6 +
lnbits/extensions/nostradmin/crud.py | 156 ++++++++++++++++++
.../{nostr => nostradmin}/migrations.py | 32 +++-
.../{nostr => nostradmin}/models.py | 14 +-
.../templates/nostradmin}/index.html | 130 ++++++++++++++-
.../extensions/{nostr => nostradmin}/views.py | 2 +-
lnbits/extensions/nostradmin/views_api.py | 87 ++++++++++
13 files changed, 421 insertions(+), 160 deletions(-)
delete mode 100644 lnbits/extensions/nostr/config.json
delete mode 100644 lnbits/extensions/nostr/crud.py
delete mode 100644 lnbits/extensions/nostr/views_api.py
rename lnbits/extensions/{nostr => nostradmin}/README.md (100%)
rename lnbits/extensions/{nostr => nostradmin}/__init__.py (51%)
create mode 100644 lnbits/extensions/nostradmin/config.json
create mode 100644 lnbits/extensions/nostradmin/crud.py
rename lnbits/extensions/{nostr => nostradmin}/migrations.py (54%)
rename lnbits/extensions/{nostr => nostradmin}/models.py (73%)
rename lnbits/extensions/{nostr/templates/nostr => nostradmin/templates/nostradmin}/index.html (50%)
rename lnbits/extensions/{nostr => nostradmin}/views.py (90%)
create mode 100644 lnbits/extensions/nostradmin/views_api.py
diff --git a/.env.example b/.env.example
index 060748a9..0d5b497e 100644
--- a/.env.example
+++ b/.env.example
@@ -8,7 +8,7 @@ PORT=5000
LNBITS_ALLOWED_USERS=""
LNBITS_ADMIN_USERS=""
# Extensions only admin can access
-LNBITS_ADMIN_EXTENSIONS="ngrok"
+LNBITS_ADMIN_EXTENSIONS="nostradmin"
LNBITS_DEFAULT_WALLET_NAME="LNbits wallet"
# Disable extensions for all users, use "all" to disable all extensions
diff --git a/lnbits/extensions/nostr/config.json b/lnbits/extensions/nostr/config.json
deleted file mode 100644
index a32e39a1..00000000
--- a/lnbits/extensions/nostr/config.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "name": "Nostr",
- "short_description": "Daemon for Nostr",
- "icon": "swap_horizontal_circle",
- "contributors": ["arcbtc"]
-}
diff --git a/lnbits/extensions/nostr/crud.py b/lnbits/extensions/nostr/crud.py
deleted file mode 100644
index 0b30bc9a..00000000
--- a/lnbits/extensions/nostr/crud.py
+++ /dev/null
@@ -1,115 +0,0 @@
-from typing import List, Optional, Union
-
-from lnbits.helpers import urlsafe_short_hash
-import shortuuid
-from . import db
-from .models import nostrKeys, nostrNotes, nostrCreateRelays, nostrRelays, nostrConnections, nostrCreateConnections
-
-###############KEYS##################
-
-async def create_nostrkeys(
- data: nostrKeys
-) -> nostrKeys:
- await db.execute(
- """
- INSERT INTO nostr.keys (
- pubkey,
- privkey
- )
- VALUES (?, ?)
- """,
- (data.pubkey, data.privkey),
- )
- return await get_nostrkeys(nostrkey_id)
-
-async def get_nostrkeys(pubkey: str) -> nostrKeys:
- row = await db.fetchone(
- "SELECT * FROM nostr.keys WHERE pubkey = ?",
- (pubkey,),
- )
- return nostrKeys(**row) if row else None
-
-
-###############NOTES##################
-
-async def create_nostrnotes(
- data: nostrNotes
-) -> nostrNotes:
- await db.execute(
- """
- INSERT INTO nostr.notes (
- id,
- pubkey,
- created_at,
- kind,
- tags,
- content,
- sig
- )
- VALUES (?, ?, ?, ?, ?, ?, ?)
- """,
- (data.id, data.pubkey, data.created_at, data.kind, data.tags, data.content, data.sig),
- )
- return await get_nostrnotes(data.id)
-
-async def get_nostrnotes(nostrnote_id: str) -> nostrNotes:
- row = await db.fetchone(
- "SELECT * FROM nostr.notes WHERE id = ?",
- (nostrnote_id,),
- )
- return nostrNotes(**row) if row else None
-
-###############RELAYS##################
-
-async def create_nostrrelays(
- data: nostrCreateRelays
-) -> nostrCreateRelays:
- nostrrelay_id = shortuuid.uuid(name=relay)
-
- if await get_nostrrelays(nostrrelay_id):
- return "error"
- await db.execute(
- """
- INSERT INTO nostr.relays (
- id,
- relay
- )
- VALUES (?, ?)
- """,
- (nostrrelay_id, data.relay),
- )
- return await get_nostrnotes(nostrrelay_id)
-
-async def get_nostrrelays(nostrrelay_id: str) -> nostrRelays:
- row = await db.fetchone(
- "SELECT * FROM nostr.relays WHERE id = ?",
- (nostrnote_id,),
- )
- return nostrRelays(**row) if row else None
-
-
-###############CONNECTIONS##################
-
-async def create_nostrconnections(
- data: nostrCreateConnections
-) -> nostrCreateConnections:
- nostrrelay_id = shortuuid.uuid(name=data.relayid + data.pubkey)
- await db.execute(
- """
- INSERT INTO nostr.connections (
- id,
- pubkey,
- relayid
- )
- VALUES (?, ?, ?)
- """,
- (data.id, data.pubkey, data.relayid),
- )
- return await get_nostrconnections(data.id)
-
-async def get_nostrconnections(nostrconnections_id: str) -> nostrConnections:
- row = await db.fetchone(
- "SELECT * FROM nostr.connections WHERE id = ?",
- (nostrconnections_id,),
- )
- return nostrConnections(**row) if row else None
\ No newline at end of file
diff --git a/lnbits/extensions/nostr/views_api.py b/lnbits/extensions/nostr/views_api.py
deleted file mode 100644
index 9e7ccfff..00000000
--- a/lnbits/extensions/nostr/views_api.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from http import HTTPStatus
-
-from fastapi import Request
-from fastapi.param_functions import Query
-from fastapi.params import Depends
-from starlette.exceptions import HTTPException
-
-from lnbits.core.crud import get_user
-from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
-from lnbits.extensions.nostr import nostr_ext
-from lnbits.utils.exchange_rates import currencies
-
-from . import nostr_ext
-from .crud import (
- create_nostrkeys,
- get_nostrkeys,
- create_nostrnotes,
- get_nostrnotes,
- create_nostrrelays,
- get_nostrrelays,
- create_nostrconnections,
- get_nostrconnections,
-)
-from .models import nostrKeys
\ No newline at end of file
diff --git a/lnbits/extensions/nostr/README.md b/lnbits/extensions/nostradmin/README.md
similarity index 100%
rename from lnbits/extensions/nostr/README.md
rename to lnbits/extensions/nostradmin/README.md
diff --git a/lnbits/extensions/nostr/__init__.py b/lnbits/extensions/nostradmin/__init__.py
similarity index 51%
rename from lnbits/extensions/nostr/__init__.py
rename to lnbits/extensions/nostradmin/__init__.py
index 9774a279..7034ca46 100644
--- a/lnbits/extensions/nostr/__init__.py
+++ b/lnbits/extensions/nostradmin/__init__.py
@@ -3,13 +3,14 @@ from fastapi import APIRouter
from lnbits.db import Database
from lnbits.helpers import template_renderer
-db = Database("ext_nostr")
+db = Database("ext_nostradmin")
-nostr_ext: APIRouter = APIRouter(prefix="/nostr", tags=["nostr"])
+nostr_ext: APIRouter = APIRouter(prefix="/nostradmin", tags=["nostradmin"])
def nostr_renderer():
- return template_renderer(["lnbits/extensions/nostr/templates"])
+ return template_renderer(["lnbits/extensions/nostradmin/templates"])
+
from .views import * # noqa
from .views_api import * # noqa
diff --git a/lnbits/extensions/nostradmin/config.json b/lnbits/extensions/nostradmin/config.json
new file mode 100644
index 00000000..2c4f76d3
--- /dev/null
+++ b/lnbits/extensions/nostradmin/config.json
@@ -0,0 +1,6 @@
+{
+ "name": "NostrAdmin",
+ "short_description": "Admin daemon for Nostr",
+ "icon": "swap_horizontal_circle",
+ "contributors": ["arcbtc"]
+}
diff --git a/lnbits/extensions/nostradmin/crud.py b/lnbits/extensions/nostradmin/crud.py
new file mode 100644
index 00000000..08908e85
--- /dev/null
+++ b/lnbits/extensions/nostradmin/crud.py
@@ -0,0 +1,156 @@
+from typing import List, Optional, Union
+
+from lnbits.helpers import urlsafe_short_hash
+import shortuuid
+from . import db
+from .models import (
+ nostrKeys,
+ nostrNotes,
+ nostrCreateRelays,
+ nostrRelays,
+ nostrConnections,
+ nostrCreateConnections,
+ nostrRelayList,
+)
+
+###############KEYS##################
+
+
+async def create_nostrkeys(data: nostrKeys) -> nostrKeys:
+ await db.execute(
+ """
+ INSERT INTO nostradmin.keys (
+ pubkey,
+ privkey
+ )
+ VALUES (?, ?)
+ """,
+ (data.pubkey, data.privkey),
+ )
+ return await get_nostrkeys(nostrkey_id)
+
+
+async def get_nostrkeys(pubkey: str) -> nostrKeys:
+ row = await db.fetchone("SELECT * FROM nostradmin.keys WHERE pubkey = ?", (pubkey,))
+ return nostrKeys(**row) if row else None
+
+
+###############NOTES##################
+
+
+async def create_nostrnotes(data: nostrNotes) -> nostrNotes:
+ await db.execute(
+ """
+ INSERT INTO nostradmin.notes (
+ id,
+ pubkey,
+ created_at,
+ kind,
+ tags,
+ content,
+ sig
+ )
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ """,
+ (
+ data.id,
+ data.pubkey,
+ data.created_at,
+ data.kind,
+ data.tags,
+ data.content,
+ data.sig,
+ ),
+ )
+ return await get_nostrnotes(data.id)
+
+
+async def get_nostrnotes(nostrnote_id: str) -> nostrNotes:
+ row = await db.fetchone("SELECT * FROM nostradmin.notes WHERE id = ?", (nostrnote_id,))
+ return nostrNotes(**row) if row else None
+
+
+###############RELAYS##################
+
+
+async def create_nostrrelays(data: nostrCreateRelays) -> nostrCreateRelays:
+ nostrrelay_id = shortuuid.uuid(name=data.relay)
+
+ if await get_nostrrelay(nostrrelay_id):
+ return "error"
+ await db.execute(
+ """
+ INSERT INTO nostradmin.relays (
+ id,
+ relay
+ )
+ VALUES (?, ?)
+ """,
+ (nostrrelay_id, data.relay),
+ )
+ return await get_nostrrelay(nostrrelay_id)
+
+
+async def get_nostrrelays() -> nostrRelays:
+ rows = await db.fetchall("SELECT * FROM nostradmin.relays")
+ return [nostrRelays(**row) for row in rows]
+
+
+async def get_nostrrelay(nostrrelay_id: str) -> nostrRelays:
+ row = await db.fetchone("SELECT * FROM nostradmin.relays WHERE id = ?", (nostrrelay_id,))
+ return nostrRelays(**row) if row else None
+
+
+async def update_nostrrelayallowlist(allowlist: str) -> nostrRelayList:
+ await db.execute(
+ """
+ UPDATE nostradmin.relaylist SET
+ allowlist = ?
+ WHERE id = ?
+ """,
+ (allowlist, 1),
+ )
+ return await get_nostrrelaylist()
+
+async def update_nostrrelaydenylist(denylist: str) -> nostrRelayList:
+ await db.execute(
+ """
+ UPDATE nostradmin.relaylist SET
+ denylist = ?
+ WHERE id = ?
+ """,
+ (denylist, 1),
+ )
+ return await get_nostrrelaylist()
+
+async def get_nostrrelaylist() -> nostrRelayList:
+ row = await db.fetchone("SELECT * FROM nostradmin.relaylist WHERE id = ?", (1,))
+ return nostrRelayList(**row) if row else None
+
+
+###############CONNECTIONS##################
+
+
+async def create_nostrconnections(
+ data: nostrCreateConnections
+) -> nostrCreateConnections:
+ nostrrelay_id = shortuuid.uuid(name=data.relayid + data.pubkey)
+ await db.execute(
+ """
+ INSERT INTO nostradmin.connections (
+ id,
+ pubkey,
+ relayid
+ )
+ VALUES (?, ?, ?)
+ """,
+ (data.id, data.pubkey, data.relayid),
+ )
+ return await get_nostrconnections(data.id)
+
+
+async def get_nostrconnections(nostrconnections_id: str) -> nostrConnections:
+ row = await db.fetchone(
+ "SELECT * FROM nostradmin.connections WHERE id = ?", (nostrconnections_id,)
+ )
+ return nostrConnections(**row) if row else None
diff --git a/lnbits/extensions/nostr/migrations.py b/lnbits/extensions/nostradmin/migrations.py
similarity index 54%
rename from lnbits/extensions/nostr/migrations.py
rename to lnbits/extensions/nostradmin/migrations.py
index 0c616110..09b28117 100644
--- a/lnbits/extensions/nostr/migrations.py
+++ b/lnbits/extensions/nostradmin/migrations.py
@@ -9,7 +9,7 @@ async def m001_initial(db):
"""
await db.execute(
f"""
- CREATE TABLE nostr.keys (
+ CREATE TABLE nostradmin.keys (
pubkey TEXT NOT NULL PRIMARY KEY,
privkey TEXT NOT NULL
);
@@ -17,7 +17,7 @@ async def m001_initial(db):
)
await db.execute(
f"""
- CREATE TABLE nostr.notes (
+ CREATE TABLE nostradmin.notes (
id TEXT NOT NULL PRIMARY KEY,
pubkey TEXT NOT NULL,
created_at TEXT NOT NULL,
@@ -30,7 +30,7 @@ async def m001_initial(db):
)
await db.execute(
f"""
- CREATE TABLE nostr.relays (
+ CREATE TABLE nostradmin.relays (
id TEXT NOT NULL PRIMARY KEY,
relay TEXT NOT NULL
);
@@ -38,10 +38,32 @@ async def m001_initial(db):
)
await db.execute(
f"""
- CREATE TABLE nostr.connections (
+ CREATE TABLE nostradmin.relaylists (
+ id TEXT NOT NULL PRIMARY KEY DEFAULT 1,
+ allowlist TEXT NOT NULL,
+ denylist TEXT NOT NULL
+ );
+ """
+ )
+ try:
+ await db.execute(
+ """
+ INSERT INTO nostradmin.relaylist (
+ id,
+ denylist
+ )
+ VALUES (?, ?,)
+ """,
+ (1, "\n".join(["wss://zucks-meta-relay.com", "wss://nostradmin.cia.gov"])),
+ )
+ except:
+ return
+ await db.execute(
+ f"""
+ CREATE TABLE nostradmin.connections (
id TEXT NOT NULL PRIMARY KEY,
publickey TEXT NOT NULL,
relayid TEXT NOT NULL
);
"""
- )
\ No newline at end of file
+ )
diff --git a/lnbits/extensions/nostr/models.py b/lnbits/extensions/nostradmin/models.py
similarity index 73%
rename from lnbits/extensions/nostr/models.py
rename to lnbits/extensions/nostradmin/models.py
index e21e5d76..fd89f515 100644
--- a/lnbits/extensions/nostr/models.py
+++ b/lnbits/extensions/nostradmin/models.py
@@ -6,6 +6,7 @@ from fastapi import Request
from pydantic import BaseModel
from pydantic.main import BaseModel
+
class nostrKeys(BaseModel):
pubkey: str
privkey: str
@@ -30,7 +31,18 @@ class nostrRelays(BaseModel):
id: str
relay: str
+class nostrRelayList(BaseModel):
+ id: str
+ allowlist: str
+ denylist: str
+
+class nostrRelayDenyList(BaseModel):
+ denylist: str
+
+class nostrRelayAllowList(BaseModel):
+ allowlist: str
+
class nostrConnections(BaseModel):
id: str
pubkey: str
- relayid: str
\ No newline at end of file
+ relayid: str
diff --git a/lnbits/extensions/nostr/templates/nostr/index.html b/lnbits/extensions/nostradmin/templates/nostradmin/index.html
similarity index 50%
rename from lnbits/extensions/nostr/templates/nostr/index.html
rename to lnbits/extensions/nostradmin/templates/nostradmin/index.html
index f17d0243..adab98e2 100644
--- a/lnbits/extensions/nostr/templates/nostr/index.html
+++ b/lnbits/extensions/nostradmin/templates/nostradmin/index.html
@@ -71,12 +71,77 @@
+
+
+
+
+
+
+
+
+
+
+
+ Deny List (denys use of relays in this list)
+
+
+
+
+
+
+
+
+ Update Deny List
+
+ Reset
+
+
+
+
+
+
+
+ Allow List (denys any relays not in this list)
+
+
+
+
+
+
+
+
+ Update Allow List
+
+ Reset
+
+
+
+
+
+
{{SITE_TITLE}} Nostr Extension
+ Only Admin users can manage this extension
Okay
@@ -109,6 +174,9 @@
mixins: [windowMixin],
data: function () {
return {
+ listSelection: 'denylist',
+ allowList: [],
+ denyList: [],
nostrTable: {
columns: [
{
@@ -136,12 +204,66 @@
LNbits.api
.request(
'GET',
- '/nostr/api/v1/relays',
+ '/nostradmin/api/v1/relays',
self.g.user.wallets[0].adminkey
)
.then(function (response) {
if (response.data) {
- self.lnurldeviceLinks = response.data.map(maplnurldevice)
+ self.nostrrelayLinks = response.data.map(maprelays)
+ }
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ setAllowList: function () {
+ var self = this
+ LNbits.api
+ .request(
+ 'POST',
+ '/nostradmin/api/v1/allowlist',
+ self.g.user.wallets[0].adminkey,
+ self.allowList
+ )
+ .then(function (response) {
+ if (response.data) {
+ self.allowList = response.data
+ }
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ setDenyList: function () {
+ var self = this
+ LNbits.api
+ .request(
+ 'POST',
+ '/nostradmin/api/v1/denylist',
+ self.g.user.wallets[0].adminkey,
+ self.allowList
+ )
+ .then(function (response) {
+ if (response.data) {
+ self.denyList = response.data
+ }
+ })
+ .catch(function (error) {
+ LNbits.utils.notifyApiError(error)
+ })
+ },
+ getLists: function () {
+ var self = this
+ LNbits.api
+ .request(
+ 'GET',
+ '/nostradmin/api/v1/relaylist',
+ self.g.user.wallets[0].adminkey
+ )
+ .then(function (response) {
+ if (response.data) {
+ self.denyList = response.data.denylist
+ self.allowList = response.data.allowlist
}
})
.catch(function (error) {
@@ -151,8 +273,8 @@
},
created: function () {
var self = this
- var getrelays = this.getrelays
- getrelays()
+ this.getrelays()
+ this.getLists()
}
})
diff --git a/lnbits/extensions/nostr/views.py b/lnbits/extensions/nostradmin/views.py
similarity index 90%
rename from lnbits/extensions/nostr/views.py
rename to lnbits/extensions/nostradmin/views.py
index 1dfc07da..5609c218 100644
--- a/lnbits/extensions/nostr/views.py
+++ b/lnbits/extensions/nostradmin/views.py
@@ -20,5 +20,5 @@ templates = Jinja2Templates(directory="templates")
@nostr_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
return nostr_renderer().TemplateResponse(
- "nostr/index.html", {"request": request, "user": user.dict()}
+ "nostradmin/index.html", {"request": request, "user": user.dict()}
)
diff --git a/lnbits/extensions/nostradmin/views_api.py b/lnbits/extensions/nostradmin/views_api.py
new file mode 100644
index 00000000..da6e140b
--- /dev/null
+++ b/lnbits/extensions/nostradmin/views_api.py
@@ -0,0 +1,87 @@
+from http import HTTPStatus
+import asyncio
+from fastapi import Request
+from fastapi.param_functions import Query
+from fastapi.params import Depends
+from starlette.exceptions import HTTPException
+
+from lnbits.core.crud import get_user
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
+from lnbits.extensions.nostr import nostr_ext
+from lnbits.utils.exchange_rates import currencies
+
+from . import nostr_ext
+from lnbits.settings import LNBITS_ADMIN_USERS
+from .crud import (
+ create_nostrkeys,
+ get_nostrkeys,
+ create_nostrnotes,
+ get_nostrnotes,
+ create_nostrrelays,
+ get_nostrrelays,
+ get_nostrrelaylist,
+ update_nostrrelayallowlist,
+ update_nostrrelaydenylist,
+ create_nostrconnections,
+ get_nostrconnections,
+)
+from .models import nostrKeys, nostrCreateRelays, nostrRelayAllowList, nostrRelayDenyList
+
+
+# while True:
+async def nostr_subscribe():
+ return
+ # for the relays:
+ # async with websockets.connect("ws://localhost:8765") as websocket:
+ # for the public keys:
+ # await websocket.send("subscribe to events")
+ # await websocket.recv()
+
+
+websocket_queue = asyncio.Queue(1000)
+
+
+async def internal_invoice_listener():
+ while True:
+ checking_id = await internal_invoice_queue.get()
+ asyncio.create_task(invoice_callback_dispatcher(checking_id))
+
+
+@nostr_ext.get("/api/v1/relays")
+async def api_relays_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
+
+ relays = await get_nostrrelays()
+ if not relays:
+ await create_nostrrelays(nostrCreateRelays(relay="wss://relayer.fiatjaf.com"))
+ await create_nostrrelays(
+ nostrCreateRelays(relay="wss://nostr-pub.wellorder.net")
+ )
+ relays = await get_nostrrelays()
+ try:
+ return [{**relays.dict()} for relays in await relays]
+ except:
+ None
+
+@nostr_ext.get("/api/v1/relaylist")
+async def api_relaylist(wallet: WalletTypeInfo = Depends(get_key_type)):
+ if wallet.wallet.user not in LNBITS_ADMIN_USERS:
+ raise HTTPException(
+ status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
+ )
+ return await get_nostrrelaylist()
+
+@nostr_ext.post("/api/v1/allowlist")
+async def api_relaysallowed(data: nostrRelayAllowList, wallet: WalletTypeInfo = Depends(get_key_type)):
+ if wallet.wallet.user not in LNBITS_ADMIN_USERS:
+ raise HTTPException(
+ status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
+ )
+ return await update_nostrrelayallowlist(data)
+
+@nostr_ext.post("/api/v1/denylist")
+async def api_relaysdenyed(data: nostrRelayDenyList, wallet: WalletTypeInfo = Depends(get_key_type)):
+ if wallet.wallet.user not in LNBITS_ADMIN_USERS:
+ raise HTTPException(
+ status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
+ )
+ return await update_nostrrelaydenylist(data)
\ No newline at end of file
From 2083ecbc27b00ad4aca605425a183b8122e7d995 Mon Sep 17 00:00:00 2001
From: benarc
Date: Tue, 8 Feb 2022 13:13:36 +0000
Subject: [PATCH 14/30] models decided
---
lnbits/extensions/nostradmin/__init__.py | 2 +-
lnbits/extensions/nostradmin/crud.py | 22 ++----
lnbits/extensions/nostradmin/migrations.py | 41 ++++++-----
lnbits/extensions/nostradmin/models.py | 33 +++++----
.../templates/nostradmin/index.html | 71 +++++++++++--------
lnbits/extensions/nostradmin/views.py | 5 +-
lnbits/extensions/nostradmin/views_api.py | 33 +++------
7 files changed, 100 insertions(+), 107 deletions(-)
diff --git a/lnbits/extensions/nostradmin/__init__.py b/lnbits/extensions/nostradmin/__init__.py
index 7034ca46..797542c4 100644
--- a/lnbits/extensions/nostradmin/__init__.py
+++ b/lnbits/extensions/nostradmin/__init__.py
@@ -5,7 +5,7 @@ from lnbits.helpers import template_renderer
db = Database("ext_nostradmin")
-nostr_ext: APIRouter = APIRouter(prefix="/nostradmin", tags=["nostradmin"])
+nostradmin_ext: APIRouter = APIRouter(prefix="/nostradmin", tags=["nostradmin"])
def nostr_renderer():
diff --git a/lnbits/extensions/nostradmin/crud.py b/lnbits/extensions/nostradmin/crud.py
index 08908e85..1e1bf810 100644
--- a/lnbits/extensions/nostradmin/crud.py
+++ b/lnbits/extensions/nostradmin/crud.py
@@ -12,6 +12,7 @@ from .models import (
nostrCreateConnections,
nostrRelayList,
)
+from .models import nostrKeys, nostrCreateRelays, nostrRelaySetList
###############KEYS##################
@@ -100,31 +101,20 @@ async def get_nostrrelay(nostrrelay_id: str) -> nostrRelays:
row = await db.fetchone("SELECT * FROM nostradmin.relays WHERE id = ?", (nostrrelay_id,))
return nostrRelays(**row) if row else None
-
-async def update_nostrrelayallowlist(allowlist: str) -> nostrRelayList:
+async def update_nostrrelaysetlist(data: nostrRelaySetList) -> nostrRelayList:
await db.execute(
"""
- UPDATE nostradmin.relaylist SET
+ UPDATE nostradmin.relaylists SET
+ denylist = ?,
allowlist = ?
WHERE id = ?
""",
- (allowlist, 1),
- )
- return await get_nostrrelaylist()
-
-async def update_nostrrelaydenylist(denylist: str) -> nostrRelayList:
- await db.execute(
- """
- UPDATE nostradmin.relaylist SET
- denylist = ?
- WHERE id = ?
- """,
- (denylist, 1),
+ (data.denylist, data.allowlist, 1),
)
return await get_nostrrelaylist()
async def get_nostrrelaylist() -> nostrRelayList:
- row = await db.fetchone("SELECT * FROM nostradmin.relaylist WHERE id = ?", (1,))
+ row = await db.fetchone("SELECT * FROM nostradmin.relaylists WHERE id = ?", (1,))
return nostrRelayList(**row) if row else None
diff --git a/lnbits/extensions/nostradmin/migrations.py b/lnbits/extensions/nostradmin/migrations.py
index 09b28117..590f72ea 100644
--- a/lnbits/extensions/nostradmin/migrations.py
+++ b/lnbits/extensions/nostradmin/migrations.py
@@ -1,11 +1,8 @@
from lnbits.db import Database
-db2 = Database("ext_nostr")
-
-
async def m001_initial(db):
"""
- Initial nostr table.
+ Initial nostradmin table.
"""
await db.execute(
f"""
@@ -40,24 +37,23 @@ async def m001_initial(db):
f"""
CREATE TABLE nostradmin.relaylists (
id TEXT NOT NULL PRIMARY KEY DEFAULT 1,
- allowlist TEXT NOT NULL,
- denylist TEXT NOT NULL
+ allowlist TEXT,
+ denylist TEXT
);
"""
)
- try:
- await db.execute(
- """
- INSERT INTO nostradmin.relaylist (
- id,
- denylist
- )
- VALUES (?, ?,)
- """,
- (1, "\n".join(["wss://zucks-meta-relay.com", "wss://nostradmin.cia.gov"])),
+
+ await db.execute(
+ """
+ INSERT INTO nostradmin.relaylists (
+ id,
+ denylist
)
- except:
- return
+ VALUES (?, ?)
+ """,
+ ("1", "wss://zucks-meta-relay.com\nwss://nostr.cia.gov",),
+ )
+
await db.execute(
f"""
CREATE TABLE nostradmin.connections (
@@ -67,3 +63,12 @@ async def m001_initial(db):
);
"""
)
+ await db.execute(
+ f"""
+ CREATE TABLE nostradmin.subscribed (
+ id TEXT NOT NULL PRIMARY KEY,
+ userPubkey TEXT NOT NULL,
+ subscribedPubkey TEXT NOT NULL
+ );
+ """
+ )
\ No newline at end of file
diff --git a/lnbits/extensions/nostradmin/models.py b/lnbits/extensions/nostradmin/models.py
index fd89f515..1968567f 100644
--- a/lnbits/extensions/nostradmin/models.py
+++ b/lnbits/extensions/nostradmin/models.py
@@ -5,7 +5,7 @@ from typing import Optional
from fastapi import Request
from pydantic import BaseModel
from pydantic.main import BaseModel
-
+from fastapi.param_functions import Query
class nostrKeys(BaseModel):
pubkey: str
@@ -21,28 +21,31 @@ class nostrNotes(BaseModel):
sig: str
class nostrCreateRelays(BaseModel):
- relay: str
+ relay: str = Query(None)
class nostrCreateConnections(BaseModel):
- pubkey: str
- relayid: str
+ pubkey: str = Query(None)
+ relayid: str = Query(None)
class nostrRelays(BaseModel):
- id: str
- relay: str
+ id: Optional[str]
+ relay: Optional[str]
class nostrRelayList(BaseModel):
id: str
- allowlist: str
- denylist: str
+ allowlist: Optional[str]
+ denylist: Optional[str]
-class nostrRelayDenyList(BaseModel):
- denylist: str
-
-class nostrRelayAllowList(BaseModel):
- allowlist: str
+class nostrRelaySetList(BaseModel):
+ allowlist: Optional[str]
+ denylist: Optional[str]
class nostrConnections(BaseModel):
id: str
- pubkey: str
- relayid: str
+ pubkey: Optional[str]
+ relayid: Optional[str]
+
+class nostrSubscriptions(BaseModel):
+ id: str
+ userPubkey: Optional[str]
+ subscribedPubkey: Optional[str]
\ No newline at end of file
diff --git a/lnbits/extensions/nostradmin/templates/nostradmin/index.html b/lnbits/extensions/nostradmin/templates/nostradmin/index.html
index adab98e2..27decdc8 100644
--- a/lnbits/extensions/nostradmin/templates/nostradmin/index.html
+++ b/lnbits/extensions/nostradmin/templates/nostradmin/index.html
@@ -6,7 +6,7 @@
-
Nostr
+ NOSTR RELAYS ONLINE
@@ -29,7 +29,7 @@
-
-
+
+
-
+
-
+
-
- Deny List (denys use of relays in this list)
-
-
+ Relays in this list will NOT be used
+
-
+
Update Deny List
- Reset
-
- Allow List (denys any relays not in this list)
-
-
+ ONLY relays in this list will be used
+
-
+
Update Allow List
- Reset
@@ -175,8 +175,12 @@
data: function () {
return {
listSelection: 'denylist',
- allowList: [],
- denyList: [],
+ setList: {
+ allowlist: '',
+ denylist: ''
+ },
+ nostrLinks: [],
+ filter: '',
nostrTable: {
columns: [
{
@@ -234,18 +238,20 @@
LNbits.utils.notifyApiError(error)
})
},
- setDenyList: function () {
+ setRelayList: function () {
var self = this
+ console.log(self.setList)
LNbits.api
.request(
'POST',
- '/nostradmin/api/v1/denylist',
+ '/nostradmin/api/v1/setlist',
self.g.user.wallets[0].adminkey,
- self.allowList
+ self.setList
)
.then(function (response) {
if (response.data) {
- self.denyList = response.data
+ console.log(response.data)
+ // self.denyList = response.data
}
})
.catch(function (error) {
@@ -262,13 +268,18 @@
)
.then(function (response) {
if (response.data) {
- self.denyList = response.data.denylist
- self.allowList = response.data.allowlist
+ console.log(response.data)
+ self.setList.denylist = response.data.denylist
+ self.setList.allowlist = response.data.allowlist
}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
+ },
+ exportlnurldeviceCSV: function () {
+ var self = this
+ LNbits.utils.exportCSV(self.nostrTable.columns, this.nostrLinks)
}
},
created: function () {
diff --git a/lnbits/extensions/nostradmin/views.py b/lnbits/extensions/nostradmin/views.py
index 5609c218..51297320 100644
--- a/lnbits/extensions/nostradmin/views.py
+++ b/lnbits/extensions/nostradmin/views.py
@@ -6,18 +6,19 @@ from fastapi.params import Depends
from fastapi.templating import Jinja2Templates
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse
+from . import nostradmin_ext, nostr_renderer
from lnbits.core.crud import update_payment_status
from lnbits.core.models import User
from lnbits.core.views.api import api_payment
from lnbits.decorators import check_user_exists
-from . import nostr_ext, nostr_renderer
+
templates = Jinja2Templates(directory="templates")
-@nostr_ext.get("/", response_class=HTMLResponse)
+@nostradmin_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)):
return nostr_renderer().TemplateResponse(
"nostradmin/index.html", {"request": request, "user": user.dict()}
diff --git a/lnbits/extensions/nostradmin/views_api.py b/lnbits/extensions/nostradmin/views_api.py
index da6e140b..d79315ac 100644
--- a/lnbits/extensions/nostradmin/views_api.py
+++ b/lnbits/extensions/nostradmin/views_api.py
@@ -7,11 +7,10 @@ from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
-from lnbits.extensions.nostr import nostr_ext
from lnbits.utils.exchange_rates import currencies
-from . import nostr_ext
from lnbits.settings import LNBITS_ADMIN_USERS
+from . import nostradmin_ext
from .crud import (
create_nostrkeys,
get_nostrkeys,
@@ -20,13 +19,11 @@ from .crud import (
create_nostrrelays,
get_nostrrelays,
get_nostrrelaylist,
- update_nostrrelayallowlist,
- update_nostrrelaydenylist,
+ update_nostrrelaysetlist,
create_nostrconnections,
get_nostrconnections,
)
-from .models import nostrKeys, nostrCreateRelays, nostrRelayAllowList, nostrRelayDenyList
-
+from .models import nostrKeys, nostrCreateRelays, nostrRelaySetList
# while True:
async def nostr_subscribe():
@@ -41,13 +38,7 @@ async def nostr_subscribe():
websocket_queue = asyncio.Queue(1000)
-async def internal_invoice_listener():
- while True:
- checking_id = await internal_invoice_queue.get()
- asyncio.create_task(invoice_callback_dispatcher(checking_id))
-
-
-@nostr_ext.get("/api/v1/relays")
+@nostradmin_ext.get("/api/v1/relays")
async def api_relays_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
relays = await get_nostrrelays()
@@ -62,7 +53,7 @@ async def api_relays_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
except:
None
-@nostr_ext.get("/api/v1/relaylist")
+@nostradmin_ext.get("/api/v1/relaylist")
async def api_relaylist(wallet: WalletTypeInfo = Depends(get_key_type)):
if wallet.wallet.user not in LNBITS_ADMIN_USERS:
raise HTTPException(
@@ -70,18 +61,10 @@ async def api_relaylist(wallet: WalletTypeInfo = Depends(get_key_type)):
)
return await get_nostrrelaylist()
-@nostr_ext.post("/api/v1/allowlist")
-async def api_relaysallowed(data: nostrRelayAllowList, wallet: WalletTypeInfo = Depends(get_key_type)):
+@nostradmin_ext.post("/api/v1/setlist")
+async def api_relayssetlist(data: nostrRelaySetList, wallet: WalletTypeInfo = Depends(get_key_type)):
if wallet.wallet.user not in LNBITS_ADMIN_USERS:
raise HTTPException(
status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
)
- return await update_nostrrelayallowlist(data)
-
-@nostr_ext.post("/api/v1/denylist")
-async def api_relaysdenyed(data: nostrRelayDenyList, wallet: WalletTypeInfo = Depends(get_key_type)):
- if wallet.wallet.user not in LNBITS_ADMIN_USERS:
- raise HTTPException(
- status_code=HTTPStatus.UNAUTHORIZED, detail="User not authorized."
- )
- return await update_nostrrelaydenylist(data)
\ No newline at end of file
+ return await update_nostrrelaysetlist(data)
\ No newline at end of file
From 21c316293b4c8986854ade1421e4119fc6e4e5bb Mon Sep 17 00:00:00 2001
From: benarc
Date: Tue, 8 Feb 2022 13:52:19 +0000
Subject: [PATCH 15/30] Started planning websocket daemon
---
lnbits/extensions/nostradmin/views.py | 70 ++++++++++++++++++++++-
lnbits/extensions/nostradmin/views_api.py | 12 ----
2 files changed, 68 insertions(+), 14 deletions(-)
diff --git a/lnbits/extensions/nostradmin/views.py b/lnbits/extensions/nostradmin/views.py
index 51297320..235ec7e9 100644
--- a/lnbits/extensions/nostradmin/views.py
+++ b/lnbits/extensions/nostradmin/views.py
@@ -1,5 +1,6 @@
from http import HTTPStatus
-
+import asyncio
+import asyncio
from fastapi import Request
from fastapi.param_functions import Query
from fastapi.params import Depends
@@ -7,13 +8,14 @@ from fastapi.templating import Jinja2Templates
from starlette.exceptions import HTTPException
from starlette.responses import HTMLResponse
from . import nostradmin_ext, nostr_renderer
+from fastapi import Request, WebSocket, WebSocketDisconnect
from lnbits.core.crud import update_payment_status
from lnbits.core.models import User
from lnbits.core.views.api import api_payment
from lnbits.decorators import check_user_exists
-
+from .crud import get_nostrkeys
templates = Jinja2Templates(directory="templates")
@@ -23,3 +25,67 @@ async def index(request: Request, user: User = Depends(check_user_exists)):
return nostr_renderer().TemplateResponse(
"nostradmin/index.html", {"request": request, "user": user.dict()}
)
+
+#####################################################################
+#################### NOSTR WEBSOCKET THREAD #########################
+##### THE QUEUE LOOP THREAD THING THAT LISTENS TO BUNCH OF ##########
+### WEBSOCKET CONNECTIONS, STORING DATA IN DB/PUSHING TO FRONTEND ###
+################### VIA updater() FUNCTION ##########################
+#####################################################################
+
+websocket_queue = asyncio.Queue(1000)
+
+# while True:
+async def nostr_subscribe():
+ return
+ # for the relays:
+ # async with websockets.connect("ws://localhost:8765") as websocket:
+ # for the public keys:
+ # await websocket.send("subscribe to events")
+ # await websocket.recv()
+
+#####################################################################
+################### LNBITS WEBSOCKET ROUTES #########################
+#### HERE IS WHERE LNBITS FRONTEND CAN RECEIVE AND SEND MESSAGES ####
+#####################################################################
+
+class ConnectionManager:
+ def __init__(self):
+ self.active_connections: List[WebSocket] = []
+
+ async def connect(self, websocket: WebSocket, nostr_id: str):
+ await websocket.accept()
+ websocket.id = nostr_id
+ self.active_connections.append(websocket)
+
+ def disconnect(self, websocket: WebSocket):
+ self.active_connections.remove(websocket)
+
+ async def send_personal_message(self, message: str, nostr_id: str):
+ for connection in self.active_connections:
+ if connection.id == nostr_id:
+ await connection.send_text(message)
+
+ async def broadcast(self, message: str):
+ for connection in self.active_connections:
+ await connection.send_text(message)
+
+
+manager = ConnectionManager()
+
+
+@nostradmin_ext.websocket("/nostradmin/ws/{nostr_id}", name="copilot.websocket_by_id")
+async def websocket_endpoint(websocket: WebSocket, copilot_id: str):
+ await manager.connect(websocket, nostr_id)
+ try:
+ while True:
+ data = await websocket.receive_text()
+ except WebSocketDisconnect:
+ manager.disconnect(websocket)
+
+
+async def updater(nostr_id, message):
+ copilot = await get_copilot(nostr_id)
+ if not copilot:
+ return
+ await manager.send_personal_message(f"{message}", nostr_id)
\ No newline at end of file
diff --git a/lnbits/extensions/nostradmin/views_api.py b/lnbits/extensions/nostradmin/views_api.py
index d79315ac..ad8bcf17 100644
--- a/lnbits/extensions/nostradmin/views_api.py
+++ b/lnbits/extensions/nostradmin/views_api.py
@@ -25,18 +25,6 @@ from .crud import (
)
from .models import nostrKeys, nostrCreateRelays, nostrRelaySetList
-# while True:
-async def nostr_subscribe():
- return
- # for the relays:
- # async with websockets.connect("ws://localhost:8765") as websocket:
- # for the public keys:
- # await websocket.send("subscribe to events")
- # await websocket.recv()
-
-
-websocket_queue = asyncio.Queue(1000)
-
@nostradmin_ext.get("/api/v1/relays")
async def api_relays_retrieve(wallet: WalletTypeInfo = Depends(get_key_type)):
From 0a5495f185a87a2aee8a2efd78b7dff0ad5d2384 Mon Sep 17 00:00:00 2001
From: benarc
Date: Tue, 8 Feb 2022 14:02:50 +0000
Subject: [PATCH 16/30] typo
---
lnbits/extensions/nostradmin/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lnbits/extensions/nostradmin/views.py b/lnbits/extensions/nostradmin/views.py
index 235ec7e9..f00c43a3 100644
--- a/lnbits/extensions/nostradmin/views.py
+++ b/lnbits/extensions/nostradmin/views.py
@@ -74,7 +74,7 @@ class ConnectionManager:
manager = ConnectionManager()
-@nostradmin_ext.websocket("/nostradmin/ws/{nostr_id}", name="copilot.websocket_by_id")
+@nostradmin_ext.websocket("/nostradmin/ws/{nostr_id}", name="nostr_id.websocket_by_id")
async def websocket_endpoint(websocket: WebSocket, copilot_id: str):
await manager.connect(websocket, nostr_id)
try:
From 6917813e2af357138bebb2df31d00be6148b7553 Mon Sep 17 00:00:00 2001
From: benarc
Date: Thu, 10 Feb 2022 09:58:50 +0000
Subject: [PATCH 17/30] Working, looking good
---
lnbits/extensions/nostradmin/models.py | 1 +
.../templates/nostradmin/index.html | 39 ++++++++++++-------
lnbits/extensions/nostradmin/views.py | 21 +++++++---
lnbits/extensions/nostradmin/views_api.py | 17 +++++---
4 files changed, 53 insertions(+), 25 deletions(-)
diff --git a/lnbits/extensions/nostradmin/models.py b/lnbits/extensions/nostradmin/models.py
index 1968567f..dc99b083 100644
--- a/lnbits/extensions/nostradmin/models.py
+++ b/lnbits/extensions/nostradmin/models.py
@@ -30,6 +30,7 @@ class nostrCreateConnections(BaseModel):
class nostrRelays(BaseModel):
id: Optional[str]
relay: Optional[str]
+ status: Optional[bool] = False
class nostrRelayList(BaseModel):
id: str
diff --git a/lnbits/extensions/nostradmin/templates/nostradmin/index.html b/lnbits/extensions/nostradmin/templates/nostradmin/index.html
index 27decdc8..57552bf2 100644
--- a/lnbits/extensions/nostradmin/templates/nostradmin/index.html
+++ b/lnbits/extensions/nostradmin/templates/nostradmin/index.html
@@ -29,7 +29,7 @@
-
-
-
- {{ col.value }}
+
+
+ {{ col.value }}
+
+
{{ col.value }}
+
@@ -143,7 +145,7 @@
{{SITE_TITLE}} Nostr Extension
Only Admin users can manage this extension
- Okay
+
@@ -153,7 +155,7 @@