feat: move /first_install to vue component (#3485)
This commit is contained in:
parent
8755984bd8
commit
f8c58aef0e
13 changed files with 205 additions and 166 deletions
|
|
@ -1,150 +0,0 @@
|
||||||
{% extends "public.html" %} {% block page_container %}
|
|
||||||
<q-page-container>
|
|
||||||
<q-page class="q-px-md q-py-lg" :class="{'q-px-lg': $q.screen.gt.xs}">
|
|
||||||
{% block page %}
|
|
||||||
<div class="row q-col-gutter-md justify-center main">
|
|
||||||
<div class="col-10 col-md-8 col-lg-6 q-gutter-y-md">
|
|
||||||
<q-card>
|
|
||||||
<q-card-section class="grid">
|
|
||||||
<div>
|
|
||||||
<h6 class="q-my-none text-center">
|
|
||||||
<strong v-text="$t('welcome_lnbits')"></strong>
|
|
||||||
<p><span v-text="$t('setup_su_account')"></span></p>
|
|
||||||
</h6>
|
|
||||||
<br />
|
|
||||||
<q-form class="q-gutter-md">
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
v-model="loginData.username"
|
|
||||||
:label="$t('username')"
|
|
||||||
></q-input>
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
v-model.trim="loginData.password"
|
|
||||||
:type="loginData.isPwd ? 'password' : 'text'"
|
|
||||||
autocomplete="off"
|
|
||||||
:label="$t('password')"
|
|
||||||
:rules="[(val) => !val || val.length >= 8 || $t('invalid_password')]"
|
|
||||||
><template v-slot:append>
|
|
||||||
<q-icon
|
|
||||||
:name="loginData.isPwd ? 'visibility_off' : 'visibility'"
|
|
||||||
class="cursor-pointer"
|
|
||||||
@click="loginData.isPwd = !loginData.isPwd"
|
|
||||||
/> </template
|
|
||||||
></q-input>
|
|
||||||
<q-input
|
|
||||||
filled
|
|
||||||
v-model.trim="loginData.passwordRepeat"
|
|
||||||
:type="loginData.isPwdRepeat ? 'password' : 'text'"
|
|
||||||
autocomplete="off"
|
|
||||||
:label="$t('password_repeat')"
|
|
||||||
:rules="[(val) => !val || val.length >= 8 || $t('invalid_password'), (val) => val === loginData.password || $t('invalid_password_repeat')]"
|
|
||||||
><template v-slot:append>
|
|
||||||
<q-icon
|
|
||||||
:name="loginData.isPwdRepeat ? 'visibility_off' : 'visibility'"
|
|
||||||
class="cursor-pointer"
|
|
||||||
@click="loginData.isPwdRepeat = !loginData.isPwdRepeat"
|
|
||||||
/> </template
|
|
||||||
></q-input>
|
|
||||||
<q-btn
|
|
||||||
@click="setPassword()"
|
|
||||||
unelevated
|
|
||||||
color="primary"
|
|
||||||
:label="$t('login')"
|
|
||||||
:disable="checkPasswordsMatch || !loginData.username || !loginData.password || !loginData.passwordRepeat"
|
|
||||||
></q-btn>
|
|
||||||
</q-form>
|
|
||||||
</div>
|
|
||||||
<div class="hero-wrapper">
|
|
||||||
<div class="hero q-mx-auto"></div>
|
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
</q-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %} {% block scripts %}
|
|
||||||
<style>
|
|
||||||
main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.grid {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.hero-wrapper {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.hero {
|
|
||||||
display: block;
|
|
||||||
height: 100%;
|
|
||||||
max-width: 250px;
|
|
||||||
background-image: url("{{ static_url_for('static', 'images/logos/lnbits.svg') }}");
|
|
||||||
background-position: center;
|
|
||||||
background-size: contain;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
grid-gap: 1rem;
|
|
||||||
}
|
|
||||||
.hero-wrapper {
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
window.app = Vue.createApp({
|
|
||||||
el: '#vue',
|
|
||||||
mixins: [window.windowMixin],
|
|
||||||
data: function () {
|
|
||||||
return {
|
|
||||||
loginData: {
|
|
||||||
isPwd: true,
|
|
||||||
isPwdRepeat: true,
|
|
||||||
username: '',
|
|
||||||
password: '',
|
|
||||||
passwordRepeat: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.hasAdminUI = '{{ LNBITS_ADMIN_UI | tojson}}'
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
checkPasswordsMatch() {
|
|
||||||
return this.loginData.password !== this.loginData.passwordRepeat
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async setPassword() {
|
|
||||||
try {
|
|
||||||
await LNbits.api.request(
|
|
||||||
'PUT',
|
|
||||||
'/api/v1/auth/first_install',
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
username: this.loginData.username,
|
|
||||||
password: this.loginData.password,
|
|
||||||
password_repeat: this.loginData.passwordRepeat
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
window.location.href = '/admin'
|
|
||||||
} catch (e) {
|
|
||||||
LNbits.utils.notifyApiError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
</q-page>
|
|
||||||
</q-page-container>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
@ -19,6 +19,7 @@ from lnbits.decorators import (
|
||||||
check_admin,
|
check_admin,
|
||||||
check_admin_ui,
|
check_admin_ui,
|
||||||
check_extension_builder,
|
check_extension_builder,
|
||||||
|
check_first_install,
|
||||||
check_user_exists,
|
check_user_exists,
|
||||||
)
|
)
|
||||||
from lnbits.helpers import check_callback_url, template_renderer
|
from lnbits.helpers import check_callback_url, template_renderer
|
||||||
|
|
@ -96,19 +97,6 @@ async def get_user_wallet(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@generic_router.get("/first_install", response_class=HTMLResponse)
|
|
||||||
async def first_install(request: Request):
|
|
||||||
if not settings.first_install:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=HTTPStatus.BAD_REQUEST,
|
|
||||||
detail="Super user account has already been configured.",
|
|
||||||
)
|
|
||||||
return template_renderer().TemplateResponse(
|
|
||||||
request,
|
|
||||||
"core/first_install.html",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@generic_router.get("/robots.txt", response_class=HTMLResponse)
|
@generic_router.get("/robots.txt", response_class=HTMLResponse)
|
||||||
async def robots():
|
async def robots():
|
||||||
data = """
|
data = """
|
||||||
|
|
@ -296,6 +284,7 @@ async def index(
|
||||||
|
|
||||||
|
|
||||||
@generic_router.get("/node/public")
|
@generic_router.get("/node/public")
|
||||||
|
@generic_router.get("/first_install", dependencies=[Depends(check_first_install)])
|
||||||
async def index_public(request: Request) -> HTMLResponse:
|
async def index_public(request: Request) -> HTMLResponse:
|
||||||
return template_renderer().TemplateResponse(request, "index_public.html")
|
return template_renderer().TemplateResponse(request, "index_public.html")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -376,3 +376,11 @@ async def check_extension_builder(
|
||||||
HTTPStatus.FORBIDDEN,
|
HTTPStatus.FORBIDDEN,
|
||||||
"Extension Builder is disabled for non admin users.",
|
"Extension Builder is disabled for non admin users.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_first_install():
|
||||||
|
if not settings.first_install:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.BAD_REQUEST,
|
||||||
|
detail="Super user account has already been configured.",
|
||||||
|
)
|
||||||
|
|
|
||||||
2
lnbits/static/bundle-components.min.js
vendored
2
lnbits/static/bundle-components.min.js
vendored
File diff suppressed because one or more lines are too long
2
lnbits/static/bundle.min.css
vendored
2
lnbits/static/bundle.min.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -341,3 +341,39 @@ video {
|
||||||
.q-dialog__inner--minimized {
|
.q-dialog__inner--minimized {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.first-install {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.first-install .grid {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.first-install .hero-wrapper {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.first-install .hero {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 250px;
|
||||||
|
background-image: url(/static/images/logos/lnbits.svg);
|
||||||
|
background-position: center;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.first-install .grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-gap: 1rem;
|
||||||
|
}
|
||||||
|
.first-install .hero-wrapper {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,11 @@ const routes = [
|
||||||
path: '/extensions',
|
path: '/extensions',
|
||||||
name: 'Extensions',
|
name: 'Extensions',
|
||||||
component: PageExtensions
|
component: PageExtensions
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/first_install',
|
||||||
|
name: 'FirstInstall',
|
||||||
|
component: PageFirstInstall
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
37
lnbits/static/js/pages/first-install.js
Normal file
37
lnbits/static/js/pages/first-install.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
window.PageFirstInstall = {
|
||||||
|
template: '#page-first-install',
|
||||||
|
mixins: [window.windowMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loginData: {
|
||||||
|
isPwd: true,
|
||||||
|
isPwdRepeat: true,
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
passwordRepeat: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
checkPasswordsMatch() {
|
||||||
|
return this.loginData.password !== this.loginData.passwordRepeat
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async setPassword() {
|
||||||
|
try {
|
||||||
|
await LNbits.api.request('PUT', '/api/v1/auth/first_install', null, {
|
||||||
|
username: this.loginData.username,
|
||||||
|
password: this.loginData.password,
|
||||||
|
password_repeat: this.loginData.passwordRepeat
|
||||||
|
})
|
||||||
|
window.location.href = '/admin'
|
||||||
|
} catch (e) {
|
||||||
|
LNbits.utils.notifyApiError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
document.title = 'First Install - LNbits'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -132,3 +132,40 @@ video {
|
||||||
.q-dialog__inner--minimized {
|
.q-dialog__inner--minimized {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.first-install {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
.grid {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.hero-wrapper {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.hero {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 250px;
|
||||||
|
background-image: url(/static/images/logos/lnbits.svg);
|
||||||
|
background-position: center;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-gap: 1rem;
|
||||||
|
}
|
||||||
|
.hero-wrapper {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@
|
||||||
"components": [
|
"components": [
|
||||||
"js/pages/extensions_builder.js",
|
"js/pages/extensions_builder.js",
|
||||||
"js/pages/extensions.js",
|
"js/pages/extensions.js",
|
||||||
|
"js/pages/first-install.js",
|
||||||
"js/pages/payments.js",
|
"js/pages/payments.js",
|
||||||
"js/pages/node.js",
|
"js/pages/node.js",
|
||||||
"js/pages/node-public.js",
|
"js/pages/node-public.js",
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,4 @@
|
||||||
include('pages/audit.vue') %} {% include('pages/wallets.vue') %} {%
|
include('pages/audit.vue') %} {% include('pages/wallets.vue') %} {%
|
||||||
include('pages/users.vue') %} {% include('pages/admin.vue') %} {%
|
include('pages/users.vue') %} {% include('pages/admin.vue') %} {%
|
||||||
include('pages/account.vue') %} {% include('pages/extensions_builder.vue') %} {%
|
include('pages/account.vue') %} {% include('pages/extensions_builder.vue') %} {%
|
||||||
include('pages/extensions.vue') %}
|
include('pages/extensions.vue') %} {% include('pages/first-install.vue') %}
|
||||||
|
|
|
||||||
75
lnbits/templates/pages/first-install.vue
Normal file
75
lnbits/templates/pages/first-install.vue
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
<template id="page-first-install">
|
||||||
|
<div class="row q-col-gutter-md justify-center main first-install">
|
||||||
|
<div class="col-10 col-md-8 col-lg-6 q-gutter-y-md">
|
||||||
|
<q-card>
|
||||||
|
<q-card-section class="grid">
|
||||||
|
<div>
|
||||||
|
<h6 class="q-my-none text-center">
|
||||||
|
<strong v-text="$t('welcome_lnbits')"></strong>
|
||||||
|
<p><span v-text="$t('setup_su_account')"></span></p>
|
||||||
|
</h6>
|
||||||
|
<br />
|
||||||
|
<q-form class="q-gutter-md">
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
v-model="loginData.username"
|
||||||
|
:label="$t('username')"
|
||||||
|
></q-input>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
v-model.trim="loginData.password"
|
||||||
|
:type="loginData.isPwd ? 'password' : 'text'"
|
||||||
|
autocomplete="off"
|
||||||
|
:label="$t('password')"
|
||||||
|
:rules="[
|
||||||
|
val => !val || val.length >= 8 || $t('invalid_password')
|
||||||
|
]"
|
||||||
|
><template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
:name="loginData.isPwd ? 'visibility_off' : 'visibility'"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="loginData.isPwd = !loginData.isPwd"
|
||||||
|
/> </template
|
||||||
|
></q-input>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
v-model.trim="loginData.passwordRepeat"
|
||||||
|
:type="loginData.isPwdRepeat ? 'password' : 'text'"
|
||||||
|
autocomplete="off"
|
||||||
|
:label="$t('password_repeat')"
|
||||||
|
:rules="[
|
||||||
|
val => !val || val.length >= 8 || $t('invalid_password'),
|
||||||
|
val =>
|
||||||
|
val === loginData.password || $t('invalid_password_repeat')
|
||||||
|
]"
|
||||||
|
><template v-slot:append>
|
||||||
|
<q-icon
|
||||||
|
:name="
|
||||||
|
loginData.isPwdRepeat ? 'visibility_off' : 'visibility'
|
||||||
|
"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="loginData.isPwdRepeat = !loginData.isPwdRepeat"
|
||||||
|
/> </template
|
||||||
|
></q-input>
|
||||||
|
<q-btn
|
||||||
|
@click="setPassword()"
|
||||||
|
unelevated
|
||||||
|
color="primary"
|
||||||
|
:label="$t('login')"
|
||||||
|
:disable="
|
||||||
|
checkPasswordsMatch ||
|
||||||
|
!loginData.username ||
|
||||||
|
!loginData.password ||
|
||||||
|
!loginData.passwordRepeat
|
||||||
|
"
|
||||||
|
></q-btn>
|
||||||
|
</q-form>
|
||||||
|
</div>
|
||||||
|
<div class="hero-wrapper">
|
||||||
|
<div class="hero q-mx-auto"></div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -97,6 +97,7 @@
|
||||||
"components": [
|
"components": [
|
||||||
"js/pages/extensions_builder.js",
|
"js/pages/extensions_builder.js",
|
||||||
"js/pages/extensions.js",
|
"js/pages/extensions.js",
|
||||||
|
"js/pages/first-install.js",
|
||||||
"js/pages/payments.js",
|
"js/pages/payments.js",
|
||||||
"js/pages/node.js",
|
"js/pages/node.js",
|
||||||
"js/pages/node-public.js",
|
"js/pages/node-public.js",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue