Merge branch 'lnbits:main' into main

This commit is contained in:
blackcoffeexbt 2022-10-25 11:31:02 +01:00 committed by GitHub
commit f62b4975b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 162 additions and 168 deletions

View file

@ -79,3 +79,8 @@ For the invoice to work you must have a publicly accessible URL in your LNbits.
- `LNBITS_BACKEND_WALLET_CLASS`: **OpenNodeWallet** - `LNBITS_BACKEND_WALLET_CLASS`: **OpenNodeWallet**
- `OPENNODE_API_ENDPOINT`: https://api.opennode.com/ - `OPENNODE_API_ENDPOINT`: https://api.opennode.com/
- `OPENNODE_KEY`: opennodeAdminApiKey - `OPENNODE_KEY`: opennodeAdminApiKey
### Cliche Wallet
- `CLICHE_ENDPOINT`: ws://127.0.0.1:12000

View file

@ -91,7 +91,6 @@ def create_app(config_object="lnbits.settings") -> FastAPI:
) )
app.add_middleware(GZipMiddleware, minimum_size=1000) app.add_middleware(GZipMiddleware, minimum_size=1000)
# app.add_middleware(ASGIProxyFix)
check_funding_source(app) check_funding_source(app)
register_assets(app) register_assets(app)

View file

@ -361,6 +361,35 @@ new Vue({
this.receive.status = 'pending' this.receive.status = 'pending'
}) })
}, },
onInitQR: async function (promise) {
try {
await promise
} catch (error) {
let mapping = {
NotAllowedError: 'ERROR: you need to grant camera access permission',
NotFoundError: 'ERROR: no camera on this device',
NotSupportedError:
'ERROR: secure context required (HTTPS, localhost)',
NotReadableError: 'ERROR: is the camera already in use?',
OverconstrainedError: 'ERROR: installed cameras are not suitable',
StreamApiNotSupportedError:
'ERROR: Stream API is not supported in this browser',
InsecureContextError:
'ERROR: Camera access is only permitted in secure context. Use HTTPS or localhost rather than HTTP.'
}
let valid_error = Object.keys(mapping).filter(key => {
return error.name === key
})
let camera_error = valid_error
? mapping[valid_error]
: `ERROR: Camera error (${error.name})`
this.parse.camera.show = false
this.$q.notify({
message: camera_error,
type: 'negative'
})
}
},
decodeQR: function (res) { decodeQR: function (res) {
this.parse.data.request = res this.parse.data.request = res
this.decodeRequest() this.decodeRequest()

View file

@ -653,6 +653,7 @@
<q-responsive :ratio="1"> <q-responsive :ratio="1">
<qrcode-stream <qrcode-stream
@decode="decodeQR" @decode="decodeQR"
@init="onInitQR"
class="rounded-borders" class="rounded-borders"
></qrcode-stream> ></qrcode-stream>
</q-responsive> </q-responsive>
@ -671,6 +672,7 @@
<div class="text-center q-mb-lg"> <div class="text-center q-mb-lg">
<qrcode-stream <qrcode-stream
@decode="decodeQR" @decode="decodeQR"
@init="onInitQR"
class="rounded-borders" class="rounded-borders"
></qrcode-stream> ></qrcode-stream>
</div> </div>

View file

@ -476,7 +476,7 @@ async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type
except: except:
# parse internet identifier (user@domain.com) # parse internet identifier (user@domain.com)
name_domain = code.split("@") name_domain = code.split("@")
if len(name_domain) == 2 and len(name_domain[1].split(".")) == 2: if len(name_domain) == 2 and len(name_domain[1].split(".")) >= 2:
name, domain = name_domain name, domain = name_domain
url = ( url = (
("http://" if domain.endswith(".onion") else "https://") ("http://" if domain.endswith(".onion") else "https://")

View file

@ -10,7 +10,7 @@ from .models import Copilots, CreateCopilotData
async def create_copilot( async def create_copilot(
data: CreateCopilotData, inkey: Optional[str] = "" data: CreateCopilotData, inkey: Optional[str] = ""
) -> Copilots: ) -> Optional[Copilots]:
copilot_id = urlsafe_short_hash() copilot_id = urlsafe_short_hash()
await db.execute( await db.execute(
""" """
@ -67,19 +67,19 @@ async def create_copilot(
async def update_copilot( async def update_copilot(
data: CreateCopilotData, copilot_id: Optional[str] = "" data: CreateCopilotData, copilot_id: str
) -> Optional[Copilots]: ) -> Optional[Copilots]:
q = ", ".join([f"{field[0]} = ?" for field in data]) q = ", ".join([f"{field[0]} = ?" for field in data])
items = [f"{field[1]}" for field in data] items = [f"{field[1]}" for field in data]
items.append(copilot_id) items.append(copilot_id)
await db.execute(f"UPDATE copilot.newer_copilots SET {q} WHERE id = ?", (items)) await db.execute(f"UPDATE copilot.newer_copilots SET {q} WHERE id = ?", (items,))
row = await db.fetchone( row = await db.fetchone(
"SELECT * FROM copilot.newer_copilots WHERE id = ?", (copilot_id,) "SELECT * FROM copilot.newer_copilots WHERE id = ?", (copilot_id,)
) )
return Copilots(**row) if row else None return Copilots(**row) if row else None
async def get_copilot(copilot_id: str) -> Copilots: async def get_copilot(copilot_id: str) -> Optional[Copilots]:
row = await db.fetchone( row = await db.fetchone(
"SELECT * FROM copilot.newer_copilots WHERE id = ?", (copilot_id,) "SELECT * FROM copilot.newer_copilots WHERE id = ?", (copilot_id,)
) )

View file

@ -26,7 +26,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None: async def on_invoice_paid(payment: Payment) -> None:
webhook = None webhook = None
data = None data = None
if payment.extra.get("tag") != "copilot": if not payment.extra or payment.extra.get("tag") != "copilot":
# not an copilot invoice # not an copilot invoice
return return
@ -71,12 +71,12 @@ async def on_invoice_paid(payment: Payment) -> None:
async def mark_webhook_sent(payment: Payment, status: int) -> None: async def mark_webhook_sent(payment: Payment, status: int) -> None:
payment.extra["wh_status"] = status if payment.extra:
payment.extra["wh_status"] = status
await core_db.execute( await core_db.execute(
""" """
UPDATE apipayments SET extra = ? UPDATE apipayments SET extra = ?
WHERE hash = ? WHERE hash = ?
""", """,
(json.dumps(payment.extra), payment.payment_hash), (json.dumps(payment.extra), payment.payment_hash),
) )

View file

@ -15,7 +15,9 @@ templates = Jinja2Templates(directory="templates")
@copilot_ext.get("/", response_class=HTMLResponse) @copilot_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)): async def index(
request: Request, user: User = Depends(check_user_exists) # type: ignore
):
return copilot_renderer().TemplateResponse( return copilot_renderer().TemplateResponse(
"copilot/index.html", {"request": request, "user": user.dict()} "copilot/index.html", {"request": request, "user": user.dict()}
) )
@ -44,7 +46,7 @@ class ConnectionManager:
async def connect(self, websocket: WebSocket, copilot_id: str): async def connect(self, websocket: WebSocket, copilot_id: str):
await websocket.accept() await websocket.accept()
websocket.id = copilot_id websocket.id = copilot_id # type: ignore
self.active_connections.append(websocket) self.active_connections.append(websocket)
def disconnect(self, websocket: WebSocket): def disconnect(self, websocket: WebSocket):
@ -52,7 +54,7 @@ class ConnectionManager:
async def send_personal_message(self, message: str, copilot_id: str): async def send_personal_message(self, message: str, copilot_id: str):
for connection in self.active_connections: for connection in self.active_connections:
if connection.id == copilot_id: if connection.id == copilot_id: # type: ignore
await connection.send_text(message) await connection.send_text(message)
async def broadcast(self, message: str): async def broadcast(self, message: str):

View file

@ -23,7 +23,7 @@ from .views import updater
@copilot_ext.get("/api/v1/copilot") @copilot_ext.get("/api/v1/copilot")
async def api_copilots_retrieve( async def api_copilots_retrieve(
req: Request, wallet: WalletTypeInfo = Depends(get_key_type) req: Request, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
): ):
wallet_user = wallet.wallet.user wallet_user = wallet.wallet.user
copilots = [copilot.dict() for copilot in await get_copilots(wallet_user)] copilots = [copilot.dict() for copilot in await get_copilots(wallet_user)]
@ -37,7 +37,7 @@ async def api_copilots_retrieve(
async def api_copilot_retrieve( async def api_copilot_retrieve(
req: Request, req: Request,
copilot_id: str = Query(None), copilot_id: str = Query(None),
wallet: WalletTypeInfo = Depends(get_key_type), wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore
): ):
copilot = await get_copilot(copilot_id) copilot = await get_copilot(copilot_id)
if not copilot: if not copilot:
@ -54,7 +54,7 @@ async def api_copilot_retrieve(
async def api_copilot_create_or_update( async def api_copilot_create_or_update(
data: CreateCopilotData, data: CreateCopilotData,
copilot_id: str = Query(None), copilot_id: str = Query(None),
wallet: WalletTypeInfo = Depends(require_admin_key), wallet: WalletTypeInfo = Depends(require_admin_key), # type: ignore
): ):
data.user = wallet.wallet.user data.user = wallet.wallet.user
data.wallet = wallet.wallet.id data.wallet = wallet.wallet.id
@ -67,7 +67,8 @@ async def api_copilot_create_or_update(
@copilot_ext.delete("/api/v1/copilot/{copilot_id}") @copilot_ext.delete("/api/v1/copilot/{copilot_id}")
async def api_copilot_delete( async def api_copilot_delete(
copilot_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key) copilot_id: str = Query(None),
wallet: WalletTypeInfo = Depends(require_admin_key), # type: ignore
): ):
copilot = await get_copilot(copilot_id) copilot = await get_copilot(copilot_id)

View file

@ -98,21 +98,21 @@ async def get_discordbot_wallet(wallet_id: str) -> Optional[Wallets]:
return Wallets(**row) if row else None return Wallets(**row) if row else None
async def get_discordbot_wallets(admin_id: str) -> Optional[Wallets]: async def get_discordbot_wallets(admin_id: str) -> List[Wallets]:
rows = await db.fetchall( rows = await db.fetchall(
"SELECT * FROM discordbot.wallets WHERE admin = ?", (admin_id,) "SELECT * FROM discordbot.wallets WHERE admin = ?", (admin_id,)
) )
return [Wallets(**row) for row in rows] return [Wallets(**row) for row in rows]
async def get_discordbot_users_wallets(user_id: str) -> Optional[Wallets]: async def get_discordbot_users_wallets(user_id: str) -> List[Wallets]:
rows = await db.fetchall( rows = await db.fetchall(
"""SELECT * FROM discordbot.wallets WHERE "user" = ?""", (user_id,) """SELECT * FROM discordbot.wallets WHERE "user" = ?""", (user_id,)
) )
return [Wallets(**row) for row in rows] return [Wallets(**row) for row in rows]
async def get_discordbot_wallet_transactions(wallet_id: str) -> Optional[Payment]: async def get_discordbot_wallet_transactions(wallet_id: str) -> List[Payment]:
return await get_payments( return await get_payments(
wallet_id=wallet_id, complete=True, pending=False, outgoing=True, incoming=True wallet_id=wallet_id, complete=True, pending=False, outgoing=True, incoming=True
) )

View file

@ -9,7 +9,9 @@ from . import discordbot_ext, discordbot_renderer
@discordbot_ext.get("/", response_class=HTMLResponse) @discordbot_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)): async def index(
request: Request, user: User = Depends(check_user_exists) # type: ignore
):
return discordbot_renderer().TemplateResponse( return discordbot_renderer().TemplateResponse(
"discordbot/index.html", {"request": request, "user": user.dict()} "discordbot/index.html", {"request": request, "user": user.dict()}
) )

View file

@ -27,32 +27,37 @@ from .models import CreateUserData, CreateUserWallet
@discordbot_ext.get("/api/v1/users", status_code=HTTPStatus.OK) @discordbot_ext.get("/api/v1/users", status_code=HTTPStatus.OK)
async def api_discordbot_users(wallet: WalletTypeInfo = Depends(get_key_type)): async def api_discordbot_users(
wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore
):
user_id = wallet.wallet.user user_id = wallet.wallet.user
return [user.dict() for user in await get_discordbot_users(user_id)] return [user.dict() for user in await get_discordbot_users(user_id)]
@discordbot_ext.get("/api/v1/users/{user_id}", status_code=HTTPStatus.OK) @discordbot_ext.get("/api/v1/users/{user_id}", status_code=HTTPStatus.OK)
async def api_discordbot_user(user_id, wallet: WalletTypeInfo = Depends(get_key_type)): async def api_discordbot_user(
user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
):
user = await get_discordbot_user(user_id) user = await get_discordbot_user(user_id)
return user.dict() if user:
return user.dict()
@discordbot_ext.post("/api/v1/users", status_code=HTTPStatus.CREATED) @discordbot_ext.post("/api/v1/users", status_code=HTTPStatus.CREATED)
async def api_discordbot_users_create( async def api_discordbot_users_create(
data: CreateUserData, wallet: WalletTypeInfo = Depends(get_key_type) data: CreateUserData, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
): ):
user = await create_discordbot_user(data) user = await create_discordbot_user(data)
full = user.dict() full = user.dict()
full["wallets"] = [ wallets = await get_discordbot_users_wallets(user.id)
wallet.dict() for wallet in await get_discordbot_users_wallets(user.id) if wallets:
] full["wallets"] = [wallet for wallet in wallets]
return full return full
@discordbot_ext.delete("/api/v1/users/{user_id}") @discordbot_ext.delete("/api/v1/users/{user_id}")
async def api_discordbot_users_delete( async def api_discordbot_users_delete(
user_id, wallet: WalletTypeInfo = Depends(get_key_type) user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
): ):
user = await get_discordbot_user(user_id) user = await get_discordbot_user(user_id)
if not user: if not user:
@ -75,7 +80,7 @@ async def api_discordbot_activate_extension(
raise HTTPException( raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist." status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
) )
update_user_extension(user_id=userid, extension=extension, active=active) await update_user_extension(user_id=userid, extension=extension, active=active)
return {"extension": "updated"} return {"extension": "updated"}
@ -84,7 +89,7 @@ async def api_discordbot_activate_extension(
@discordbot_ext.post("/api/v1/wallets") @discordbot_ext.post("/api/v1/wallets")
async def api_discordbot_wallets_create( async def api_discordbot_wallets_create(
data: CreateUserWallet, wallet: WalletTypeInfo = Depends(get_key_type) data: CreateUserWallet, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
): ):
user = await create_discordbot_wallet( user = await create_discordbot_wallet(
user_id=data.user_id, wallet_name=data.wallet_name, admin_id=data.admin_id user_id=data.user_id, wallet_name=data.wallet_name, admin_id=data.admin_id
@ -93,28 +98,30 @@ async def api_discordbot_wallets_create(
@discordbot_ext.get("/api/v1/wallets") @discordbot_ext.get("/api/v1/wallets")
async def api_discordbot_wallets(wallet: WalletTypeInfo = Depends(get_key_type)): async def api_discordbot_wallets(
wallet: WalletTypeInfo = Depends(get_key_type), # type: ignore
):
admin_id = wallet.wallet.user admin_id = wallet.wallet.user
return [wallet.dict() for wallet in await get_discordbot_wallets(admin_id)] return await get_discordbot_wallets(admin_id)
@discordbot_ext.get("/api/v1/transactions/{wallet_id}") @discordbot_ext.get("/api/v1/transactions/{wallet_id}")
async def api_discordbot_wallet_transactions( async def api_discordbot_wallet_transactions(
wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
): ):
return await get_discordbot_wallet_transactions(wallet_id) return await get_discordbot_wallet_transactions(wallet_id)
@discordbot_ext.get("/api/v1/wallets/{user_id}") @discordbot_ext.get("/api/v1/wallets/{user_id}")
async def api_discordbot_users_wallets( async def api_discordbot_users_wallets(
user_id, wallet: WalletTypeInfo = Depends(get_key_type) user_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
): ):
return [s_wallet.dict() for s_wallet in await get_discordbot_users_wallets(user_id)] return await get_discordbot_users_wallets(user_id)
@discordbot_ext.delete("/api/v1/wallets/{wallet_id}") @discordbot_ext.delete("/api/v1/wallets/{wallet_id}")
async def api_discordbot_wallets_delete( async def api_discordbot_wallets_delete(
wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) wallet_id, wallet: WalletTypeInfo = Depends(get_key_type) # type: ignore
): ):
get_wallet = await get_discordbot_wallet(wallet_id) get_wallet = await get_discordbot_wallet(wallet_id)
if not get_wallet: if not get_wallet:

View file

@ -12,7 +12,10 @@ templates = Jinja2Templates(directory="templates")
@example_ext.get("/", response_class=HTMLResponse) @example_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)): async def index(
request: Request,
user: User = Depends(check_user_exists), # type: ignore
):
return example_renderer().TemplateResponse( return example_renderer().TemplateResponse(
"example/index.html", {"request": request, "user": user.dict()} "example/index.html", {"request": request, "user": user.dict()}
) )

View file

@ -3,10 +3,10 @@ from typing import List, Optional, Union
from lnbits.helpers import urlsafe_short_hash from lnbits.helpers import urlsafe_short_hash
from . import db from . import db
from .models import CreateDomain, Domains, Subdomains from .models import CreateDomain, CreateSubdomain, Domains, Subdomains
async def create_subdomain(payment_hash, wallet, data: CreateDomain) -> Subdomains: async def create_subdomain(payment_hash, wallet, data: CreateSubdomain) -> Subdomains:
await db.execute( await db.execute(
""" """
INSERT INTO subdomains.subdomain (id, domain, email, subdomain, ip, wallet, sats, duration, paid, record_type) INSERT INTO subdomains.subdomain (id, domain, email, subdomain, ip, wallet, sats, duration, paid, record_type)

View file

@ -3,24 +3,24 @@ from pydantic.main import BaseModel
class CreateDomain(BaseModel): class CreateDomain(BaseModel):
wallet: str = Query(...) wallet: str = Query(...) # type: ignore
domain: str = Query(...) domain: str = Query(...) # type: ignore
cf_token: str = Query(...) cf_token: str = Query(...) # type: ignore
cf_zone_id: str = Query(...) cf_zone_id: str = Query(...) # type: ignore
webhook: str = Query("") webhook: str = Query("") # type: ignore
description: str = Query(..., min_length=0) description: str = Query(..., min_length=0) # type: ignore
cost: int = Query(..., ge=0) cost: int = Query(..., ge=0) # type: ignore
allowed_record_types: str = Query(...) allowed_record_types: str = Query(...) # type: ignore
class CreateSubdomain(BaseModel): class CreateSubdomain(BaseModel):
domain: str = Query(...) domain: str = Query(...) # type: ignore
subdomain: str = Query(...) subdomain: str = Query(...) # type: ignore
email: str = Query(...) email: str = Query(...) # type: ignore
ip: str = Query(...) ip: str = Query(...) # type: ignore
sats: int = Query(..., ge=0) sats: int = Query(..., ge=0) # type: ignore
duration: int = Query(...) duration: int = Query(...) # type: ignore
record_type: str = Query(...) record_type: str = Query(...) # type: ignore
class Domains(BaseModel): class Domains(BaseModel):

View file

@ -20,7 +20,7 @@ async def wait_for_paid_invoices():
async def on_invoice_paid(payment: Payment) -> None: async def on_invoice_paid(payment: Payment) -> None:
if payment.extra.get("tag") != "lnsubdomain": if not payment.extra or payment.extra.get("tag") != "lnsubdomain":
# not an lnurlp invoice # not an lnurlp invoice
return return
@ -37,7 +37,7 @@ async def on_invoice_paid(payment: Payment) -> None:
) )
### Use webhook to notify about cloudflare registration ### Use webhook to notify about cloudflare registration
if domain.webhook: if domain and domain.webhook:
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
try: try:
r = await client.post( r = await client.post(

View file

@ -16,7 +16,9 @@ templates = Jinja2Templates(directory="templates")
@subdomains_ext.get("/", response_class=HTMLResponse) @subdomains_ext.get("/", response_class=HTMLResponse)
async def index(request: Request, user: User = Depends(check_user_exists)): async def index(
request: Request, user: User = Depends(check_user_exists) # type:ignore
):
return subdomains_renderer().TemplateResponse( return subdomains_renderer().TemplateResponse(
"subdomains/index.html", {"request": request, "user": user.dict()} "subdomains/index.html", {"request": request, "user": user.dict()}
) )

View file

@ -29,12 +29,15 @@ from .crud import (
@subdomains_ext.get("/api/v1/domains") @subdomains_ext.get("/api/v1/domains")
async def api_domains( async def api_domains(
g: WalletTypeInfo = Depends(get_key_type), all_wallets: bool = Query(False) g: WalletTypeInfo = Depends(get_key_type), # type: ignore
all_wallets: bool = Query(False),
): ):
wallet_ids = [g.wallet.id] wallet_ids = [g.wallet.id]
if all_wallets: if all_wallets:
wallet_ids = (await get_user(g.wallet.user)).wallet_ids user = await get_user(g.wallet.user)
if user is not None:
wallet_ids = user.wallet_ids
return [domain.dict() for domain in await get_domains(wallet_ids)] return [domain.dict() for domain in await get_domains(wallet_ids)]
@ -42,7 +45,9 @@ async def api_domains(
@subdomains_ext.post("/api/v1/domains") @subdomains_ext.post("/api/v1/domains")
@subdomains_ext.put("/api/v1/domains/{domain_id}") @subdomains_ext.put("/api/v1/domains/{domain_id}")
async def api_domain_create( async def api_domain_create(
data: CreateDomain, domain_id=None, g: WalletTypeInfo = Depends(get_key_type) data: CreateDomain,
domain_id=None,
g: WalletTypeInfo = Depends(get_key_type), # type: ignore
): ):
if domain_id: if domain_id:
domain = await get_domain(domain_id) domain = await get_domain(domain_id)
@ -63,7 +68,9 @@ async def api_domain_create(
@subdomains_ext.delete("/api/v1/domains/{domain_id}") @subdomains_ext.delete("/api/v1/domains/{domain_id}")
async def api_domain_delete(domain_id, g: WalletTypeInfo = Depends(get_key_type)): async def api_domain_delete(
domain_id, g: WalletTypeInfo = Depends(get_key_type) # type: ignore
):
domain = await get_domain(domain_id) domain = await get_domain(domain_id)
if not domain: if not domain:
@ -82,12 +89,14 @@ async def api_domain_delete(domain_id, g: WalletTypeInfo = Depends(get_key_type)
@subdomains_ext.get("/api/v1/subdomains") @subdomains_ext.get("/api/v1/subdomains")
async def api_subdomains( async def api_subdomains(
all_wallets: bool = Query(False), g: WalletTypeInfo = Depends(get_key_type) all_wallets: bool = Query(False), g: WalletTypeInfo = Depends(get_key_type) # type: ignore
): ):
wallet_ids = [g.wallet.id] wallet_ids = [g.wallet.id]
if all_wallets: if all_wallets:
wallet_ids = (await get_user(g.wallet.user)).wallet_ids user = await get_user(g.wallet.user)
if user is not None:
wallet_ids = user.wallet_ids
return [domain.dict() for domain in await get_subdomains(wallet_ids)] return [domain.dict() for domain in await get_subdomains(wallet_ids)]
@ -173,7 +182,9 @@ async def api_subdomain_send_subdomain(payment_hash):
@subdomains_ext.delete("/api/v1/subdomains/{subdomain_id}") @subdomains_ext.delete("/api/v1/subdomains/{subdomain_id}")
async def api_subdomain_delete(subdomain_id, g: WalletTypeInfo = Depends(get_key_type)): async def api_subdomain_delete(
subdomain_id, g: WalletTypeInfo = Depends(get_key_type) # type: ignore
):
subdomain = await get_subdomain(subdomain_id) subdomain = await get_subdomain(subdomain_id)
if not subdomain: if not subdomain:

View file

@ -1,95 +0,0 @@
from functools import partial
from typing import Callable, List, Optional
from urllib.parse import urlparse
from urllib.request import parse_http_list as _parse_list_header
from quart import Request
from quart_trio.asgi import TrioASGIHTTPConnection
from werkzeug.datastructures import Headers
class ASGIProxyFix(TrioASGIHTTPConnection):
def _create_request_from_scope(self, send: Callable) -> Request:
headers = Headers()
headers["Remote-Addr"] = (self.scope.get("client") or ["<local>"])[0]
for name, value in self.scope["headers"]:
headers.add(name.decode("latin1").title(), value.decode("latin1"))
if self.scope["http_version"] < "1.1":
headers.setdefault("Host", self.app.config["SERVER_NAME"] or "")
path = self.scope["path"]
path = path if path[0] == "/" else urlparse(path).path
x_proto = self._get_real_value(1, headers.get("X-Forwarded-Proto"))
if x_proto:
self.scope["scheme"] = x_proto
x_host = self._get_real_value(1, headers.get("X-Forwarded-Host"))
if x_host:
headers["host"] = x_host.lower()
return self.app.request_class(
self.scope["method"],
self.scope["scheme"],
path,
self.scope["query_string"],
headers,
self.scope.get("root_path", ""),
self.scope["http_version"],
max_content_length=self.app.config["MAX_CONTENT_LENGTH"],
body_timeout=self.app.config["BODY_TIMEOUT"],
send_push_promise=partial(self._send_push_promise, send),
scope=self.scope,
)
def _get_real_value(self, trusted: int, value: Optional[str]) -> Optional[str]:
"""Get the real value from a list header based on the configured
number of trusted proxies.
:param trusted: Number of values to trust in the header.
:param value: Comma separated list header value to parse.
:return: The real value, or ``None`` if there are fewer values
than the number of trusted proxies.
.. versionchanged:: 1.0
Renamed from ``_get_trusted_comma``.
.. versionadded:: 0.15
"""
if not (trusted and value):
return None
values = self.parse_list_header(value)
if len(values) >= trusted:
return values[-trusted]
return None
def parse_list_header(self, value: str) -> List[str]:
result = []
for item in _parse_list_header(value):
if item[:1] == item[-1:] == '"':
item = self.unquote_header_value(item[1:-1])
result.append(item)
return result
def unquote_header_value(self, value: str, is_filename: bool = False) -> str:
r"""Unquotes a header value. (Reversal of :func:`quote_header_value`).
This does not use the real unquoting but what browsers are actually
using for quoting.
.. versionadded:: 0.5
:param value: the header value to unquote.
:param is_filename: The value represents a filename or path.
"""
if value and value[0] == value[-1] == '"':
# this is not the real unquoting, but fixing this so that the
# RFC is met will result in bugs with internet explorer and
# probably some other browsers as well. IE for example is
# uploading files with "C:\foo\bar.txt" as filename
value = value[1:-1]
# if this is a filename and the starting characters look like
# a UNC path, then just return the value without quotes. Using the
# replace sequence below on a UNC path has the effect of turning
# the leading double slash into a single slash and then
# _fix_ie_filename() doesn't work correctly. See #458.
if not is_filename or value[:2] != "\\\\":
return value.replace("\\\\", "\\").replace('\\"', '"')
return value

View file

@ -89,8 +89,34 @@ profile = "black"
ignore_missing_imports = "True" ignore_missing_imports = "True"
files = "lnbits" files = "lnbits"
exclude = """(?x)( exclude = """(?x)(
^lnbits/extensions. ^lnbits/extensions/bleskomat.
| ^lnbits/wallets/lnd_grpc_files. | ^lnbits/extensions/boltz.
| ^lnbits/extensions/boltcards.
| ^lnbits/extensions/events.
| ^lnbits/extensions/hivemind.
| ^lnbits/extensions/invoices.
| ^lnbits/extensions/jukebox.
| ^lnbits/extensions/livestream.
| ^lnbits/extensions/lnaddress.
| ^lnbits/extensions/lndhub.
| ^lnbits/extensions/lnticket.
| ^lnbits/extensions/lnurldevice.
| ^lnbits/extensions/lnurlp.
| ^lnbits/extensions/lnurlpayout.
| ^lnbits/extensions/ngrok.
| ^lnbits/extensions/offlineshop.
| ^lnbits/extensions/paywall.
| ^lnbits/extensions/satsdice.
| ^lnbits/extensions/satspay.
| ^lnbits/extensions/scrub.
| ^lnbits/extensions/splitpayments.
| ^lnbits/extensions/streamalerts.
| ^lnbits/extensions/tipjar.
| ^lnbits/extensions/tpos.
| ^lnbits/extensions/usermanager.
| ^lnbits/extensions/watchonly.
| ^lnbits/extensions/withdraw.
| ^lnbits/wallets/lnd_grpc_files.
)""" )"""
[tool.pytest.ini_options] [tool.pytest.ini_options]