Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce57d08163 | ||
|
|
52304e0730 | ||
|
|
6664eebf5a | ||
|
|
2a2af81827 | ||
|
|
9b47f6323f | ||
|
|
a61807a257 |
@@ -119,6 +119,8 @@ LNBITS_SITE_TAGLINE="Open Source Lightning Payments Platform"
|
|||||||
LNBITS_SITE_DESCRIPTION="The world's most powerful suite of bitcoin tools. Run for yourself, for others, or as part of a stack."
|
LNBITS_SITE_DESCRIPTION="The world's most powerful suite of bitcoin tools. Run for yourself, for others, or as part of a stack."
|
||||||
# Choose from bitcoin, mint, flamingo, freedom, salvador, autumn, monochrome, classic, cyber
|
# Choose from bitcoin, mint, flamingo, freedom, salvador, autumn, monochrome, classic, cyber
|
||||||
LNBITS_THEME_OPTIONS="classic, bitcoin, flamingo, freedom, mint, autumn, monochrome, salvador, cyber"
|
LNBITS_THEME_OPTIONS="classic, bitcoin, flamingo, freedom, mint, autumn, monochrome, salvador, cyber"
|
||||||
|
# Toggle the background styling on burger menus / drawers
|
||||||
|
# LNBITS_DEFAULT_BURGER_MENU_BACKGROUND=true
|
||||||
# LNBITS_CUSTOM_LOGO="https://lnbits.com/assets/images/logo/logo.svg"
|
# LNBITS_CUSTOM_LOGO="https://lnbits.com/assets/images/logo/logo.svg"
|
||||||
|
|
||||||
######################################
|
######################################
|
||||||
|
|||||||
@@ -306,7 +306,7 @@ async def update_payment_checking_id(
|
|||||||
await (conn or db).execute(
|
await (conn or db).execute(
|
||||||
f"""
|
f"""
|
||||||
UPDATE apipayments
|
UPDATE apipayments
|
||||||
SET checking_id = :new_id, updated_at = {db.timestamp_placeholder('now')}
|
SET checking_id = :new_id, updated_at = {db.timestamp_placeholder("now")}
|
||||||
WHERE checking_id = :old_id
|
WHERE checking_id = :old_id
|
||||||
""", # noqa: S608
|
""", # noqa: S608
|
||||||
{
|
{
|
||||||
@@ -399,7 +399,6 @@ async def get_payment_count_stats(
|
|||||||
user_id: str | None = None,
|
user_id: str | None = None,
|
||||||
conn: Connection | None = None,
|
conn: Connection | None = None,
|
||||||
) -> list[PaymentCountStat]:
|
) -> list[PaymentCountStat]:
|
||||||
|
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = Filters()
|
filters = Filters()
|
||||||
extra_stmts = []
|
extra_stmts = []
|
||||||
@@ -432,7 +431,6 @@ async def get_daily_stats(
|
|||||||
user_id: str | None = None,
|
user_id: str | None = None,
|
||||||
conn: Connection | None = None,
|
conn: Connection | None = None,
|
||||||
) -> tuple[list[PaymentDailyStats], list[PaymentDailyStats]]:
|
) -> tuple[list[PaymentDailyStats], list[PaymentDailyStats]]:
|
||||||
|
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = Filters()
|
filters = Filters()
|
||||||
|
|
||||||
@@ -482,7 +480,6 @@ async def get_wallets_stats(
|
|||||||
user_id: str | None = None,
|
user_id: str | None = None,
|
||||||
conn: Connection | None = None,
|
conn: Connection | None = None,
|
||||||
) -> list[PaymentWalletStats]:
|
) -> list[PaymentWalletStats]:
|
||||||
|
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = Filters()
|
filters = Filters()
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from .payments import (
|
|||||||
PaymentState,
|
PaymentState,
|
||||||
PaymentWalletStats,
|
PaymentWalletStats,
|
||||||
SettleInvoice,
|
SettleInvoice,
|
||||||
|
UpdatePaymentExtra,
|
||||||
)
|
)
|
||||||
from .tinyurl import TinyURL
|
from .tinyurl import TinyURL
|
||||||
from .users import (
|
from .users import (
|
||||||
@@ -90,6 +91,7 @@ __all__ = [
|
|||||||
"SimpleStatus",
|
"SimpleStatus",
|
||||||
"TinyURL",
|
"TinyURL",
|
||||||
"UpdateBalance",
|
"UpdateBalance",
|
||||||
|
"UpdatePaymentExtra",
|
||||||
"UpdateSuperuserPassword",
|
"UpdateSuperuserPassword",
|
||||||
"UpdateUser",
|
"UpdateUser",
|
||||||
"UpdateUserPassword",
|
"UpdateUserPassword",
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ class PaymentExtra(BaseModel):
|
|||||||
lnurl_response: str | None = None
|
lnurl_response: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class UpdatePaymentExtra(BaseModel):
|
||||||
|
payment_hash: str
|
||||||
|
extra: dict = Field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
class PayInvoice(BaseModel):
|
class PayInvoice(BaseModel):
|
||||||
payment_request: str
|
payment_request: str
|
||||||
description: str | None = None
|
description: str | None = None
|
||||||
|
|||||||
@@ -133,10 +133,6 @@ async def create_fiat_invoice(
|
|||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Fiat provider '{fiat_provider_name}' is not enabled.",
|
f"Fiat provider '{fiat_provider_name}' is not enabled.",
|
||||||
)
|
)
|
||||||
if settings.fiat_providers_admin_only:
|
|
||||||
wallet = await get_wallet(wallet_id, conn=conn)
|
|
||||||
if not wallet or not settings.is_admin_user(wallet.user):
|
|
||||||
raise ValueError("Fiat providers are available to admins only.")
|
|
||||||
|
|
||||||
if invoice_data.unit == "sat":
|
if invoice_data.unit == "sat":
|
||||||
raise ValueError("Fiat provider cannot be used with satoshis.")
|
raise ValueError("Fiat provider cannot be used with satoshis.")
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ from lnbits.core.models import (
|
|||||||
PaymentWalletStats,
|
PaymentWalletStats,
|
||||||
SettleInvoice,
|
SettleInvoice,
|
||||||
SimpleStatus,
|
SimpleStatus,
|
||||||
|
UpdatePaymentExtra,
|
||||||
)
|
)
|
||||||
from lnbits.core.models.payments import UpdatePaymentLabels
|
from lnbits.core.models.payments import UpdatePaymentLabels
|
||||||
from lnbits.core.models.users import AccountId
|
from lnbits.core.models.users import AccountId
|
||||||
@@ -297,6 +298,38 @@ async def api_update_payment_labels(
|
|||||||
return SimpleStatus(success=True, message="Payment labels updated.")
|
return SimpleStatus(success=True, message="Payment labels updated.")
|
||||||
|
|
||||||
|
|
||||||
|
@payment_router.patch(
|
||||||
|
"/extra",
|
||||||
|
name="Update payment extra",
|
||||||
|
description="Append new extra metadata to a payment.",
|
||||||
|
response_model=Payment,
|
||||||
|
)
|
||||||
|
async def api_update_payment_extra(
|
||||||
|
data: UpdatePaymentExtra,
|
||||||
|
key_type: WalletTypeInfo = Depends(require_admin_key),
|
||||||
|
) -> Payment:
|
||||||
|
payment = await get_standalone_payment(
|
||||||
|
data.payment_hash, wallet_id=key_type.wallet.id
|
||||||
|
)
|
||||||
|
if payment is None:
|
||||||
|
raise HTTPException(HTTPStatus.NOT_FOUND, "Payment does not exist.")
|
||||||
|
if not payment.success:
|
||||||
|
raise HTTPException(
|
||||||
|
HTTPStatus.BAD_REQUEST, "Payment extra can only be updated after success."
|
||||||
|
)
|
||||||
|
|
||||||
|
duplicate_keys = sorted(set(payment.extra).intersection(data.extra))
|
||||||
|
if duplicate_keys:
|
||||||
|
raise HTTPException(
|
||||||
|
HTTPStatus.BAD_REQUEST,
|
||||||
|
f"Extra keys already exist: {', '.join(duplicate_keys)}.",
|
||||||
|
)
|
||||||
|
|
||||||
|
payment.extra.update(data.extra)
|
||||||
|
await update_payment(payment)
|
||||||
|
return payment
|
||||||
|
|
||||||
|
|
||||||
@payment_router.get("/fee-reserve")
|
@payment_router.get("/fee-reserve")
|
||||||
async def api_payments_fee_reserve(invoice: str = Query("invoice")) -> JSONResponse:
|
async def api_payments_fee_reserve(invoice: str = Query("invoice")) -> JSONResponse:
|
||||||
invoice_obj = bolt11.decode(invoice)
|
invoice_obj = bolt11.decode(invoice)
|
||||||
|
|||||||
+3
-11
@@ -300,6 +300,7 @@ class ThemesSettings(LNbitsSettings):
|
|||||||
lnbits_default_card_rounded: bool = Field(default=True)
|
lnbits_default_card_rounded: bool = Field(default=True)
|
||||||
lnbits_default_card_gradient: bool = Field(default=True)
|
lnbits_default_card_gradient: bool = Field(default=True)
|
||||||
lnbits_default_card_shadow: bool = Field(default=False)
|
lnbits_default_card_shadow: bool = Field(default=False)
|
||||||
|
lnbits_default_burger_menu_background: bool = Field(default=True)
|
||||||
|
|
||||||
|
|
||||||
class OpsSettings(LNbitsSettings):
|
class OpsSettings(LNbitsSettings):
|
||||||
@@ -770,8 +771,6 @@ class FiatProvidersSettings(
|
|||||||
SquareFiatProvider,
|
SquareFiatProvider,
|
||||||
RevolutFiatProvider,
|
RevolutFiatProvider,
|
||||||
):
|
):
|
||||||
fiat_providers_admin_only: bool = Field(default=True)
|
|
||||||
|
|
||||||
def is_fiat_provider_enabled(self, provider: str | None) -> bool:
|
def is_fiat_provider_enabled(self, provider: str | None) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks if a specific fiat provider is enabled.
|
Checks if a specific fiat provider is enabled.
|
||||||
@@ -792,15 +791,6 @@ class FiatProvidersSettings(
|
|||||||
"""
|
"""
|
||||||
Returns a list of fiat payment methods allowed for the user.
|
Returns a list of fiat payment methods allowed for the user.
|
||||||
"""
|
"""
|
||||||
if self.fiat_providers_admin_only:
|
|
||||||
if not self.is_admin_user(user_id):
|
|
||||||
return []
|
|
||||||
return [
|
|
||||||
provider
|
|
||||||
for provider in ["stripe", "paypal", "square", "revolut"]
|
|
||||||
if self.is_fiat_provider_enabled(provider)
|
|
||||||
]
|
|
||||||
|
|
||||||
allowed_providers = []
|
allowed_providers = []
|
||||||
if self.stripe_enabled and (
|
if self.stripe_enabled and (
|
||||||
not self.stripe_limits.allowed_users
|
not self.stripe_limits.allowed_users
|
||||||
@@ -1292,6 +1282,7 @@ class PublicSettings(BaseModel):
|
|||||||
default_card_rounded: bool = Field(alias="defaultCardRounded")
|
default_card_rounded: bool = Field(alias="defaultCardRounded")
|
||||||
default_card_gradient: bool = Field(alias="defaultCardGradient")
|
default_card_gradient: bool = Field(alias="defaultCardGradient")
|
||||||
default_card_shadow: bool = Field(alias="defaultCardShadow")
|
default_card_shadow: bool = Field(alias="defaultCardShadow")
|
||||||
|
default_burger_menu_background: bool = Field(alias="defaultBurgerMenuBackground")
|
||||||
denomination: str | None = Field()
|
denomination: str | None = Field()
|
||||||
extensions: list[str] = Field()
|
extensions: list[str] = Field()
|
||||||
allowed_currencies: list[str] = Field(alias="allowedCurrencies")
|
allowed_currencies: list[str] = Field(alias="allowedCurrencies")
|
||||||
@@ -1355,6 +1346,7 @@ class PublicSettings(BaseModel):
|
|||||||
defaultCardRounded=settings.lnbits_default_card_rounded,
|
defaultCardRounded=settings.lnbits_default_card_rounded,
|
||||||
defaultCardGradient=settings.lnbits_default_card_gradient,
|
defaultCardGradient=settings.lnbits_default_card_gradient,
|
||||||
defaultCardShadow=settings.lnbits_default_card_shadow,
|
defaultCardShadow=settings.lnbits_default_card_shadow,
|
||||||
|
defaultBurgerMenuBackground=settings.lnbits_default_burger_menu_background,
|
||||||
denomination=settings.lnbits_denomination,
|
denomination=settings.lnbits_denomination,
|
||||||
extensions=list(settings.lnbits_installed_extensions_ids),
|
extensions=list(settings.lnbits_installed_extensions_ids),
|
||||||
allowedCurrencies=settings.lnbits_allowed_currencies,
|
allowedCurrencies=settings.lnbits_allowed_currencies,
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -395,6 +395,13 @@ body.card-shadow.body--dark .q-card:not(.q-dialog .q-card, .lnbits__dialog-card,
|
|||||||
filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.45));
|
filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.45));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.no-burger-background .q-drawer {
|
||||||
|
background-color: transparent !important;
|
||||||
|
background-image: none !important;
|
||||||
|
backdrop-filter: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--size: 100px;
|
--size: 100px;
|
||||||
--gap: 25px;
|
--gap: 25px;
|
||||||
|
|||||||
@@ -490,6 +490,8 @@ window.localisation.en = {
|
|||||||
toggle_card_gradient: 'Toggle gradient on cards',
|
toggle_card_gradient: 'Toggle gradient on cards',
|
||||||
card_shadow: 'Card Shadow',
|
card_shadow: 'Card Shadow',
|
||||||
toggle_card_shadow: 'Toggle shadow on cards',
|
toggle_card_shadow: 'Toggle shadow on cards',
|
||||||
|
burger_menu_background: 'Burger Menu Background',
|
||||||
|
toggle_burger_menu_background: 'Toggle burger menu background',
|
||||||
language: 'Language',
|
language: 'Language',
|
||||||
assets: 'Assets',
|
assets: 'Assets',
|
||||||
max_asset_size_mb: 'Max Asset Size (MB)',
|
max_asset_size_mb: 'Max Asset Size (MB)',
|
||||||
|
|||||||
@@ -452,7 +452,9 @@ window.app.component('username-password', {
|
|||||||
confirmationMethod: 'code',
|
confirmationMethod: 'code',
|
||||||
confirmationEmail: '',
|
confirmationEmail: '',
|
||||||
confirmationCode: this.invitationCode || '',
|
confirmationCode: this.invitationCode || '',
|
||||||
showConfirmationCode: false
|
showConfirmationCode: false,
|
||||||
|
showPwd: false,
|
||||||
|
showPwdRepeat: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -12,21 +12,6 @@ window.app.component('lnbits-admin-fiat-providers', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
fiatProvidersAllUsers: {
|
|
||||||
get() {
|
|
||||||
return this.formData?.fiat_providers_admin_only === false
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
this.formData.fiat_providers_admin_only = !value
|
|
||||||
this.formData.touch = null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fiatProviderAccessLabel() {
|
|
||||||
return this.fiatProvidersAllUsers ? 'All users' : 'Admins only'
|
|
||||||
},
|
|
||||||
secretInputStyle() {
|
|
||||||
return this.hideInputToggle ? {'-webkit-text-security': 'disc'} : {}
|
|
||||||
},
|
|
||||||
stripeWebhookUrl() {
|
stripeWebhookUrl() {
|
||||||
return (
|
return (
|
||||||
this.formData?.stripe_payment_webhook_url ||
|
this.formData?.stripe_payment_webhook_url ||
|
||||||
|
|||||||
@@ -69,6 +69,14 @@ window.app.component('lnbits-theme', {
|
|||||||
document.body.classList.remove('card-shadow')
|
document.body.classList.remove('card-shadow')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'g.burgerMenuChoice'(val) {
|
||||||
|
this.$q.localStorage.set('lnbits.burgerMenu', val)
|
||||||
|
if (val === true) {
|
||||||
|
document.body.classList.remove('no-burger-background')
|
||||||
|
} else {
|
||||||
|
document.body.classList.add('no-burger-background')
|
||||||
|
}
|
||||||
|
},
|
||||||
'g.mobileSimple'(val) {
|
'g.mobileSimple'(val) {
|
||||||
this.$q.localStorage.set('lnbits.mobileSimple', val)
|
this.$q.localStorage.set('lnbits.mobileSimple', val)
|
||||||
if (val === true) {
|
if (val === true) {
|
||||||
@@ -150,6 +158,9 @@ window.app.component('lnbits-theme', {
|
|||||||
if (this.g.cardShadowChoice === true) {
|
if (this.g.cardShadowChoice === true) {
|
||||||
document.body.classList.add('card-shadow')
|
document.body.classList.add('card-shadow')
|
||||||
}
|
}
|
||||||
|
if (this.g.burgerMenuChoice !== true) {
|
||||||
|
document.body.classList.add('no-burger-background')
|
||||||
|
}
|
||||||
if (this.g.bgimageChoice !== '') {
|
if (this.g.bgimageChoice !== '') {
|
||||||
document.body.classList.add('bg-image')
|
document.body.classList.add('bg-image')
|
||||||
document.body.style.setProperty(
|
document.body.style.setProperty(
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ window.g = Vue.reactive({
|
|||||||
SETTINGS.defaultCardGradient
|
SETTINGS.defaultCardGradient
|
||||||
),
|
),
|
||||||
cardShadowChoice: localStore('lnbits.cardShadow', SETTINGS.defaultCardShadow),
|
cardShadowChoice: localStore('lnbits.cardShadow', SETTINGS.defaultCardShadow),
|
||||||
|
burgerMenuChoice: localStore(
|
||||||
|
'lnbits.burgerMenu',
|
||||||
|
SETTINGS.defaultBurgerMenuBackground
|
||||||
|
),
|
||||||
reactionChoice: localStore('lnbits.reactions', SETTINGS.defaultReaction),
|
reactionChoice: localStore('lnbits.reactions', SETTINGS.defaultReaction),
|
||||||
bgimageChoice: localStore(
|
bgimageChoice: localStore(
|
||||||
'lnbits.backgroundImage',
|
'lnbits.backgroundImage',
|
||||||
|
|||||||
@@ -732,7 +732,8 @@ window.PageAccount = {
|
|||||||
darkChoice: this.g.settings.defaultDark,
|
darkChoice: this.g.settings.defaultDark,
|
||||||
cardRoundedChoice: this.g.settings.defaultCardRounded,
|
cardRoundedChoice: this.g.settings.defaultCardRounded,
|
||||||
cardGradientChoice: this.g.settings.defaultCardGradient,
|
cardGradientChoice: this.g.settings.defaultCardGradient,
|
||||||
cardShadowChoice: this.g.settings.defaultCardShadow
|
cardShadowChoice: this.g.settings.defaultCardShadow,
|
||||||
|
burgerMenuChoice: this.g.settings.defaultBurgerMenuBackground
|
||||||
}
|
}
|
||||||
this.siteCustomisationChanged(defaults)
|
this.siteCustomisationChanged(defaults)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,3 +70,12 @@ body.card-shadow.body--dark {
|
|||||||
filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.45));
|
filter: drop-shadow(0 12px 28px rgba(0, 0, 0, 0.45));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.no-burger-background {
|
||||||
|
.q-drawer {
|
||||||
|
background-color: transparent !important;
|
||||||
|
background-image: none !important;
|
||||||
|
backdrop-filter: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -774,7 +774,13 @@ include('components/lnbits-error.vue') %}
|
|||||||
v-model="password"
|
v-model="password"
|
||||||
name="password"
|
name="password"
|
||||||
:label="$t('password') + ' *'"
|
:label="$t('password') + ' *'"
|
||||||
type="password"
|
:type="showPwd ? 'text' : 'password'"
|
||||||
|
><template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
:name="showPwd ? 'visibility' : 'visibility_off'"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="showPwd = !showPwd"
|
||||||
|
/> </template
|
||||||
></q-input>
|
></q-input>
|
||||||
<div class="row justify-end">
|
<div class="row justify-end">
|
||||||
<q-btn
|
<q-btn
|
||||||
@@ -803,16 +809,28 @@ include('components/lnbits-error.vue') %}
|
|||||||
filled
|
filled
|
||||||
v-model="password"
|
v-model="password"
|
||||||
:label="$t('password') + ' *'"
|
:label="$t('password') + ' *'"
|
||||||
type="password"
|
:type="showPwd ? 'text' : 'password'"
|
||||||
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
|
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
|
||||||
|
><template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
:name="showPwd ? 'visibility' : 'visibility_off'"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="showPwd = !showPwd"
|
||||||
|
/> </template
|
||||||
></q-input>
|
></q-input>
|
||||||
<q-input
|
<q-input
|
||||||
dense
|
dense
|
||||||
filled
|
filled
|
||||||
v-model="passwordRepeat"
|
v-model="passwordRepeat"
|
||||||
:label="$t('password_repeat') + ' *'"
|
:label="$t('password_repeat') + ' *'"
|
||||||
type="password"
|
:type="showPwdRepeat ? 'text' : 'password'"
|
||||||
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
|
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
|
||||||
|
><template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
:name="showPwdRepeat ? 'visibility' : 'visibility_off'"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="showPwdRepeat = !showPwdRepeat"
|
||||||
|
/> </template
|
||||||
></q-input>
|
></q-input>
|
||||||
<div
|
<div
|
||||||
v-if="confirmationMethodsCount > 1"
|
v-if="confirmationMethodsCount > 1"
|
||||||
@@ -925,16 +943,28 @@ include('components/lnbits-error.vue') %}
|
|||||||
filled
|
filled
|
||||||
v-model="password"
|
v-model="password"
|
||||||
:label="$t('password') + ' *'"
|
:label="$t('password') + ' *'"
|
||||||
type="password"
|
:type="showPwd ? 'text' : 'password'"
|
||||||
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
|
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
|
||||||
|
><template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
:name="showPwd ? 'visibility' : 'visibility_off'"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="showPwd = !showPwd"
|
||||||
|
/> </template
|
||||||
></q-input>
|
></q-input>
|
||||||
<q-input
|
<q-input
|
||||||
dense
|
dense
|
||||||
filled
|
filled
|
||||||
v-model="passwordRepeat"
|
v-model="passwordRepeat"
|
||||||
:label="$t('password_repeat') + ' *'"
|
:label="$t('password_repeat') + ' *'"
|
||||||
type="password"
|
:type="showPwdRepeat ? 'text' : 'password'"
|
||||||
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
|
:rules="[val => !val || val.length >= 8 || $t('invalid_password')]"
|
||||||
|
><template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
:name="showPwdRepeat ? 'visibility' : 'visibility_off'"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="showPwdRepeat = !showPwdRepeat"
|
||||||
|
/> </template
|
||||||
></q-input>
|
></q-input>
|
||||||
<div class="row justify-end">
|
<div class="row justify-end">
|
||||||
<q-btn
|
<q-btn
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template id="lnbits-admin-fiat-providers">
|
<template id="lnbits-admin-fiat-providers">
|
||||||
<h6 class="q-my-none q-mb-sm row items-center q-gutter-sm">
|
<h6 class="q-my-none q-mb-sm">
|
||||||
<span v-text="$t('fiat_providers')"></span>
|
<span v-text="$t('fiat_providers')"></span>
|
||||||
<q-btn
|
<q-btn
|
||||||
round
|
round
|
||||||
@@ -7,18 +7,6 @@
|
|||||||
@click="hideInputToggle = !hideInputToggle"
|
@click="hideInputToggle = !hideInputToggle"
|
||||||
:icon="hideInputToggle ? 'visibility_off' : 'visibility'"
|
:icon="hideInputToggle ? 'visibility_off' : 'visibility'"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
<q-toggle
|
|
||||||
dense
|
|
||||||
size="sm"
|
|
||||||
color="warning"
|
|
||||||
v-model="fiatProvidersAllUsers"
|
|
||||||
:label="fiatProviderAccessLabel"
|
|
||||||
>
|
|
||||||
<q-tooltip>
|
|
||||||
If enabled for all users, your users may pass a memo that suspends your
|
|
||||||
account with your fiat providers
|
|
||||||
</q-tooltip>
|
|
||||||
</q-toggle>
|
|
||||||
</h6>
|
</h6>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@@ -58,11 +46,7 @@
|
|||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
class="q-mt-md"
|
class="q-mt-md"
|
||||||
type="text"
|
:type="hideInputToggle ? 'password' : 'text'"
|
||||||
:input-style="secretInputStyle"
|
|
||||||
autocomplete="off"
|
|
||||||
autocapitalize="off"
|
|
||||||
spellcheck="false"
|
|
||||||
v-model="formData.stripe_api_secret_key"
|
v-model="formData.stripe_api_secret_key"
|
||||||
:label="$t('secret_key')"
|
:label="$t('secret_key')"
|
||||||
></q-input>
|
></q-input>
|
||||||
@@ -124,11 +108,7 @@
|
|||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
class="q-mt-md"
|
class="q-mt-md"
|
||||||
type="text"
|
:type="hideInputToggle ? 'password' : 'text'"
|
||||||
:input-style="secretInputStyle"
|
|
||||||
autocomplete="off"
|
|
||||||
autocapitalize="off"
|
|
||||||
spellcheck="false"
|
|
||||||
v-model="formData.stripe_webhook_signing_secret"
|
v-model="formData.stripe_webhook_signing_secret"
|
||||||
:label="$t('signing_secret')"
|
:label="$t('signing_secret')"
|
||||||
:hint="$t('signing_secret_hint')"
|
:hint="$t('signing_secret_hint')"
|
||||||
@@ -345,22 +325,14 @@
|
|||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
class="q-mt-md"
|
class="q-mt-md"
|
||||||
type="text"
|
:type="hideInputToggle ? 'password' : 'text'"
|
||||||
:input-style="secretInputStyle"
|
|
||||||
autocomplete="off"
|
|
||||||
autocapitalize="off"
|
|
||||||
spellcheck="false"
|
|
||||||
v-model="formData.paypal_client_id"
|
v-model="formData.paypal_client_id"
|
||||||
:label="$t('client_id')"
|
:label="$t('client_id')"
|
||||||
></q-input>
|
></q-input>
|
||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
class="q-mt-md"
|
class="q-mt-md"
|
||||||
type="text"
|
:type="hideInputToggle ? 'password' : 'text'"
|
||||||
:input-style="secretInputStyle"
|
|
||||||
autocomplete="off"
|
|
||||||
autocapitalize="off"
|
|
||||||
spellcheck="false"
|
|
||||||
v-model="formData.paypal_client_secret"
|
v-model="formData.paypal_client_secret"
|
||||||
:label="$t('secret_key')"
|
:label="$t('secret_key')"
|
||||||
></q-input>
|
></q-input>
|
||||||
@@ -422,11 +394,7 @@
|
|||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
class="q-mt-md"
|
class="q-mt-md"
|
||||||
type="text"
|
:type="hideInputToggle ? 'password' : 'text'"
|
||||||
:input-style="secretInputStyle"
|
|
||||||
autocomplete="off"
|
|
||||||
autocapitalize="off"
|
|
||||||
spellcheck="false"
|
|
||||||
v-model="formData.paypal_webhook_id"
|
v-model="formData.paypal_webhook_id"
|
||||||
:label="$t('webhook_id')"
|
:label="$t('webhook_id')"
|
||||||
:hint="$t('webhook_id_hint')"
|
:hint="$t('webhook_id_hint')"
|
||||||
@@ -643,11 +611,7 @@
|
|||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
class="q-mt-md"
|
class="q-mt-md"
|
||||||
type="text"
|
:type="hideInputToggle ? 'password' : 'text'"
|
||||||
:input-style="secretInputStyle"
|
|
||||||
autocomplete="off"
|
|
||||||
autocapitalize="off"
|
|
||||||
spellcheck="false"
|
|
||||||
v-model="formData.square_access_token"
|
v-model="formData.square_access_token"
|
||||||
:label="$t('access_token')"
|
:label="$t('access_token')"
|
||||||
></q-input>
|
></q-input>
|
||||||
@@ -724,11 +688,7 @@
|
|||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
class="q-mt-md"
|
class="q-mt-md"
|
||||||
type="text"
|
:type="hideInputToggle ? 'password' : 'text'"
|
||||||
:input-style="secretInputStyle"
|
|
||||||
autocomplete="off"
|
|
||||||
autocapitalize="off"
|
|
||||||
spellcheck="false"
|
|
||||||
v-model="formData.square_webhook_signature_key"
|
v-model="formData.square_webhook_signature_key"
|
||||||
:label="$t('signing_secret')"
|
:label="$t('signing_secret')"
|
||||||
:hint="$t('square_webhook_signature_key_hint')"
|
:hint="$t('square_webhook_signature_key_hint')"
|
||||||
@@ -940,11 +900,7 @@
|
|||||||
<q-input
|
<q-input
|
||||||
filled
|
filled
|
||||||
class="q-mt-md"
|
class="q-mt-md"
|
||||||
type="text"
|
:type="hideInputToggle ? 'password' : 'text'"
|
||||||
:input-style="secretInputStyle"
|
|
||||||
autocomplete="off"
|
|
||||||
autocapitalize="off"
|
|
||||||
spellcheck="false"
|
|
||||||
v-model="formData.revolut_api_secret_key"
|
v-model="formData.revolut_api_secret_key"
|
||||||
label="API secret key"
|
label="API secret key"
|
||||||
></q-input>
|
></q-input>
|
||||||
|
|||||||
@@ -320,6 +320,15 @@
|
|||||||
>
|
>
|
||||||
</q-toggle>
|
</q-toggle>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12 col-sm-6 col-lg-2">
|
||||||
|
<q-toggle
|
||||||
|
type="bool"
|
||||||
|
v-model="formData.lnbits_default_burger_menu_background"
|
||||||
|
color="primary"
|
||||||
|
:label="$t('burger_menu_background')"
|
||||||
|
>
|
||||||
|
</q-toggle>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
|
|||||||
@@ -601,6 +601,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row q-mb-md">
|
||||||
|
<div class="col-4">
|
||||||
|
<span v-text="$t('burger_menu_background')"></span>
|
||||||
|
</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<q-toggle
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
icon="menu_open"
|
||||||
|
v-model="g.burgerMenuChoice"
|
||||||
|
@update:model-value="
|
||||||
|
siteCustomisationChanged({burgerMenuChoice: $event})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<q-tooltip
|
||||||
|
><span
|
||||||
|
v-text="$t('toggle_burger_menu_background')"
|
||||||
|
></span
|
||||||
|
></q-tooltip>
|
||||||
|
</q-toggle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row q-mb-md">
|
<div class="row q-mb-md">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span v-text="$t('toggle_darkmode')"></span>
|
<span v-text="$t('toggle_darkmode')"></span>
|
||||||
|
|||||||
Generated
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 2.4.0 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohappyeyeballs"
|
name = "aiohappyeyeballs"
|
||||||
@@ -3394,7 +3394,7 @@ version = "5.1.2"
|
|||||||
description = "Call stack profiler for Python. Shows you why your code is slow!"
|
description = "Call stack profiler for Python. Shows you why your code is slow!"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
groups = ["dev"]
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pyinstrument-5.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f224fe80ba288a00980af298d3808219f9d246fd95b4f91729c9c33a0dc54fe6"},
|
{file = "pyinstrument-5.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f224fe80ba288a00980af298d3808219f9d246fd95b4f91729c9c33a0dc54fe6"},
|
||||||
{file = "pyinstrument-5.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7df09fc0d5b72daf48b73cdf07738761bff7f656c81aff686b3ccdd7d2abe236"},
|
{file = "pyinstrument-5.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7df09fc0d5b72daf48b73cdf07738761bff7f656c81aff686b3ccdd7d2abe236"},
|
||||||
@@ -5110,4 +5110,4 @@ migration = ["psycopg2-binary"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.1"
|
lock-version = "2.1"
|
||||||
python-versions = ">=3.10,<3.13"
|
python-versions = ">=3.10,<3.13"
|
||||||
content-hash = "4dd6ccfc459064fb5335531d4ceb9c0f228279b1c95d0911e895232a51b90006"
|
content-hash = "4050934800e6dfcc5242d1847d3db69eb6e91d634c09368a28255ad3c908b568"
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "lnbits"
|
name = "lnbits"
|
||||||
version = "1.5.4"
|
version = "1.5.5-rc1"
|
||||||
requires-python = ">=3.10,<3.13"
|
requires-python = ">=3.10,<3.13"
|
||||||
description = "LNbits, free and open-source Lightning wallet and accounts system."
|
description = "LNbits, free and open-source Lightning wallet and accounts system."
|
||||||
authors = [{ name = "Alan Bits", email = "alan@lnbits.com" }]
|
authors = [{ name = "Alan Bits", email = "alan@lnbits.com" }]
|
||||||
@@ -52,6 +52,7 @@ dependencies = [
|
|||||||
"python-dotenv~=1.2.1",
|
"python-dotenv~=1.2.1",
|
||||||
"greenlet~=3.3.0",
|
"greenlet~=3.3.0",
|
||||||
"urllib3>=2.7.0",
|
"urllib3>=2.7.0",
|
||||||
|
"pyinstrument>=5.1.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
@@ -84,7 +85,6 @@ dev = [
|
|||||||
"types-mock~=5.2.0.20250924",
|
"types-mock~=5.2.0.20250924",
|
||||||
"mock~=5.2.0",
|
"mock~=5.2.0",
|
||||||
"grpcio-tools~=1.76.0",
|
"grpcio-tools~=1.76.0",
|
||||||
"pyinstrument>=5.1.2",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.uv]
|
[tool.uv]
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import pytest
|
|||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from lnbits.core.crud.payments import create_payment, get_payments
|
from lnbits.core.crud.payments import create_payment, get_payment, get_payments
|
||||||
from lnbits.core.models import Account, CreateInvoice, PaymentFilters, PaymentState
|
from lnbits.core.models import Account, CreateInvoice, PaymentFilters, PaymentState
|
||||||
from lnbits.core.models.payments import CancelInvoice, CreatePayment, SettleInvoice
|
from lnbits.core.models.payments import CancelInvoice, CreatePayment, SettleInvoice
|
||||||
from lnbits.core.models.users import AccountId
|
from lnbits.core.models.users import AccountId
|
||||||
@@ -218,6 +218,164 @@ async def test_payment_api_fee_reserve_and_hold_invoice_actions(mocker):
|
|||||||
cancel_mock.assert_awaited_once()
|
cancel_mock.assert_awaited_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_payment_extra_update_appends_new_keys(
|
||||||
|
client,
|
||||||
|
to_wallet,
|
||||||
|
adminkey_headers_to,
|
||||||
|
):
|
||||||
|
payment_hash = uuid4().hex
|
||||||
|
checking_id = await _create_payment(
|
||||||
|
to_wallet.id,
|
||||||
|
amount_msat=1_000,
|
||||||
|
payment_hash=payment_hash,
|
||||||
|
tag="splitpayments",
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await client.patch(
|
||||||
|
"/api/v1/payments/extra",
|
||||||
|
headers=adminkey_headers_to,
|
||||||
|
json={
|
||||||
|
"payment_hash": payment_hash,
|
||||||
|
"extra": {"child": "daughter", "compliance_note": "reviewed"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
extra = response.json()["extra"]
|
||||||
|
assert extra["tag"] == "splitpayments"
|
||||||
|
assert extra["child"] == "daughter"
|
||||||
|
assert extra["compliance_note"] == "reviewed"
|
||||||
|
|
||||||
|
payment = await get_payment(checking_id)
|
||||||
|
assert payment.extra == extra
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_payment_extra_update_creates_extra_when_missing(
|
||||||
|
client,
|
||||||
|
to_wallet,
|
||||||
|
adminkey_headers_to,
|
||||||
|
):
|
||||||
|
payment_hash = uuid4().hex
|
||||||
|
checking_id = await _create_payment(
|
||||||
|
to_wallet.id,
|
||||||
|
amount_msat=1_000,
|
||||||
|
payment_hash=payment_hash,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await client.patch(
|
||||||
|
"/api/v1/payments/extra",
|
||||||
|
headers=adminkey_headers_to,
|
||||||
|
json={"payment_hash": payment_hash, "extra": {"note": "reviewed"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["extra"] == {"note": "reviewed"}
|
||||||
|
|
||||||
|
payment = await get_payment(checking_id)
|
||||||
|
assert payment.extra == {"note": "reviewed"}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_payment_extra_update_rejects_existing_keys(
|
||||||
|
client,
|
||||||
|
to_wallet,
|
||||||
|
adminkey_headers_to,
|
||||||
|
):
|
||||||
|
payment_hash = uuid4().hex
|
||||||
|
checking_id = await _create_payment(
|
||||||
|
to_wallet.id,
|
||||||
|
amount_msat=1_000,
|
||||||
|
payment_hash=payment_hash,
|
||||||
|
tag="original",
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await client.patch(
|
||||||
|
"/api/v1/payments/extra",
|
||||||
|
headers=adminkey_headers_to,
|
||||||
|
json={"payment_hash": payment_hash, "extra": {"tag": "overwritten"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert response.json()["detail"] == "Extra keys already exist: tag."
|
||||||
|
|
||||||
|
payment = await get_payment(checking_id)
|
||||||
|
assert payment.extra == {"tag": "original"}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_payment_extra_update_requires_admin_key(
|
||||||
|
client,
|
||||||
|
to_wallet,
|
||||||
|
inkey_headers_to,
|
||||||
|
):
|
||||||
|
payment_hash = uuid4().hex
|
||||||
|
await _create_payment(
|
||||||
|
to_wallet.id,
|
||||||
|
amount_msat=1_000,
|
||||||
|
payment_hash=payment_hash,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await client.patch(
|
||||||
|
"/api/v1/payments/extra",
|
||||||
|
headers=inkey_headers_to,
|
||||||
|
json={"payment_hash": payment_hash, "extra": {"note": "invoice key"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 403
|
||||||
|
assert response.json()["detail"] == "Invalid adminkey."
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_payment_extra_update_is_wallet_scoped(
|
||||||
|
client,
|
||||||
|
from_wallet,
|
||||||
|
adminkey_headers_to,
|
||||||
|
):
|
||||||
|
payment_hash = uuid4().hex
|
||||||
|
await _create_payment(
|
||||||
|
from_wallet.id,
|
||||||
|
amount_msat=1_000,
|
||||||
|
payment_hash=payment_hash,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await client.patch(
|
||||||
|
"/api/v1/payments/extra",
|
||||||
|
headers=adminkey_headers_to,
|
||||||
|
json={"payment_hash": payment_hash, "extra": {"note": "wrong wallet"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 404
|
||||||
|
assert response.json()["detail"] == "Payment does not exist."
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_payment_extra_update_requires_successful_payment(
|
||||||
|
client,
|
||||||
|
to_wallet,
|
||||||
|
adminkey_headers_to,
|
||||||
|
):
|
||||||
|
payment_hash = uuid4().hex
|
||||||
|
await _create_payment(
|
||||||
|
to_wallet.id,
|
||||||
|
amount_msat=1_000,
|
||||||
|
payment_hash=payment_hash,
|
||||||
|
status=PaymentState.PENDING,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await client.patch(
|
||||||
|
"/api/v1/payments/extra",
|
||||||
|
headers=adminkey_headers_to,
|
||||||
|
json={"payment_hash": payment_hash, "extra": {"note": "too early"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert (
|
||||||
|
response.json()["detail"] == "Payment extra can only be updated after success."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _create_payment(
|
async def _create_payment(
|
||||||
wallet_id: str,
|
wallet_id: str,
|
||||||
*,
|
*,
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ class MockHTTPClient:
|
|||||||
def fiat_provider_test_settings(settings: Settings):
|
def fiat_provider_test_settings(settings: Settings):
|
||||||
original_lnbits_running = settings.lnbits_running
|
original_lnbits_running = settings.lnbits_running
|
||||||
original_allowed_currencies = settings.lnbits_allowed_currencies
|
original_allowed_currencies = settings.lnbits_allowed_currencies
|
||||||
original_fiat_providers_admin_only = settings.fiat_providers_admin_only
|
|
||||||
original_paypal_enabled = settings.paypal_enabled
|
original_paypal_enabled = settings.paypal_enabled
|
||||||
original_square_enabled = settings.square_enabled
|
original_square_enabled = settings.square_enabled
|
||||||
original_square_api_endpoint = settings.square_api_endpoint
|
original_square_api_endpoint = settings.square_api_endpoint
|
||||||
@@ -96,14 +95,12 @@ def fiat_provider_test_settings(settings: Settings):
|
|||||||
original_revolut_webhook_signing_secret = settings.revolut_webhook_signing_secret
|
original_revolut_webhook_signing_secret = settings.revolut_webhook_signing_secret
|
||||||
original_revolut_limits = settings.revolut_limits.copy(deep=True)
|
original_revolut_limits = settings.revolut_limits.copy(deep=True)
|
||||||
settings.lnbits_allowed_currencies = []
|
settings.lnbits_allowed_currencies = []
|
||||||
settings.fiat_providers_admin_only = False
|
|
||||||
settings.paypal_enabled = False
|
settings.paypal_enabled = False
|
||||||
settings.square_enabled = False
|
settings.square_enabled = False
|
||||||
settings.revolut_enabled = False
|
settings.revolut_enabled = False
|
||||||
yield
|
yield
|
||||||
settings.lnbits_running = original_lnbits_running
|
settings.lnbits_running = original_lnbits_running
|
||||||
settings.lnbits_allowed_currencies = original_allowed_currencies
|
settings.lnbits_allowed_currencies = original_allowed_currencies
|
||||||
settings.fiat_providers_admin_only = original_fiat_providers_admin_only
|
|
||||||
settings.paypal_enabled = original_paypal_enabled
|
settings.paypal_enabled = original_paypal_enabled
|
||||||
settings.square_enabled = original_square_enabled
|
settings.square_enabled = original_square_enabled
|
||||||
settings.square_api_endpoint = original_square_api_endpoint
|
settings.square_api_endpoint = original_square_api_endpoint
|
||||||
@@ -220,39 +217,6 @@ async def test_create_wallet_fiat_invoice_allowed_users(
|
|||||||
assert user.fiat_providers == ["revolut"]
|
assert user.fiat_providers == ["revolut"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
|
||||||
async def test_fiat_providers_admin_only_default(to_user: User, settings: Settings):
|
|
||||||
original_admin_users = list(settings.lnbits_admin_users)
|
|
||||||
try:
|
|
||||||
settings.fiat_providers_admin_only = True
|
|
||||||
settings.stripe_enabled = True
|
|
||||||
|
|
||||||
user = await get_user(to_user.id)
|
|
||||||
assert user
|
|
||||||
assert user.fiat_providers == []
|
|
||||||
|
|
||||||
settings.lnbits_admin_users.append(to_user.id)
|
|
||||||
user = await get_user(to_user.id)
|
|
||||||
assert user
|
|
||||||
assert user.fiat_providers == ["stripe"]
|
|
||||||
finally:
|
|
||||||
settings.lnbits_admin_users = original_admin_users
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
|
||||||
async def test_create_wallet_fiat_invoice_admin_only_rejects_non_admin(
|
|
||||||
to_wallet: Wallet, settings: Settings
|
|
||||||
):
|
|
||||||
settings.fiat_providers_admin_only = True
|
|
||||||
settings.stripe_enabled = True
|
|
||||||
invoice_data = CreateInvoice(
|
|
||||||
unit="USD", amount=1.0, memo="Test", fiat_provider="stripe"
|
|
||||||
)
|
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="available to admins only"):
|
|
||||||
await payments.create_fiat_invoice(to_wallet.id, invoice_data)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
async def test_create_wallet_fiat_invoice_fiat_limits_fail(
|
async def test_create_wallet_fiat_invoice_fiat_limits_fail(
|
||||||
to_wallet: Wallet, settings: Settings, mocker: MockerFixture
|
to_wallet: Wallet, settings: Settings, mocker: MockerFixture
|
||||||
|
|||||||
@@ -37,12 +37,14 @@ def test_dict_to_settings_parses_known_values():
|
|||||||
{
|
{
|
||||||
"lnbits_site_title": "Test Title",
|
"lnbits_site_title": "Test Title",
|
||||||
"lnbits_service_fee": 5,
|
"lnbits_service_fee": 5,
|
||||||
|
"lnbits_default_burger_menu_background": False,
|
||||||
"ignored_field": "ignored",
|
"ignored_field": "ignored",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert parsed.lnbits_site_title == "Test Title"
|
assert parsed.lnbits_site_title == "Test Title"
|
||||||
assert parsed.lnbits_service_fee == 5
|
assert parsed.lnbits_service_fee == 5
|
||||||
|
assert parsed.lnbits_default_burger_menu_background is False
|
||||||
assert not hasattr(parsed, "ignored_field")
|
assert not hasattr(parsed, "ignored_field")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -232,6 +232,14 @@ def test_installed_extensions_settings_activate_and_deactivate_paths():
|
|||||||
assert installed.find_extension_redirect("/.well-known/lnurlp", []) is None
|
assert installed.find_extension_redirect("/.well-known/lnurlp", []) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_public_settings_include_burger_menu_background(settings: Settings):
|
||||||
|
settings.lnbits_default_burger_menu_background = False
|
||||||
|
|
||||||
|
public_settings = PublicSettings.from_settings(settings)
|
||||||
|
|
||||||
|
assert public_settings.default_burger_menu_background is False
|
||||||
|
|
||||||
|
|
||||||
def test_installed_extensions_settings_detects_conflicting_redirects():
|
def test_installed_extensions_settings_detects_conflicting_redirects():
|
||||||
installed = InstalledExtensionsSettings(
|
installed = InstalledExtensionsSettings(
|
||||||
lnbits_extensions_redirects=[
|
lnbits_extensions_redirects=[
|
||||||
|
|||||||
@@ -1275,7 +1275,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lnbits"
|
name = "lnbits"
|
||||||
version = "1.5.4"
|
version = "1.5.5rc1"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "aiosqlite" },
|
{ name = "aiosqlite" },
|
||||||
@@ -1302,6 +1302,7 @@ dependencies = [
|
|||||||
{ name = "protobuf" },
|
{ name = "protobuf" },
|
||||||
{ name = "pycryptodomex" },
|
{ name = "pycryptodomex" },
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
|
{ name = "pyinstrument" },
|
||||||
{ name = "pyjwt" },
|
{ name = "pyjwt" },
|
||||||
{ name = "pyln-client" },
|
{ name = "pyln-client" },
|
||||||
{ name = "pynostr" },
|
{ name = "pynostr" },
|
||||||
@@ -1347,7 +1348,6 @@ dev = [
|
|||||||
{ name = "openai" },
|
{ name = "openai" },
|
||||||
{ name = "openapi-spec-validator" },
|
{ name = "openapi-spec-validator" },
|
||||||
{ name = "pre-commit" },
|
{ name = "pre-commit" },
|
||||||
{ name = "pyinstrument" },
|
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
{ name = "pytest-cov" },
|
{ name = "pytest-cov" },
|
||||||
{ name = "pytest-httpserver" },
|
{ name = "pytest-httpserver" },
|
||||||
@@ -1388,6 +1388,7 @@ requires-dist = [
|
|||||||
{ name = "psycopg2-binary", marker = "extra == 'migration'", specifier = "~=2.9.11" },
|
{ name = "psycopg2-binary", marker = "extra == 'migration'", specifier = "~=2.9.11" },
|
||||||
{ name = "pycryptodomex", specifier = "~=3.23.0" },
|
{ name = "pycryptodomex", specifier = "~=3.23.0" },
|
||||||
{ name = "pydantic", specifier = "~=1.10.26" },
|
{ name = "pydantic", specifier = "~=1.10.26" },
|
||||||
|
{ name = "pyinstrument", specifier = ">=5.1.2" },
|
||||||
{ name = "pyjwt", specifier = "~=2.12.0" },
|
{ name = "pyjwt", specifier = "~=2.12.0" },
|
||||||
{ name = "pyln-client", specifier = "~=25.12.0" },
|
{ name = "pyln-client", specifier = "~=25.12.0" },
|
||||||
{ name = "pynostr", specifier = "~=0.7.0" },
|
{ name = "pynostr", specifier = "~=0.7.0" },
|
||||||
@@ -1423,7 +1424,6 @@ dev = [
|
|||||||
{ name = "openai", specifier = "~=2.14.0" },
|
{ name = "openai", specifier = "~=2.14.0" },
|
||||||
{ name = "openapi-spec-validator", specifier = "~=0.7.2" },
|
{ name = "openapi-spec-validator", specifier = "~=0.7.2" },
|
||||||
{ name = "pre-commit", specifier = "~=4.5.1" },
|
{ name = "pre-commit", specifier = "~=4.5.1" },
|
||||||
{ name = "pyinstrument", specifier = ">=5.1.2" },
|
|
||||||
{ name = "pytest", specifier = "~=9.0.2" },
|
{ name = "pytest", specifier = "~=9.0.2" },
|
||||||
{ name = "pytest-cov", specifier = "~=7.0.0" },
|
{ name = "pytest-cov", specifier = "~=7.0.0" },
|
||||||
{ name = "pytest-httpserver", specifier = "~=1.1.3" },
|
{ name = "pytest-httpserver", specifier = "~=1.1.3" },
|
||||||
|
|||||||
Reference in New Issue
Block a user