lnbits-legend/lnbits/extensions/shop/views_api.py
2022-12-21 17:45:44 +00:00

508 lines
14 KiB
Python

from base64 import urlsafe_b64encode
from http import HTTPStatus
from typing import List, Union
from uuid import uuid4
from fastapi import Request
from fastapi.param_functions import Body, Query
from fastapi.params import Depends
from loguru import logger
from secp256k1 import PrivateKey, PublicKey
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
from lnbits.core.services import create_invoice
from lnbits.core.views.api import api_payment
from lnbits.decorators import (
WalletTypeInfo,
get_key_type,
require_admin_key,
require_invoice_key,
)
from ...helpers import urlsafe_short_hash
from . import db, shop_ext
from .crud import (
create_shop_market,
create_shop_market_stalls,
create_shop_order,
create_shop_order_details,
create_shop_product,
create_shop_stall,
create_shop_zone,
delete_shop_order,
delete_shop_product,
delete_shop_stall,
delete_shop_zone,
get_shop_chat_by_merchant,
get_shop_chat_messages,
get_shop_latest_chat_messages,
get_shop_market,
get_shop_market_stalls,
get_shop_markets,
get_shop_order,
get_shop_order_details,
get_shop_order_invoiceid,
get_shop_orders,
get_shop_product,
get_shop_products,
get_shop_stall,
get_shop_stalls,
get_shop_stalls_by_ids,
get_shop_zone,
get_shop_zones,
set_shop_order_pubkey,
update_shop_market,
update_shop_product,
update_shop_stall,
update_shop_zone,
)
from .models import (
CreateMarket,
Orders,
Products,
Stalls,
Zones,
createOrder,
createProduct,
createStalls,
createZones,
)
# from lnbits.db import open_ext_db
### Products
@shop_ext.get("/api/v1/products")
async def api_shop_products(
wallet: WalletTypeInfo = Depends(get_key_type),
all_stalls: bool = Query(False),
):
wallet_ids = [wallet.wallet.id]
if all_stalls:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
stalls = [stall.id for stall in await get_shop_stalls(wallet_ids)]
if not stalls:
return
return [product.dict() for product in await get_shop_products(stalls)]
@shop_ext.post("/api/v1/products")
@shop_ext.put("/api/v1/products/{product_id}")
async def api_shop_product_create(
data: createProduct, product_id=None, wallet: WalletTypeInfo = Depends(get_key_type)
):
if product_id:
product = await get_shop_product(product_id)
if not product:
return {"message": "Withdraw product does not exist."}
stall = await get_shop_stall(stall_id=product.stall)
if stall.wallet != wallet.wallet.id:
return {"message": "Not your withdraw product."}
product = await update_shop_product(product_id, **data.dict())
else:
product = await create_shop_product(data=data)
return product.dict()
@shop_ext.delete("/api/v1/products/{product_id}")
async def api_shop_products_delete(
product_id, wallet: WalletTypeInfo = Depends(require_admin_key)
):
product = await get_shop_product(product_id)
if not product:
return {"message": "Product does not exist."}
stall = await get_shop_stall(product.stall)
if stall.wallet != wallet.wallet.id:
return {"message": "Not your Shop."}
await delete_shop_product(product_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
# # # Shippingzones
@shop_ext.get("/api/v1/zones")
async def api_shop_zones(wallet: WalletTypeInfo = Depends(get_key_type)):
return await get_shop_zones(wallet.wallet.user)
@shop_ext.post("/api/v1/zones")
async def api_shop_zone_create(
data: createZones, wallet: WalletTypeInfo = Depends(get_key_type)
):
zone = await create_shop_zone(user=wallet.wallet.user, data=data)
return zone.dict()
@shop_ext.post("/api/v1/zones/{zone_id}")
async def api_shop_zone_update(
data: createZones,
zone_id: str,
wallet: WalletTypeInfo = Depends(require_admin_key),
):
zone = await get_shop_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_shop_zone(zone_id, **data.dict())
return zone
@shop_ext.delete("/api/v1/zones/{zone_id}")
async def api_shop_zone_delete(
zone_id, wallet: WalletTypeInfo = Depends(require_admin_key)
):
zone = await get_shop_zone(zone_id)
if not zone:
return {"message": "zone does not exist."}
if zone.user != wallet.wallet.user:
return {"message": "Not your zone."}
await delete_shop_zone(zone_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
# # # Stalls
@shop_ext.get("/api/v1/stalls")
async def api_shop_stalls(
wallet: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False)
):
wallet_ids = [wallet.wallet.id]
if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
return [stall.dict() for stall in await get_shop_stalls(wallet_ids)]
@shop_ext.post("/api/v1/stalls")
@shop_ext.put("/api/v1/stalls/{stall_id}")
async def api_shop_stall_create(
data: createStalls,
stall_id: str = None,
wallet: WalletTypeInfo = Depends(require_invoice_key),
):
if stall_id:
stall = await get_shop_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_shop_stall(stall_id, **data.dict())
else:
stall = await create_shop_stall(data=data)
return stall.dict()
@shop_ext.delete("/api/v1/stalls/{stall_id}")
async def api_shop_stall_delete(
stall_id: str, wallet: WalletTypeInfo = Depends(require_admin_key)
):
stall = await get_shop_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_shop_stall(stall_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
###Orders
@shop_ext.get("/api/v1/orders")
async def api_shop_orders(
wallet: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False)
):
wallet_ids = [wallet.wallet.id]
if all_wallets:
wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
orders = await get_shop_orders(wallet_ids)
orders_with_details = []
for order in orders:
order = order.dict()
order["details"] = await get_shop_order_details(order["id"])
orders_with_details.append(order)
try:
return orders_with_details # [order for order in orders]
# return [order.dict() for order in await get_shop_orders(wallet_ids)]
except:
return {"message": "We could not retrieve the orders."}
@shop_ext.get("/api/v1/orders/{order_id}")
async def api_shop_order_by_id(order_id: str):
order = (await get_shop_order(order_id)).dict()
order["details"] = await get_shop_order_details(order_id)
return order
@shop_ext.post("/api/v1/orders")
async def api_shop_order_create(data: createOrder):
ref = urlsafe_short_hash()
payment_hash, payment_request = await create_invoice(
wallet_id=data.wallet,
amount=data.total,
memo=f"New order on Diagon alley",
extra={
"tag": "shop",
"reference": ref,
},
)
order_id = await create_shop_order(invoiceid=payment_hash, data=data)
logger.debug(f"ORDER ID {order_id}")
logger.debug(f"PRODUCTS {data.products}")
await create_shop_order_details(order_id=order_id, data=data.products)
return {
"payment_hash": payment_hash,
"payment_request": payment_request,
"order_reference": ref,
}
@shop_ext.get("/api/v1/orders/payments/{payment_hash}")
async def api_shop_check_payment(payment_hash: str):
order = await get_shop_order_invoiceid(payment_hash)
if not order:
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Order does not exist."
)
try:
status = await api_payment(payment_hash)
except Exception as exc:
logger.error(exc)
return {"paid": False}
return status
@shop_ext.delete("/api/v1/orders/{order_id}")
async def api_shop_order_delete(
order_id: str, wallet: WalletTypeInfo = Depends(require_admin_key)
):
order = await get_shop_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_shop_order(order_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
@shop_ext.get("/api/v1/orders/paid/{order_id}")
async def api_shop_order_paid(
order_id, wallet: WalletTypeInfo = Depends(require_admin_key)
):
await db.execute(
"UPDATE shop.orders SET paid = ? WHERE id = ?",
(
True,
order_id,
),
)
return "", HTTPStatus.OK
@shop_ext.get("/api/v1/order/pubkey/{payment_hash}/{pubkey}")
async def api_shop_order_pubkey(payment_hash: str, pubkey: str):
await set_shop_order_pubkey(payment_hash, pubkey)
return "", HTTPStatus.OK
@shop_ext.get("/api/v1/orders/shipped/{order_id}")
async def api_shop_order_shipped(
order_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
await db.execute(
"UPDATE shop.orders SET shipped = ? WHERE id = ?",
(
True,
order_id,
),
)
order = await db.fetchone("SELECT * FROM shop.orders WHERE id = ?", (order_id,))
return order
###List products based on stall id
@shop_ext.get("/api/v1/stall/products/{stall_id}")
async def api_shop_stall_products(
stall_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
rows = await db.fetchone("SELECT * FROM shop.stalls WHERE id = ?", (stall_id,))
if not rows:
return {"message": "Stall does not exist."}
products = db.fetchone("SELECT * FROM shop.products WHERE wallet = ?", (rows[1],))
if not products:
return {"message": "No products"}
return [products.dict() for products in await get_shop_products(rows[1])]
###Check a product has been shipped
@shop_ext.get("/api/v1/stall/checkshipped/{checking_id}")
async def api_shop_stall_checkshipped(
checking_id, wallet: WalletTypeInfo = Depends(get_key_type)
):
rows = await db.fetchone(
"SELECT * FROM shop.orders WHERE invoiceid = ?", (checking_id,)
)
return {"shipped": rows["shipped"]}
###Place order
# @shop_ext.post("/api/v1/stall/order/{stall_id}")
# async def api_shop_stall_order(
# stall_id, data: createOrder, wallet: WalletTypeInfo = Depends(get_key_type)
# ):
# product = await get_shop_product(data.productid)
# shipping = await get_shop_stall(stall_id)
# if data.shippingzone == 1:
# shippingcost = shipping.zone1cost # missing in model
# else:
# shippingcost = shipping.zone2cost # missing in model
# checking_id, payment_request = await create_invoice(
# wallet_id=product.wallet,
# amount=shippingcost + (data.quantity * product.price),
# memo=shipping.wallet,
# )
# selling_id = urlsafe_b64encode(uuid4().bytes_le).decode("utf-8")
# await db.execute(
# """
# INSERT INTO shop.orders (id, productid, wallet, product, quantity, shippingzone, address, email, invoiceid, paid, shipped)
# VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
# """,
# (
# selling_id,
# data.productid,
# product.wallet, # doesn't exist in model
# product.product,
# data.quantity,
# data.shippingzone,
# data.address,
# data.email,
# checking_id,
# False,
# False,
# ),
# )
# return {"checking_id": checking_id, "payment_request": payment_request}
##
# MARKETS
##
@shop_ext.get("/api/v1/markets")
async def api_shop_markets(wallet: WalletTypeInfo = Depends(get_key_type)):
# await get_shop_market_stalls(market_id="FzpWnMyHQMcRppiGVua4eY")
try:
return [market.dict() for market in await get_shop_markets(wallet.wallet.user)]
except:
return {"message": "We could not retrieve the markets."}
@shop_ext.get("/api/v1/markets/{market_id}/stalls")
async def api_shop_market_stalls(market_id: str):
stall_ids = await get_shop_market_stalls(market_id)
return stall_ids
@shop_ext.post("/api/v1/markets")
@shop_ext.put("/api/v1/markets/{market_id}")
async def api_shop_stall_create(
data: CreateMarket,
market_id: str = None,
wallet: WalletTypeInfo = Depends(require_invoice_key),
):
if market_id:
market = await get_shop_market(market_id)
if not market:
return {"message": "Market does not exist."}
if market.usr != wallet.wallet.user:
return {"message": "Not your market."}
market = await update_shop_market(market_id, **data.dict())
else:
market = await create_shop_market(data=data)
await create_shop_market_stalls(market_id=market.id, data=data.stalls)
return market.dict()
## KEYS
@shop_ext.get("/api/v1/keys")
async def api_shop_generate_keys():
private_key = PrivateKey()
public_key = private_key.pubkey.serialize().hex()
while not public_key.startswith("02"):
private_key = PrivateKey()
public_key = private_key.pubkey.serialize().hex()
return {"privkey": private_key.serialize(), "pubkey": public_key[2:]}
## MESSAGES/CHAT
@shop_ext.get("/api/v1/chat/messages/merchant")
async def api_get_merchant_messages(
orders: str = Query(...), wallet: WalletTypeInfo = Depends(require_admin_key)
):
return [msg.dict() for msg in await get_shop_chat_by_merchant(orders.split(","))]
@shop_ext.get("/api/v1/chat/messages/{room_name}")
async def api_get_latest_chat_msg(room_name: str, all_messages: bool = Query(False)):
if all_messages:
messages = await get_shop_chat_messages(room_name)
else:
messages = await get_shop_latest_chat_messages(room_name)
return messages