Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ test:
uv run pytest

test-js:
node --test tests/test_registration.js
node --test tests/test_*.js
install-pre-commit-hook:
@echo "Installing pre-commit hook to git"
@echo "Uninstall the hook with uv run pre-commit uninstall"
Expand Down
8 changes: 8 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
from datetime import date, datetime
from uuid import uuid4

Expand Down Expand Up @@ -55,6 +56,13 @@ class EventExtra(PublicEventExtra):
onchain_zeroconf: bool = False
onchain_fasttrack: bool = False

@field_validator("onchain_wallet_id")
@classmethod
def validate_onchain_wallet_id(cls, value):
if value and not re.fullmatch(r"(?:core:)?[a-zA-Z0-9_-]+", value):
raise ValueError("Invalid onchain wallet reference")
return value


class CreateEvent(BaseModel):
wallet: str
Expand Down
12 changes: 12 additions & 0 deletions services.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,25 @@ async def get_satspay_charge(api_key: str, charge_id: str) -> dict[str, Any]:
return resp.json()


async def fetch_core_onchain_wallets(api_key: str) -> list[dict[str, Any]]:
async with httpx.AsyncClient() as client:
resp = await client.get(
url=f"http://{settings.host}:{settings.port}/satspay/api/v1/onchain-wallets",
headers={"X-API-KEY": api_key},
)
resp.raise_for_status()
return resp.json()


async def create_satspay_charge(api_key: str, data: dict) -> dict[str, Any]:
async with httpx.AsyncClient() as client:
resp = await client.post(
url=f"http://{settings.host}:{settings.port}/satspay/api/v1/charge",
headers={"X-API-KEY": api_key},
json=data,
)
if resp.status_code == 400:
raise ValueError(resp.json().get("detail", "Cannot create SatsPay charge."))
resp.raise_for_status()
return resp.json()

Expand Down
13 changes: 13 additions & 0 deletions static/js/i18n/de.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,23 @@ window.i18n.global.mergeLocaleMessage('de', {
onchain_payments: 'Onchain-Zahlungen',
onchain_desc:
'Bitcoin-Onchain-Zahlungen akzeptieren. Erfordert die installierten und aktivierten Erweiterungen SatsPay und Watchonly.',
onchain_sources_desc:
'Bitcoin-Onchain-Zahlungen über SatsPay mit einer LNbits-Onchain-Wallet oder der Watchonly-Erweiterung akzeptieren.',
accept_onchain: 'Onchain-Zahlungen akzeptieren',
watchonly_wallet: 'Watchonly-Wallet',
watchonly_wallet_hint:
'Bitcoin-Watchonly-Wallet für den Empfang von Onchain-Zahlungen',
onchain_wallet: 'Onchain-Wallet',
onchain_wallet_hint:
'LNbits- oder Watchonly-Wallet für den Empfang von Onchain-Zahlungen',
onchain_wallet_not_ready: 'Nicht bereit',
create_onchain_wallet: 'Onchain-Wallet erstellen',
onchain_wallet_setup_required:
'Richte diese Onchain-Wallet vor dem Empfang ein.',
onchain_wallet_backup_required:
'Sichere diese Onchain-Wallet vor dem Empfang.',
setup_onchain_wallet: 'Wallet einrichten',
backup_onchain_wallet: 'Wallet sichern',
onchain_zeroconf: 'Zero-Conf (unbestätigte Transaktionen akzeptieren)',
onchain_fasttrack: 'Fasttrack (ausstehende als bezahlt behandeln)',
update_event: 'Veranstaltung aktualisieren',
Expand Down
13 changes: 13 additions & 0 deletions static/js/i18n/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,23 @@ window.i18n.global.mergeLocaleMessage('en', {
onchain_payments: 'Onchain payments',
onchain_desc:
'Accept Bitcoin onchain payments. Requires the SatsPay and Watchonly extensions to be installed and enabled.',
onchain_sources_desc:
'Accept Bitcoin onchain payments through SatsPay using an LNbits onchain wallet or the Watchonly extension.',
accept_onchain: 'Accept onchain payments',
watchonly_wallet: 'Watchonly wallet',
watchonly_wallet_hint:
'Bitcoin watchonly wallet for receiving onchain payments',
onchain_wallet: 'Onchain wallet',
onchain_wallet_hint:
'LNbits or Watchonly wallet for receiving onchain payments',
onchain_wallet_not_ready: 'Not ready',
create_onchain_wallet: 'Create onchain wallet',
onchain_wallet_setup_required:
'Set up this onchain wallet before receiving.',
onchain_wallet_backup_required:
'Back up this onchain wallet before receiving.',
setup_onchain_wallet: 'Set up wallet',
backup_onchain_wallet: 'Back up wallet',
onchain_zeroconf: 'Zero-conf (accept unconfirmed transactions)',
onchain_fasttrack: 'Fasttrack (treat pending as paid)',
update_event: 'Update Event',
Expand Down
13 changes: 13 additions & 0 deletions static/js/i18n/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,23 @@ window.i18n.global.mergeLocaleMessage('es', {
onchain_payments: 'Pagos onchain',
onchain_desc:
'Aceptar pagos Bitcoin onchain. Requiere las extensiones SatsPay y Watchonly instaladas y habilitadas.',
onchain_sources_desc:
'Aceptar pagos Bitcoin onchain mediante SatsPay con una cartera onchain de LNbits o la extensión Watchonly.',
accept_onchain: 'Aceptar pagos onchain',
watchonly_wallet: 'Cartera watchonly',
watchonly_wallet_hint:
'Cartera Bitcoin watchonly para recibir pagos onchain',
onchain_wallet: 'Cartera onchain',
onchain_wallet_hint:
'Cartera de LNbits o Watchonly para recibir pagos onchain',
onchain_wallet_not_ready: 'No está lista',
create_onchain_wallet: 'Crear cartera onchain',
onchain_wallet_setup_required:
'Configura esta cartera onchain antes de recibir pagos.',
onchain_wallet_backup_required:
'Haz una copia de seguridad de esta cartera onchain antes de recibir pagos.',
setup_onchain_wallet: 'Configurar cartera',
backup_onchain_wallet: 'Respaldar cartera',
onchain_zeroconf: 'Zero-conf (aceptar transacciones no confirmadas)',
onchain_fasttrack: 'Fasttrack (tratar pendientes como pagados)',
update_event: 'Actualizar evento',
Expand Down
52 changes: 49 additions & 3 deletions static/js/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
window.PageEvents = {
template: '#page-events',
computed: {
lightningWalletOptions() {
const walletIds = this.g.user.wallets
.filter(wallet => wallet.canReceivePayments)
.map(wallet => wallet.id)
return this.g.user.walletOptions.filter(wallet =>
walletIds.includes(wallet.value)
)
},
onchainWalletOptions() {
const core = this.coreOnchainWallets.map(wallet => ({
label:
`LNbits: ${wallet.name}` +
(wallet.status === 'ready'
? ''
: ` (${this.$t('events.onchain_wallet_not_ready')})`),
value: `core:${wallet.id}`
}))
const watchonly = this.onchainWallets.map(wallet => ({
label: `Watchonly: ${wallet.title || wallet.id}`,
value: wallet.id
}))
return [...core, ...watchonly]
},
selectedCoreWallet() {
const reference = this.formDialog.data.extra?.onchain_wallet_id
if (!reference?.startsWith('core:')) return null
return (
this.coreOnchainWallets.find(w => w.id === reference.slice(5)) || null
)
},
hasCoreOnchainWallet() {
return this.g.user.wallets.some(wallet => wallet.walletType === 'onchain')
},
eventsColumns() {
return [
{
Expand Down Expand Up @@ -150,7 +183,9 @@ window.PageEvents = {
}
}
},
onchainWallets: []
onchainWallets: [],
coreOnchainWallets: [],
onchainMessages: []
}
},
methods: {
Expand All @@ -175,6 +210,9 @@ window.PageEvents = {
return value.length > 4 ? `${value.slice(0, 4)}...` : value
},
async loadOnchainWallets() {
this.onchainWallets = []
this.coreOnchainWallets = []
this.onchainMessages = []
const wallet = _.findWhere(this.g.user.wallets, {
id: this.formDialog.data.wallet
})
Expand All @@ -186,10 +224,18 @@ window.PageEvents = {
wallet.adminkey
)
this.onchainWallets = data.available ? data.wallets || [] : []
} catch {
this.onchainWallets = []
this.coreOnchainWallets = data.core_wallets || []
this.onchainMessages = [data.core_message, data.message].filter(Boolean)
} catch (error) {
LNbits.utils.notifyApiError(error)
}
},
createOnchainWallet() {
this.g.newWalletType = 'onchain'
},
openOnchainWallet(walletId) {
this.$router.push({path: '/wallet', query: {wal: walletId}})
},
async confirmOnchainTicket(ticket) {
const wallet = _.findWhere(this.g.user.wallets, {id: ticket.wallet})
if (!wallet) return
Expand Down
62 changes: 52 additions & 10 deletions static/js/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -374,8 +374,13 @@
dense
emit-value
v-model="formDialog.data.wallet"
:options="g.user.walletOptions"
:options="lightningWalletOptions"
:label="$t('events.wallet_label')"
@update:model-value="
() =>
formDialog.data.extra.onchain_enabled &&
loadOnchainWallets()
"
>
</q-select>
</div>
Expand Down Expand Up @@ -638,7 +643,7 @@
<div class="q-mt-lg">
<div
class="text-caption q-mb-md"
v-text="$t('events.onchain_desc')"
v-text="$t('events.onchain_sources_desc')"
></div>
<q-toggle
v-model="formDialog.data.extra.onchain_enabled"
Expand All @@ -652,18 +657,55 @@
dense
class="q-mt-md"
v-model="formDialog.data.extra.onchain_wallet_id"
:label="$t('events.watchonly_wallet')"
:options="
onchainWallets.map(w => ({
label: w.title || w.id,
value: w.id
}))
"
:label="$t('events.onchain_wallet')"
:options="onchainWalletOptions"
emit-value
map-options
:hint="$t('events.watchonly_wallet_hint')"
:hint="$t('events.onchain_wallet_hint')"
></q-select>
<div v-if="formDialog.data.extra.onchain_enabled" class="q-mt-md">
<q-btn
v-if="!hasCoreOnchainWallet"
flat
dense
color="primary"
:label="$t('events.create_onchain_wallet')"
@click="createOnchainWallet"
></q-btn>
<q-banner
v-for="message in onchainMessages"
:key="message"
dense
class="bg-orange-2 text-orange-9 q-mb-sm"
>
<span v-text="message"></span>
</q-banner>
<q-banner
v-if="
selectedCoreWallet && selectedCoreWallet.status !== 'ready'
"
dense
class="bg-orange-2 text-orange-9 q-mb-sm"
>
<div
v-text="
selectedCoreWallet.status === 'setup'
? $t('events.onchain_wallet_setup_required')
: $t('events.onchain_wallet_backup_required')
"
></div>
<q-btn
flat
dense
color="primary"
:label="
selectedCoreWallet.status === 'setup'
? $t('events.setup_onchain_wallet')
: $t('events.backup_onchain_wallet')
"
@click="openOnchainWallet(selectedCoreWallet.id)"
></q-btn>
</q-banner>
<q-toggle
v-model="formDialog.data.extra.onchain_zeroconf"
:label="$t('events.onchain_zeroconf')"
Expand Down
Loading
Loading