feat: add dynamic errorpage (#3602)

This commit is contained in:
dni ⚡ 2025-12-02 13:33:25 +01:00 committed by GitHub
parent 89cabda123
commit eefaf3c50c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 143 additions and 117 deletions

View file

@ -221,6 +221,7 @@ async def index(
@generic_router.get("/")
@generic_router.get("/error")
@generic_router.get("/node/public")
@generic_router.get("/first_install", dependencies=[Depends(check_first_install)])
async def index_public(request: Request) -> HTMLResponse:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -454,3 +454,12 @@ video {
width: 90% !important;
}
}
.error-code {
font-size: clamp(15vh, 20vw, 30vh);
}
.error-message {
font-size: clamp(1.5rem, 3vw, 3.75rem);
font-weight: 300;
opacity: 0.4;
}

View file

@ -0,0 +1,53 @@
window.app.component('lnbits-error', {
template: '#lnbits-error',
mixins: [window.windowMixin],
props: ['dynamic', 'code', 'message'],
computed: {
isExtension() {
if (this.code != 403) return false
if (this.message.startsWith('Extension ')) return true
}
},
methods: {
goBack() {
window.history.back()
},
goHome() {
window.location = '/'
},
goToWallet() {
if (this.dynamic) {
this.$router.push('/wallet')
return
}
window.location = '/wallet'
},
goToExtension() {
const extension = this.message.match(/'([^']+)'/)[1]
const url = `/extensions#${extension}`
if (this.dynamic) {
this.$router.push(url)
return
}
window.location = url
},
async logOut() {
try {
await LNbits.api.logout()
window.location = '/'
} catch (e) {
LNbits.utils.notifyApiError(e)
}
}
},
async created() {
// check if we have error from error.html
if (!this.dynamic) {
if (this.code == 401) {
console.warn(`Unauthorized: ${this.errorMessage}`)
this.logOut()
return
}
}
}
})

View file

@ -9,6 +9,8 @@ const localStore = (key, defaultValue) => {
}
window.g = Vue.reactive({
errorCode: null,
errorMessage: null,
isUserAuthorized: !!Quasar.Cookies.get('is_lnbits_user_authorized'),
offline: !navigator.onLine,
hasCamera: false,

View file

@ -78,6 +78,11 @@ const routes = [
path: '/',
name: 'PageHome',
component: PageHome
},
{
path: '/error',
name: 'PageError',
component: PageError
}
]

View file

@ -0,0 +1,4 @@
window.PageError = {
template: '#page-error',
mixins: [window.windowMixin]
}

View file

@ -604,16 +604,17 @@ window.PageWallet = {
this.decodeRequest()
this.parse.show = true
}
let wallet = g.user.wallets.find(w => w.id === this.$route.params.id)
if (!wallet) {
const walletId = g.lastActiveWallet || g.user.wallets[0].id
wallet = g.user.wallets.find(w => w.id === walletId)
// TODO: should show PageError(404) if wallet not found
}
const wallet = g.user.wallets.find(w => w.id === this.$route.params.id)
if (wallet) {
this.g.wallet = wallet
this.g.lastActiveWallet = wallet.id
this.$q.localStorage.setItem('lnbits.lastActiveWallet', wallet.id)
this.$router.replace(`/wallet/${wallet.id}`)
} else {
this.g.errorCode = 404
this.g.errorMessage = 'Wallet not found.'
this.$router.push('/error')
}
},
watch: {
'g.updatePaymentsHash'() {

View file

@ -197,3 +197,12 @@ video {
}
}
}
.error-code {
font-size: clamp(15vh, 20vw, 30vh);
}
.error-message {
font-size: clamp(1.5rem, calc(1.5 / 10 * 20vw), 3.75rem);
font-weight: 300;
opacity: 0.4;
}

View file

@ -42,6 +42,7 @@
"js/bolt11-decoder.js"
],
"components": [
"js/pages/error.js",
"js/pages/home.js",
"js/pages/extensions_builder.js",
"js/pages/extensions.js",
@ -75,6 +76,7 @@
"js/components/lnbits-wallet-paylinks.js",
"js/components/lnbits-wallet-extra.js",
"js/components/lnbits-home-logos.js",
"js/components/lnbits-error.js",
"js/components/lnbits-qrcode.js",
"js/components/lnbits-qrcode-lnurl.js",
"js/components/lnbits-disclaimer.js",

View file

@ -28,7 +28,8 @@ include('components/lnbits-wallet-api-docs.vue') %} {%
include('components/lnbits-wallet-share.vue') %} {%
include('components/lnbits-wallet-charts.vue') %} {%
include('components/lnbits-wallet-paylinks.vue') %} {%
include('components/lnbits-wallet-extra.vue') %}
include('components/lnbits-wallet-extra.vue') %} {%
include('components/lnbits-error.vue') %}
<template id="lnbits-manage">
<q-list v-if="g.user" dense class="lnbits-drawer__q-list">

View file

@ -24,7 +24,6 @@
flat
:icon="g.walletFlip ? 'view_list' : 'view_column'"
color="grey"
class=""
@click="g.walletFlip = !g.walletFlip"
>
<q-tooltip

View file

@ -0,0 +1,25 @@
<template id="lnbits-error">
<div class="text-center q-pa-md flex flex-center">
<div>
<div class="error-code" v-text="code"></div>
<div class="error-message" v-text="message"></div>
<div class="q-mx-auto q-mt-lg justify-center" style="width: max-content">
<q-btn
v-if="isExtension"
color="primary"
@click="goToExtension()"
label="Go To Extension"
></q-btn>
<q-btn
v-else-if="g.isUserAuthorized"
color="primary"
@click="goToWallet()"
label="Go to Wallet"
></q-btn>
<q-btn v-else color="primary" @click="goBack()" label="Go Back"></q-btn>
<span class="q-mx-md">OR</span>
<q-btn color="secondary" @click="goHome()" label="Go Home"></q-btn>
</div>
</div>
</div>
</template>

View file

@ -1,102 +1,5 @@
{% extends "public.html" %} {% block page_container %}
<q-page-container>
<q-page
class="q-px-md q-py-lg content-center"
:class="{'q-px-lg': $q.screen.gt.xs}"
>
{% block page %}
<div class="text-center q-pa-md flex flex-center">
<div v-if="statusCode">
<div class="error-code" v-text="statusCode"></div>
<div class="error-message" v-text="message"></div>
<div
class="q-mx-auto q-mt-lg justify-center"
style="width: max-content"
>
<q-btn
v-if="isExtension"
color="primary"
@click="goToExtension"
label="Go To Extension"
class="q-mb-lg full-width"
></q-btn>
<br />
<q-btn color="primary" href="/" label="Go Home"></q-btn>
<span class="q-mx-md">OR</span>
<q-btn color="primary" @click="goBack" label="Go Back"></q-btn>
</div>
</div>
</div>
{% endblock %}
</q-page>
</q-page-container>
{% endblock %} {% block scripts %}
<style>
.error-code {
font-size: clamp(15vh, 20vw, 30vh);
}
.error-message {
font-size: clamp(1.5rem, calc(1.5 / 10 * 20vw), 3.75rem);
font-weight: 300;
opacity: 0.4;
}
</style>
<script>
window.app = Vue.createApp({
el: '#vue',
mixins: [window.windowMixin],
data() {
return {
err: null,
statusCode: null,
message: null
}
},
methods: {
goBack: function () {
window.history.back()
},
goHome: function () {
window.location.href = '/'
},
goToExtension() {
window.location.href = `/extensions#${this.extension}`
},
async logOut() {
try {
await LNbits.api.logout()
window.location = '/'
} catch (e) {
LNbits.utils.notifyApiError(e)
}
},
},
computed: {
isExtension() {
if (this.statusCode != 403) return false
if (this.message.startsWith('Extension ')) return true
}
},
async created() {
this.err = '{{ err }}'
const statusCode = '{{ status_code }}' || 404
this.message = String({{ message | tojson }}) || 'Page not found'
if (statusCode == 401) {
console.warn(`Unauthorized: ${this.message}`)
this.logOut()
return
}
this.statusCode = statusCode
if (this.isExtension) {
this.extension = this.message.match(/'([^']+)'/)[1]
}
}
})
</script>
{% extends "public.html" %} {% from "macros.jinja" import window_vars with
context %} {% block scripts %} {{ window_vars() }} {% endblock %} {% block
page_container %}
<lnbits-error code="{{ status_code }}" message="{{ message }}"></lnbits-error>
{% endblock %}

View file

@ -3,4 +3,5 @@ include('pages/audit.vue') %} {% include('pages/wallets.vue') %} {%
include('pages/users.vue') %} {% include('pages/admin.vue') %} {%
include('pages/account.vue') %} {% include('pages/extensions_builder.vue') %} {%
include('pages/extensions.vue') %} {% include('pages/first-install.vue') %} {%
include('pages/home.vue') %} {% include('pages/wallet.vue') %}
include('pages/home.vue') %} {% include('pages/wallet.vue') %} {%
include('pages/error.vue') %}

View file

@ -0,0 +1,9 @@
<template id="page-error">
<div id="page-error">
<lnbits-error
:dynamic="true"
:code="g.errorCode"
:message="g.errorMessage"
></lnbits-error>
</div>
</template>

View file

@ -1,5 +1,5 @@
<template id="page-wallet">
<div class="row q-col-gutter-md" style="margin-bottom: 6rem">
<div v-if="g.wallet" class="row q-col-gutter-md" style="margin-bottom: 6rem">
<div class="col-12 col-md-7 q-gutter-y-md wallet-wrapper">
<q-card class="wallet-card">
<q-card-section>

View file

@ -94,6 +94,7 @@
"js/bolt11-decoder.js"
],
"components": [
"js/pages/error.js",
"js/pages/home.js",
"js/pages/extensions_builder.js",
"js/pages/extensions.js",
@ -127,6 +128,7 @@
"js/components/lnbits-wallet-paylinks.js",
"js/components/lnbits-wallet-extra.js",
"js/components/lnbits-home-logos.js",
"js/components/lnbits-error.js",
"js/components/lnbits-qrcode.js",
"js/components/lnbits-qrcode-lnurl.js",
"js/components/lnbits-disclaimer.js",