Repository navigation
Expand file tree
/
Copy pathservices_tabs.py
More file actions
142 lines (119 loc) · 4.71 KB
/
Copy pathservices_tabs.py
File metadata and controls
142 lines (119 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
from http import HTTPStatus
from typing import Any
import httpx
from fastapi import HTTPException
from lnbits.core.crud import (
get_installed_extension,
get_user_active_extensions_ids,
)
from lnbits.settings import settings
from .helpers import create_internal_user_access_token
from .models import Tpos
_TAB_STATUSES = {"open", "suspended", "closed"}
async def tabs_available_for_user(user_id: str) -> bool:
installed = await get_installed_extension("tabs")
if not installed or not installed.active:
return False
active_extensions = await get_user_active_extensions_ids(user_id)
return "tabs" in active_extensions
async def fetch_tabs_for_tpos(
user_id: str,
wallet_id: str,
status: str | None = "open",
query: str | None = None,
) -> list[dict[str, Any]]:
if status and status not in _TAB_STATUSES:
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="Invalid tab status filter.",
)
payload = await _tabs_request(user_id, "GET", "/tabs")
if not isinstance(payload, list):
return []
tabs = [tab for tab in payload if tab.get("wallet") == wallet_id]
if status:
tabs = [tab for tab in tabs if tab.get("status") == status]
if query:
needle = query.lower()
tabs = [
tab
for tab in tabs
if needle in (tab.get("name") or "").lower()
or needle in (tab.get("customer_name") or "").lower()
or needle in (tab.get("reference") or "").lower()
or needle in (tab.get("id") or "").lower()
]
tabs.sort(key=lambda tab: tab.get("updated_at") or "", reverse=True)
return tabs[:50]
async def create_tab_for_tpos(user_id: str, payload: dict[str, Any]) -> dict[str, Any]:
return await _tabs_request(user_id, "POST", "/tabs", json=payload)
async def create_tab_charge_for_tpos(
user_id: str,
tab_id: str,
payload: dict[str, Any],
) -> dict[str, Any]:
return await _tabs_request(user_id, "POST", f"/tabs/{tab_id}/entries", json=payload)
async def fetch_single_tab_for_tpos(user_id: str, tab_id: str) -> dict[str, Any]:
return await _tabs_request(user_id, "GET", f"/tabs/{tab_id}")
async def get_tab_for_tpos(user_id: str, tpos: Tpos, tab_id: str) -> dict[str, Any]:
tab = await fetch_single_tab_for_tpos(user_id, tab_id)
if tab.get("wallet") != tpos.wallet:
raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Tab not found.")
if (tab.get("currency") or "sats").lower() != (tpos.currency or "sats").lower():
raise HTTPException(
status_code=HTTPStatus.BAD_REQUEST,
detail="Tab currency must match TPoS currency.",
)
return tab
def tab_settlement_tolerance(currency: str | None) -> float:
return 1 if (currency or "sats").lower() == "sats" else 0.01
async def create_tab_settlement_for_tpos(
user_id: str,
tab_id: str,
payload: dict[str, Any],
) -> dict[str, Any]:
return await _tabs_request(
user_id, "POST", f"/tabs/{tab_id}/settlements", json=payload
)
async def _tabs_request(
user_id: str,
method: str,
path: str,
*,
json: dict[str, Any] | None = None,
) -> Any:
access = create_internal_user_access_token(user_id)
try:
async with httpx.AsyncClient() as client:
resp = await client.request(
method,
url=f"http://{settings.host}:{settings.port}/tabs/api/v1{path}",
headers={"Authorization": f"Bearer {access}"},
json=json,
)
resp.raise_for_status()
except httpx.HTTPStatusError as exc:
raise _raise_tabs_bridge_error(exc) from exc
except httpx.RequestError as exc:
raise HTTPException(
status_code=HTTPStatus.BAD_GATEWAY,
detail="Tabs service is temporarily unavailable.",
) from exc
return resp.json()
def _raise_tabs_bridge_error(exc: httpx.HTTPStatusError) -> HTTPException:
status_code = exc.response.status_code if exc.response else HTTPStatus.BAD_GATEWAY
if status_code == HTTPStatus.NOT_FOUND:
detail = "Tab not found."
elif status_code in (HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN):
detail = "Tabs action not allowed for this TPoS."
elif status_code == HTTPStatus.BAD_REQUEST:
try:
response_detail = exc.response.json().get("detail")
except Exception:
response_detail = None
detail = response_detail or "Invalid tabs request."
else:
detail = "Tabs service is temporarily unavailable."
if status_code >= HTTPStatus.INTERNAL_SERVER_ERROR:
status_code = HTTPStatus.BAD_GATEWAY
return HTTPException(status_code=status_code, detail=detail)