mirror of
https://github.com/lnbits/lnbits.git
synced 2025-09-19 12:01:12 +02:00
@@ -1,3 +1,17 @@
|
|||||||
|
|
||||||
|
## Defining a route with path parameters
|
||||||
|
**old:**
|
||||||
|
```python
|
||||||
|
# with <>
|
||||||
|
@offlineshop_ext.route("/lnurl/<item_id>", methods=["GET"])
|
||||||
|
```
|
||||||
|
|
||||||
|
**new:**
|
||||||
|
```python
|
||||||
|
# with curly braces: {}
|
||||||
|
@offlineshop_ext.get("/lnurl/{item_id}")
|
||||||
|
```
|
||||||
|
|
||||||
## Check if a user exists and access user object
|
## Check if a user exists and access user object
|
||||||
**old:**
|
**old:**
|
||||||
```python
|
```python
|
||||||
|
@@ -95,9 +95,14 @@ def register_routes(app: FastAPI) -> None:
|
|||||||
try:
|
try:
|
||||||
ext_module = importlib.import_module(f"lnbits.extensions.{ext.code}")
|
ext_module = importlib.import_module(f"lnbits.extensions.{ext.code}")
|
||||||
ext_route = getattr(ext_module, f"{ext.code}_ext")
|
ext_route = getattr(ext_module, f"{ext.code}_ext")
|
||||||
|
|
||||||
|
ext_statics = getattr(ext_module, f"{ext.code}_static_files")
|
||||||
|
for s in ext_statics:
|
||||||
|
app.mount(s["path"], s["app"], s["name"])
|
||||||
|
|
||||||
app.include_router(ext_route)
|
app.include_router(ext_route)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
print(str(e))
|
||||||
raise ImportError(
|
raise ImportError(
|
||||||
f"Please make sure that the extension `{ext.code}` follows conventions."
|
f"Please make sure that the extension `{ext.code}` follows conventions."
|
||||||
)
|
)
|
||||||
|
@@ -1,15 +1,41 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter, FastAPI
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from starlette.routing import Mount
|
||||||
|
|
||||||
from lnbits.db import Database
|
from lnbits.db import Database
|
||||||
|
from lnbits.helpers import template_renderer
|
||||||
|
|
||||||
db = Database("ext_offlineshop")
|
db = Database("ext_offlineshop")
|
||||||
|
|
||||||
|
offlineshop_static_files = [
|
||||||
|
{
|
||||||
|
"path": "/offlineshop/static",
|
||||||
|
"app": StaticFiles(directory="lnbits/extensions/offlineshop/static"),
|
||||||
|
"name": "offlineshop_static",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
offlineshop_ext: APIRouter = APIRouter(
|
offlineshop_ext: APIRouter = APIRouter(
|
||||||
prefix="/Extension",
|
prefix="/offlineshop",
|
||||||
tags=["Offlineshop"]
|
tags=["Offlineshop"],
|
||||||
|
# routes=[
|
||||||
|
# Mount(
|
||||||
|
# "/static",
|
||||||
|
# app=StaticFiles(directory="lnbits/extensions/offlineshop/static"),
|
||||||
|
# name="offlineshop_static",
|
||||||
|
# )
|
||||||
|
# ],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
from .views_api import * # noqa
|
def offlineshop_renderer():
|
||||||
from .views import * # noqa
|
return template_renderer(
|
||||||
|
[
|
||||||
|
"lnbits/extensions/offlineshop/templates",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
from .lnurl import * # noqa
|
from .lnurl import * # noqa
|
||||||
|
from .views import * # noqa
|
||||||
|
from .views_api import * # noqa
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
|
from fastapi.params import Query
|
||||||
|
|
||||||
|
from starlette.requests import Request
|
||||||
|
from lnbits.helpers import url_for
|
||||||
from lnurl import LnurlPayResponse, LnurlPayActionResponse, LnurlErrorResponse # type: ignore
|
from lnurl import LnurlPayResponse, LnurlPayActionResponse, LnurlErrorResponse # type: ignore
|
||||||
|
|
||||||
from lnbits.core.services import create_invoice
|
from lnbits.core.services import create_invoice
|
||||||
@@ -8,8 +12,8 @@ from . import offlineshop_ext
|
|||||||
from .crud import get_shop, get_item
|
from .crud import get_shop, get_item
|
||||||
|
|
||||||
|
|
||||||
@offlineshop_ext.get("/lnurl/<item_id>")
|
@offlineshop_ext.get("/lnurl/{item_id}", name="offlineshop.lnurl_response")
|
||||||
async def lnurl_response(item_id):
|
async def lnurl_response(item_id: int = Query(...)):
|
||||||
item = await get_item(item_id)
|
item = await get_item(item_id)
|
||||||
if not item:
|
if not item:
|
||||||
return {"status": "ERROR", "reason": "Item not found."}
|
return {"status": "ERROR", "reason": "Item not found."}
|
||||||
@@ -34,7 +38,7 @@ async def lnurl_response(item_id):
|
|||||||
|
|
||||||
|
|
||||||
@offlineshop_ext.get("/lnurl/cb/<item_id>")
|
@offlineshop_ext.get("/lnurl/cb/<item_id>")
|
||||||
async def lnurl_callback(item_id):
|
async def lnurl_callback(request: Request, item_id: int):
|
||||||
item = await get_item(item_id)
|
item = await get_item(item_id)
|
||||||
if not item:
|
if not item:
|
||||||
return {"status": "ERROR", "reason": "Couldn't find item."}
|
return {"status": "ERROR", "reason": "Couldn't find item."}
|
||||||
@@ -51,12 +55,12 @@ async def lnurl_callback(item_id):
|
|||||||
amount_received = int(request.args.get("amount") or 0)
|
amount_received = int(request.args.get("amount") or 0)
|
||||||
if amount_received < min:
|
if amount_received < min:
|
||||||
return LnurlErrorResponse(
|
return LnurlErrorResponse(
|
||||||
reason=f"Amount {amount_received} is smaller than minimum {min}."
|
reason=f"Amount {amount_received} is smaller than minimum {min}."
|
||||||
).dict()
|
).dict()
|
||||||
elif amount_received > max:
|
elif amount_received > max:
|
||||||
return LnurlErrorResponse(
|
return LnurlErrorResponse(
|
||||||
reason=f"Amount {amount_received} is greater than maximum {max}."
|
reason=f"Amount {amount_received} is greater than maximum {max}."
|
||||||
).dict()
|
).dict()
|
||||||
|
|
||||||
shop = await get_shop(item.shop)
|
shop = await get_shop(item.shop)
|
||||||
|
|
||||||
@@ -75,7 +79,7 @@ async def lnurl_callback(item_id):
|
|||||||
|
|
||||||
resp = LnurlPayActionResponse(
|
resp = LnurlPayActionResponse(
|
||||||
pr=payment_request,
|
pr=payment_request,
|
||||||
success_action=item.success_action(shop, payment_hash) if shop.method else None,
|
success_action=item.success_action(shop, payment_hash, request) if shop.method else None,
|
||||||
routes=[],
|
routes=[],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -8,6 +8,7 @@ from lnurl import encode as lnurl_encode # type: ignore
|
|||||||
from lnurl.types import LnurlPayMetadata # type: ignore
|
from lnurl.types import LnurlPayMetadata # type: ignore
|
||||||
from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore
|
from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from starlette.requests import Request
|
||||||
from .helpers import totp
|
from .helpers import totp
|
||||||
|
|
||||||
shop_counters: Dict = {}
|
shop_counters: Dict = {}
|
||||||
@@ -82,20 +83,16 @@ class Item(BaseModel):
|
|||||||
id: int
|
id: int
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
image: str
|
image: Optional[str]
|
||||||
enabled: bool
|
enabled: bool
|
||||||
price: int
|
price: int
|
||||||
unit: str
|
unit: str
|
||||||
|
|
||||||
@property
|
def values(self, req: Request):
|
||||||
def lnurl(self) -> str:
|
values = self.dict()
|
||||||
return lnurl_encode(
|
values["lnurl"] = lnurl_encode(
|
||||||
url_for("offlineshop.lnurl_response", item_id=self.id, _external=True)
|
req.url_for("offlineshop.lnurl_response", item_id=self.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
def values(self):
|
|
||||||
values = self._asdict()
|
|
||||||
values["lnurl"] = self.lnurl
|
|
||||||
return values
|
return values
|
||||||
|
|
||||||
async def lnurlpay_metadata(self) -> LnurlPayMetadata:
|
async def lnurlpay_metadata(self) -> LnurlPayMetadata:
|
||||||
@@ -107,14 +104,14 @@ class Item(BaseModel):
|
|||||||
return LnurlPayMetadata(json.dumps(metadata))
|
return LnurlPayMetadata(json.dumps(metadata))
|
||||||
|
|
||||||
def success_action(
|
def success_action(
|
||||||
self, shop: Shop, payment_hash: str
|
self, shop: Shop, payment_hash: str, req: Request
|
||||||
) -> Optional[LnurlPaySuccessAction]:
|
) -> Optional[LnurlPaySuccessAction]:
|
||||||
if not shop.wordlist:
|
if not shop.wordlist:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return UrlAction(
|
return UrlAction(
|
||||||
url=url_for(
|
url=req.url_for(
|
||||||
"offlineshop.confirmation_code", p=payment_hash, _external=True
|
"offlineshop.confirmation_code", p=payment_hash
|
||||||
),
|
),
|
||||||
description="Open to get the confirmation code for your purchase.",
|
description="Open to get the confirmation code for your purchase.",
|
||||||
)
|
)
|
||||||
|
@@ -64,7 +64,7 @@
|
|||||||
<code
|
<code
|
||||||
>curl -X GET {{ request.url_root
|
>curl -X GET {{ request.url_root
|
||||||
}}/offlineshop/api/v1/offlineshop/items -H "Content-Type:
|
}}/offlineshop/api/v1/offlineshop/items -H "Content-Type:
|
||||||
application/json" -H "X-Api-Key: {{ g.user.wallets[0].inkey }}" -d
|
application/json" -H "X-Api-Key: {{ user.wallets[0].inkey }}" -d
|
||||||
'{"name": <string>, "description": <string>, "image":
|
'{"name": <string>, "description": <string>, "image":
|
||||||
<data-uri string>, "price": <integer>, "unit": <"sat"
|
<data-uri string>, "price": <integer>, "unit": <"sat"
|
||||||
or "USD">}'
|
or "USD">}'
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
<h5 class="text-caption q-mt-sm q-mb-none">Curl example</h5>
|
||||||
<code
|
<code
|
||||||
>curl -X GET {{ request.url_root }}/offlineshop/api/v1/offlineshop -H
|
>curl -X GET {{ request.url_root }}/offlineshop/api/v1/offlineshop -H
|
||||||
"X-Api-Key: {{ g.user.wallets[0].inkey }}"
|
"X-Api-Key: {{ user.wallets[0].inkey }}"
|
||||||
</code>
|
</code>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
>curl -X GET {{ request.url_root
|
>curl -X GET {{ request.url_root
|
||||||
}}/offlineshop/api/v1/offlineshop/items/<item_id> -H
|
}}/offlineshop/api/v1/offlineshop/items/<item_id> -H
|
||||||
"Content-Type: application/json" -H "X-Api-Key: {{
|
"Content-Type: application/json" -H "X-Api-Key: {{
|
||||||
g.user.wallets[0].inkey }}" -d '{"name": <string>,
|
user.wallets[0].inkey }}" -d '{"name": <string>,
|
||||||
"description": <string>, "image": <data-uri string>,
|
"description": <string>, "image": <data-uri string>,
|
||||||
"price": <integer>, "unit": <"sat" or "USD">}'
|
"price": <integer>, "unit": <"sat" or "USD">}'
|
||||||
</code>
|
</code>
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
<code
|
<code
|
||||||
>curl -X GET {{ request.url_root
|
>curl -X GET {{ request.url_root
|
||||||
}}/offlineshop/api/v1/offlineshop/items/<item_id> -H "X-Api-Key:
|
}}/offlineshop/api/v1/offlineshop/items/<item_id> -H "X-Api-Key:
|
||||||
{{ g.user.wallets[0].inkey }}"
|
{{ user.wallets[0].inkey }}"
|
||||||
</code>
|
</code>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
@@ -331,5 +331,5 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
||||||
<script src="https://cdn.jsdelivr.net/npm/pica@6.1.1/dist/pica.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/pica@6.1.1/dist/pica.min.js"></script>
|
||||||
<script src="/offlineshop/static/js/index.js"></script>
|
<script src="{{ url_for('offlineshop_static', path='js/index.js') }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -2,25 +2,24 @@ import time
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
|
from fastapi.params import Depends
|
||||||
|
from starlette.responses import HTMLResponse
|
||||||
|
|
||||||
from lnbits.decorators import check_user_exists
|
from lnbits.decorators import check_user_exists
|
||||||
from lnbits.core.models import Payment
|
from lnbits.core.models import Payment, User
|
||||||
from lnbits.core.crud import get_standalone_payment
|
from lnbits.core.crud import get_standalone_payment
|
||||||
|
|
||||||
from . import offlineshop_ext
|
from . import offlineshop_ext, offlineshop_renderer
|
||||||
from .crud import get_item, get_shop
|
from .crud import get_item, get_shop
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import Request, HTTPException
|
||||||
from fastapi.templating import Jinja2Templates
|
|
||||||
|
|
||||||
templates = Jinja2Templates(directory="templates")
|
|
||||||
|
|
||||||
@offlineshop_ext.get("/")
|
|
||||||
# @validate_uuids(["usr"], required=True)
|
|
||||||
# @check_user_exists()
|
|
||||||
async def index(request: Request):
|
|
||||||
return await templates.TemplateResponse("offlineshop/index.html", {"request": request,"user":g.user})
|
|
||||||
|
|
||||||
|
|
||||||
@offlineshop_ext.get("/print")
|
@offlineshop_ext.get("/", response_class=HTMLResponse)
|
||||||
|
async def index(request: Request, user: User = Depends(check_user_exists)):
|
||||||
|
return offlineshop_renderer().TemplateResponse("offlineshop/index.html", {"request": request, "user": user.dict()})
|
||||||
|
|
||||||
|
|
||||||
|
@offlineshop_ext.get("/print", response_class=HTMLResponse)
|
||||||
async def print_qr_codes(request: Request):
|
async def print_qr_codes(request: Request):
|
||||||
items = []
|
items = []
|
||||||
for item_id in request.args.get("items").split(","):
|
for item_id in request.args.get("items").split(","):
|
||||||
@@ -34,29 +33,32 @@ async def print_qr_codes(request: Request):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return await templates.TemplateResponse("offlineshop/print.html", {"request": request,"items":items})
|
return offlineshop_renderer().TemplateResponse("offlineshop/print.html", {"request": request,"items":items})
|
||||||
|
|
||||||
|
|
||||||
@offlineshop_ext.get("/confirmation")
|
@offlineshop_ext.get("/confirmation")
|
||||||
async def confirmation_code():
|
async def confirmation_code(p: str):
|
||||||
style = "<style>* { font-size: 100px}</style>"
|
style = "<style>* { font-size: 100px}</style>"
|
||||||
|
|
||||||
payment_hash = request.args.get("p")
|
payment_hash = p
|
||||||
payment: Payment = await get_standalone_payment(payment_hash)
|
payment: Payment = await get_standalone_payment(payment_hash)
|
||||||
if not payment:
|
if not payment:
|
||||||
return (
|
raise HTTPException(
|
||||||
f"Couldn't find the payment {payment_hash}." + style,
|
status_code=HTTPStatus.NOT_FOUND,
|
||||||
HTTPStatus.NOT_FOUND,
|
detail=f"Couldn't find the payment {payment_hash}." + style
|
||||||
)
|
)
|
||||||
if payment.pending:
|
if payment.pending:
|
||||||
return (
|
raise HTTPException(
|
||||||
f"Payment {payment_hash} wasn't received yet. Please try again in a minute."
|
status_code=HTTPStatus.PAYMENT_REQUIRED,
|
||||||
+ style,
|
detail=f"Payment {payment_hash} wasn't received yet. Please try again in a minute." + style
|
||||||
HTTPStatus.PAYMENT_REQUIRED,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if payment.time + 60 * 15 < time.time():
|
if payment.time + 60 * 15 < time.time():
|
||||||
return "too much time has passed." + style
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.REQUEST_TIMEOUT,
|
||||||
|
detail="Too much time has passed." + style
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
item = await get_item(payment.extra.get("item"))
|
item = await get_item(payment.extra.get("item"))
|
||||||
shop = await get_shop(item.shop)
|
shop = await get_shop(item.shop)
|
||||||
|
@@ -1,10 +1,16 @@
|
|||||||
from typing import Optional
|
import json
|
||||||
|
|
||||||
|
from typing import List, Optional
|
||||||
|
from fastapi.params import Depends
|
||||||
from pydantic.main import BaseModel
|
from pydantic.main import BaseModel
|
||||||
|
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl # type: ignore
|
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl
|
||||||
|
from starlette.exceptions import HTTPException
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import HTMLResponse, JSONResponse # type: ignore
|
||||||
|
|
||||||
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
|
from lnbits.decorators import WalletTypeInfo, get_key_type
|
||||||
from lnbits.utils.exchange_rates import currencies
|
from lnbits.utils.exchange_rates import currencies
|
||||||
from lnbits.requestvars import g
|
from lnbits.requestvars import g
|
||||||
|
|
||||||
@@ -22,46 +28,43 @@ from .models import ShopCounter
|
|||||||
|
|
||||||
@offlineshop_ext.get("/api/v1/currencies")
|
@offlineshop_ext.get("/api/v1/currencies")
|
||||||
async def api_list_currencies_available():
|
async def api_list_currencies_available():
|
||||||
return jsonify(list(currencies.keys()))
|
return json.dumps(list(currencies.keys()))
|
||||||
|
|
||||||
|
|
||||||
@offlineshop_ext.get("/api/v1/offlineshop")
|
@offlineshop_ext.get("/api/v1/offlineshop")
|
||||||
@api_check_wallet_key("invoice")
|
# @api_check_wallet_key("invoice")
|
||||||
async def api_shop_from_wallet():
|
async def api_shop_from_wallet(r: Request, wallet: WalletTypeInfo = Depends(get_key_type)):
|
||||||
shop = await get_or_create_shop_by_wallet(g().wallet.id)
|
shop = await get_or_create_shop_by_wallet(wallet.wallet.id)
|
||||||
items = await get_items(shop.id)
|
items = await get_items(shop.id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return (
|
return {
|
||||||
{
|
**shop.dict(),
|
||||||
**shop._asdict(),
|
**{
|
||||||
**{
|
"otp_key": shop.otp_key,
|
||||||
"otp_key": shop.otp_key,
|
"items": [item.values(r) for item in items],
|
||||||
"items": [item.values() for item in items],
|
},
|
||||||
},
|
}
|
||||||
},
|
|
||||||
HTTPStatus.OK,
|
|
||||||
)
|
|
||||||
except LnurlInvalidUrl:
|
except LnurlInvalidUrl:
|
||||||
return (
|
raise HTTPException(
|
||||||
{
|
status_code=HTTPStatus.UPGRADE_REQUIRED,
|
||||||
"message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor."
|
detail="LNURLs need to be delivered over a publically accessible `https` domain or Tor.",
|
||||||
},
|
|
||||||
HTTPStatus.UPGRADE_REQUIRED,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CreateItemsData(BaseModel):
|
class CreateItemsData(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
image: Optional[str]
|
image: Optional[str]
|
||||||
price: int
|
price: int
|
||||||
unit: str
|
unit: str
|
||||||
|
|
||||||
|
|
||||||
@offlineshop_ext.post("/api/v1/offlineshop/items")
|
@offlineshop_ext.post("/api/v1/offlineshop/items")
|
||||||
@offlineshop_ext.put("/api/v1/offlineshop/items/{item_id}")
|
@offlineshop_ext.put("/api/v1/offlineshop/items/{item_id}")
|
||||||
@api_check_wallet_key("invoice")
|
# @api_check_wallet_key("invoice")
|
||||||
async def api_add_or_update_item(data: CreateItemsData, item_id=None):
|
async def api_add_or_update_item(data: CreateItemsData, item_id=None, wallet: WalletTypeInfo = Depends(get_key_type)):
|
||||||
shop = await get_or_create_shop_by_wallet(g().wallet.id)
|
shop = await get_or_create_shop_by_wallet(wallet.wallet.id)
|
||||||
if item_id == None:
|
if item_id == None:
|
||||||
await add_item(
|
await add_item(
|
||||||
shop.id,
|
shop.id,
|
||||||
@@ -71,7 +74,7 @@ async def api_add_or_update_item(data: CreateItemsData, item_id=None):
|
|||||||
data.price,
|
data.price,
|
||||||
data.unit,
|
data.unit,
|
||||||
)
|
)
|
||||||
return "", HTTPStatus.CREATED
|
return HTMLResponse(status_code=HTTPStatus.CREATED)
|
||||||
else:
|
else:
|
||||||
await update_item(
|
await update_item(
|
||||||
shop.id,
|
shop.id,
|
||||||
@@ -82,36 +85,35 @@ async def api_add_or_update_item(data: CreateItemsData, item_id=None):
|
|||||||
data.price,
|
data.price,
|
||||||
data.unit,
|
data.unit,
|
||||||
)
|
)
|
||||||
return "", HTTPStatus.OK
|
|
||||||
|
|
||||||
|
|
||||||
@offlineshop_ext.delete("/api/v1/offlineshop/items/{item_id}")
|
@offlineshop_ext.delete("/api/v1/offlineshop/items/{item_id}")
|
||||||
@api_check_wallet_key("invoice")
|
# @api_check_wallet_key("invoice")
|
||||||
async def api_delete_item(item_id):
|
async def api_delete_item(item_id, wallet: WalletTypeInfo = Depends(get_key_type)):
|
||||||
shop = await get_or_create_shop_by_wallet(g().wallet.id)
|
shop = await get_or_create_shop_by_wallet(wallet.wallet.id)
|
||||||
await delete_item_from_shop(shop.id, item_id)
|
await delete_item_from_shop(shop.id, item_id)
|
||||||
return "", HTTPStatus.NO_CONTENT
|
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
class CreateMethodData(BaseModel):
|
class CreateMethodData(BaseModel):
|
||||||
method: str
|
method: str
|
||||||
wordlist: Optional[str]
|
wordlist: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
@offlineshop_ext.put("/api/v1/offlineshop/method")
|
@offlineshop_ext.put("/api/v1/offlineshop/method")
|
||||||
@api_check_wallet_key("invoice")
|
# @api_check_wallet_key("invoice")
|
||||||
async def api_set_method(data: CreateMethodData):
|
async def api_set_method(data: CreateMethodData, wallet: WalletTypeInfo = Depends(get_key_type)):
|
||||||
method = data.method
|
method = data.method
|
||||||
|
|
||||||
wordlist = data.wordlist.split("\n") if data.wordlist else None
|
wordlist = data.wordlist.split("\n") if data.wordlist else None
|
||||||
wordlist = [word.strip() for word in wordlist if word.strip()]
|
wordlist = [word.strip() for word in wordlist if word.strip()]
|
||||||
|
|
||||||
shop = await get_or_create_shop_by_wallet(g().wallet.id)
|
shop = await get_or_create_shop_by_wallet(wallet.wallet.id)
|
||||||
if not shop:
|
if not shop:
|
||||||
return "", HTTPStatus.NOT_FOUND
|
raise HTTPException(status_code=HTTPStatus.NOT_FOUND)
|
||||||
|
|
||||||
updated_shop = await set_method(shop.id, method, "\n".join(wordlist))
|
updated_shop = await set_method(shop.id, method, "\n".join(wordlist))
|
||||||
if not updated_shop:
|
if not updated_shop:
|
||||||
return "", HTTPStatus.NOT_FOUND
|
raise HTTPException(status_code=HTTPStatus.NOT_FOUND)
|
||||||
|
|
||||||
ShopCounter.reset(updated_shop)
|
ShopCounter.reset(updated_shop)
|
||||||
return "", HTTPStatus.OK
|
|
||||||
|
@@ -149,9 +149,9 @@ def url_for(
|
|||||||
url = f"{base}{endpoint}{url_params}"
|
url = f"{base}{endpoint}{url_params}"
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def template_renderer() -> Jinja2Templates:
|
def template_renderer(additional_folders: List = []) -> Jinja2Templates:
|
||||||
t = Jinja2Templates(
|
t = Jinja2Templates(
|
||||||
loader=jinja2.FileSystemLoader(["lnbits/templates", "lnbits/core/templates"]),
|
loader=jinja2.FileSystemLoader(["lnbits/templates", "lnbits/core/templates", *additional_folders]),
|
||||||
)
|
)
|
||||||
t.env.globals["SITE_TITLE"] = settings.LNBITS_SITE_TITLE
|
t.env.globals["SITE_TITLE"] = settings.LNBITS_SITE_TITLE
|
||||||
t.env.globals["SITE_TAGLINE"] = settings.LNBITS_SITE_TAGLINE
|
t.env.globals["SITE_TAGLINE"] = settings.LNBITS_SITE_TAGLINE
|
||||||
|
@@ -5,6 +5,7 @@ import typing
|
|||||||
|
|
||||||
from starlette import templating
|
from starlette import templating
|
||||||
from starlette.datastructures import QueryParams
|
from starlette.datastructures import QueryParams
|
||||||
|
from starlette.requests import Request
|
||||||
|
|
||||||
from lnbits.requestvars import g
|
from lnbits.requestvars import g
|
||||||
|
|
||||||
@@ -22,8 +23,8 @@ class Jinja2Templates(templating.Jinja2Templates):
|
|||||||
def get_environment(self, loader: "jinja2.BaseLoader") -> "jinja2.Environment":
|
def get_environment(self, loader: "jinja2.BaseLoader") -> "jinja2.Environment":
|
||||||
@jinja2.contextfunction
|
@jinja2.contextfunction
|
||||||
def url_for(context: dict, name: str, **path_params: typing.Any) -> str:
|
def url_for(context: dict, name: str, **path_params: typing.Any) -> str:
|
||||||
request = context["request"]
|
request: Request = context["request"] # type: starlette.requests.Request
|
||||||
return request.url_for(name, **path_params)
|
return request.app.url_path_for(name, **path_params)
|
||||||
|
|
||||||
def url_params_update(init: QueryParams, **new: typing.Any) -> QueryParams:
|
def url_params_update(init: QueryParams, **new: typing.Any) -> QueryParams:
|
||||||
values = dict(init)
|
values = dict(init)
|
||||||
|
Reference in New Issue
Block a user