This commit is contained in:
dni ⚡
2024-10-10 09:08:07 +02:00
parent 0b9e328363
commit 6e1b746056
8 changed files with 59 additions and 38 deletions
+15 -9
View File
@@ -230,7 +230,7 @@ async def get_user(
else: else:
account = account_or_id account = account_or_id
extensions = await get_user_active_extensions_ids(account.id, conn) extensions = await get_user_active_extensions_ids(account.id, conn)
wallets = await get_wallets(account.id, conn) wallets = await get_wallets(account.id, False, conn=conn)
return User( return User(
id=account.id, id=account.id,
email=account.email, email=account.email,
@@ -508,27 +508,33 @@ async def delete_unused_wallets(
async def get_wallet( async def get_wallet(
wallet_id: str, conn: Optional[Connection] = None wallet_id: str, deleted: Optional[bool] = None, conn: Optional[Connection] = None
) -> Optional[Wallet]: ) -> Optional[Wallet]:
where = "AND deleted = :deleted" if deleted is not None else ""
return await (conn or db).fetchone( return await (conn or db).fetchone(
""" f"""
SELECT *, COALESCE(( SELECT *, COALESCE((
SELECT balance FROM balances WHERE wallet_id = wallets.id SELECT balance FROM balances WHERE wallet_id = wallets.id
), 0) AS balance_msat FROM wallets WHERE id = :wallet ), 0) AS balance_msat FROM wallets
WHERE id = :wallet {where}
""", """,
{"wallet": wallet_id}, {"wallet": wallet_id, "deleted": deleted},
Wallet, Wallet,
) )
async def get_wallets(user_id: str, conn: Optional[Connection] = None) -> list[Wallet]: async def get_wallets(
user_id: str, deleted: Optional[bool] = None, conn: Optional[Connection] = None
) -> list[Wallet]:
where = "AND deleted = :deleted" if deleted is not None else ""
return await (conn or db).fetchall( return await (conn or db).fetchall(
""" f"""
SELECT *, COALESCE(( SELECT *, COALESCE((
SELECT balance FROM balances WHERE wallet_id = wallets.id SELECT balance FROM balances WHERE wallet_id = wallets.id
), 0) AS balance_msat FROM wallets WHERE "user" = :user ), 0) AS balance_msat FROM wallets
WHERE "user" = :user {where}
""", """,
{"user": user_id}, {"user": user_id, "deleted": deleted},
Wallet, Wallet,
) )
@@ -37,6 +37,7 @@
icon="content_copy" icon="content_copy"
size="sm" size="sm"
color="primary" color="primary"
class="q-ml-xs"
@click="copyText(props.row.id)" @click="copyText(props.row.id)"
> >
<q-tooltip>Copy Wallet ID</q-tooltip> <q-tooltip>Copy Wallet ID</q-tooltip>
@@ -45,6 +46,7 @@
v-if="!props.row.deleted" v-if="!props.row.deleted"
:wallet_id="props.row.id" :wallet_id="props.row.id"
:callback="topupCallback" :callback="topupCallback"
class="q-ml-xs"
></lnbits-update-balance> ></lnbits-update-balance>
<q-btn <q-btn
round round
@@ -52,6 +54,7 @@
icon="vpn_key" icon="vpn_key"
size="sm" size="sm"
color="primary" color="primary"
class="q-ml-xs"
@click="copyText(props.row.adminkey)" @click="copyText(props.row.adminkey)"
> >
<q-tooltip>Copy Admin Key</q-tooltip> <q-tooltip>Copy Admin Key</q-tooltip>
@@ -62,6 +65,7 @@
icon="vpn_key" icon="vpn_key"
size="sm" size="sm"
color="secondary" color="secondary"
class="q-ml-xs"
@click="copyText(props.row.inkey)" @click="copyText(props.row.inkey)"
> >
<q-tooltip>Copy Invoice Key</q-tooltip> <q-tooltip>Copy Invoice Key</q-tooltip>
@@ -72,6 +76,7 @@
icon="toggle_off" icon="toggle_off"
size="sm" size="sm"
color="secondary" color="secondary"
class="q-ml-xs"
@click="undeleteUserWallet(props.row.user, props.row.id)" @click="undeleteUserWallet(props.row.user, props.row.id)"
> >
<q-tooltip>Undelete Wallet</q-tooltip> <q-tooltip>Undelete Wallet</q-tooltip>
@@ -81,6 +86,7 @@
icon="delete" icon="delete"
size="sm" size="sm"
color="negative" color="negative"
class="q-ml-xs"
@click="deleteUserWallet(props.row.user, props.row.id, props.row.deleted)" @click="deleteUserWallet(props.row.user, props.row.id, props.row.deleted)"
> >
<q-tooltip>Delete Wallet</q-tooltip> <q-tooltip>Delete Wallet</q-tooltip>
+8 -1
View File
@@ -61,6 +61,7 @@ include "users/_createWalletDialog.html" %}
icon="content_copy" icon="content_copy"
size="sm" size="sm"
color="primary" color="primary"
class="q-ml-xs"
@click="copyText(props.row.id)" @click="copyText(props.row.id)"
> >
<q-tooltip>Copy User ID</q-tooltip> <q-tooltip>Copy User ID</q-tooltip>
@@ -71,6 +72,7 @@ include "users/_createWalletDialog.html" %}
icon="build" icon="build"
size="sm" size="sm"
:color="props.row.is_admin ? 'primary' : 'grey'" :color="props.row.is_admin ? 'primary' : 'grey'"
class="q-ml-xs"
@click="toggleAdmin(props.row.id)" @click="toggleAdmin(props.row.id)"
> >
<q-tooltip>Toggle Admin</q-tooltip> <q-tooltip>Toggle Admin</q-tooltip>
@@ -81,6 +83,7 @@ include "users/_createWalletDialog.html" %}
icon="build" icon="build"
size="sm" size="sm"
color="positive" color="positive"
class="q-ml-xs"
> >
<q-tooltip>Super User</q-tooltip> <q-tooltip>Super User</q-tooltip>
</q-btn> </q-btn>
@@ -98,6 +101,7 @@ include "users/_createWalletDialog.html" %}
icon="delete" icon="delete"
size="sm" size="sm"
color="negative" color="negative"
class="q-ml-xs"
@click="deleteUser(props.row.id, props)" @click="deleteUser(props.row.id, props)"
> >
<q-tooltip>Delete User</q-tooltip> <q-tooltip>Delete User</q-tooltip>
@@ -111,7 +115,10 @@ include "users/_createWalletDialog.html" %}
<q-td auto-width v-text="props.row.transaction_count"></q-td> <q-td auto-width v-text="props.row.transaction_count"></q-td>
<q-td auto-width v-text="props.row.username"></q-td> <q-td auto-width v-text="props.row.username"></q-td>
<q-td auto-width v-text="props.row.email"></q-td> <q-td auto-width v-text="props.row.email"></q-td>
<q-td auto-width v-text="props.row.last_payment"></q-td> <q-td
auto-width
v-text="formatDate(props.row.last_payment)"
></q-td>
</q-tr> </q-tr>
</template> </template>
</q-table> </q-table>
+16 -21
View File
@@ -113,33 +113,28 @@ async def api_install_extension(data: CreateExtension):
) from exc ) from exc
@extension_router.get("/{ext_id}/details", dependencies=[Depends(check_user_exists)]) @extension_router.get("/{ext_id}/details")
async def api_extension_details( async def api_extension_details(
ext_id: str, ext_id: str,
details_link: str, details_link: str,
): ):
all_releases = await InstallableExtension.get_extension_releases(ext_id)
try: release = next((r for r in all_releases if r.details_link == details_link), None)
all_releases = await InstallableExtension.get_extension_releases(ext_id) if not release:
release = next(
(r for r in all_releases if r.details_link == details_link), None
)
assert release, "Details not found for release"
release_details = await ExtensionRelease.fetch_release_details(details_link)
assert release_details, "Cannot fetch details for release"
release_details["icon"] = release.icon
release_details["repo"] = release.repo
return release_details
except AssertionError as exc:
raise HTTPException(HTTPStatus.BAD_REQUEST, str(exc)) from exc
except Exception as exc:
logger.warning(exc)
raise HTTPException( raise HTTPException(
HTTPStatus.INTERNAL_SERVER_ERROR, status_code=HTTPStatus.NOT_FOUND, detail="Release not found"
f"Failed to get details for extension {ext_id}.", )
) from exc
release_details = await ExtensionRelease.fetch_release_details(details_link)
if not release_details:
raise HTTPException(
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
detail="Cannot fetch details for release",
)
release_details["icon"] = release.icon
release_details["repo"] = release.repo
return release_details
@extension_router.put("/{ext_id}/sell") @extension_router.put("/{ext_id}/sell")
+3 -5
View File
@@ -24,7 +24,6 @@ from ...utils.exchange_rates import allowed_currencies, currencies
from ..crud import ( from ..crud import (
create_account, create_account,
create_wallet, create_wallet,
get_account,
get_dbversions, get_dbversions,
get_installed_extensions, get_installed_extensions,
get_user, get_user,
@@ -231,10 +230,9 @@ async def service_worker(request: Request):
@generic_router.get("/manifest/{usr}.webmanifest") @generic_router.get("/manifest/{usr}.webmanifest")
async def manifest(request: Request, usr: str): async def manifest(request: Request, usr: str):
host = urlparse(str(request.url)).netloc host = urlparse(str(request.url)).netloc
account = await get_account(usr) user = await get_user(usr)
if not account: if not user:
raise HTTPException(status_code=HTTPStatus.NOT_FOUND) raise HTTPException(status_code=HTTPStatus.NOT_FOUND)
user = await get_user(account)
return { return {
"short_name": settings.lnbits_site_title, "short_name": settings.lnbits_site_title,
"name": settings.lnbits_site_title + " Wallet", "name": settings.lnbits_site_title + " Wallet",
@@ -325,7 +323,7 @@ async def node(request: Request, user: User = Depends(check_admin)):
"user": user.json(), "user": user.json(),
"settings": settings.dict(), "settings": settings.dict(),
"balance": balance, "balance": balance,
"wallets": user.wallets[0].dict(), "wallets": user.wallets[0].json(),
}, },
) )
+2 -2
View File
@@ -17,8 +17,8 @@ from lnbits.core.crud import (
update_admin_settings, update_admin_settings,
) )
from lnbits.core.models import ( from lnbits.core.models import (
Account,
AccountFilters, AccountFilters,
AccountOverview,
CreateTopup, CreateTopup,
User, User,
Wallet, Wallet,
@@ -40,7 +40,7 @@ users_router = APIRouter(prefix="/users/api/v1", dependencies=[Depends(check_adm
) )
async def api_get_users( async def api_get_users(
filters: Filters = Depends(parse_filters(AccountFilters)), filters: Filters = Depends(parse_filters(AccountFilters)),
) -> Page[Account]: ) -> Page[AccountOverview]:
return await get_accounts(filters=filters) return await get_accounts(filters=filters)
+6
View File
@@ -337,6 +337,12 @@ window.LNbits = {
.join('') .join('')
return hashHex return hashHex
}, },
formatDate: function (timestamp) {
return Quasar.date.formatDate(
new Date(timestamp * 1000),
'YYYY-MM-DD HH:mm'
)
},
formatCurrency: function (value, currency) { formatCurrency: function (value, currency) {
return new Intl.NumberFormat(window.LOCALE, { return new Intl.NumberFormat(window.LOCALE, {
style: 'currency', style: 'currency',
+3
View File
@@ -205,6 +205,9 @@ window.app = Vue.createApp({
}) })
}, },
methods: { methods: {
formatDate: function (value) {
return LNbits.utils.formatDate(value)
},
formatSat: function (value) { formatSat: function (value) {
return LNbits.utils.formatSat(Math.floor(value / 1000)) return LNbits.utils.formatSat(Math.floor(value / 1000))
}, },