mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2024-11-20 02:28:10 +01:00
Added old admin extension
This commit is contained in:
parent
5a12f4f237
commit
166530eb0c
12
.env.example
12
.env.example
@ -3,10 +3,12 @@ PORT=5000
|
||||
|
||||
DEBUG=false
|
||||
|
||||
LNBITS_ALLOWED_USERS=""
|
||||
LNBITS_ADMIN_USERS=""
|
||||
# Extensions only admin can access
|
||||
LNBITS_ADMIN_EXTENSIONS="ngrok"
|
||||
LNBITS_ADMIN_USERS="" # User IDs seperated by comma
|
||||
LNBITS_ADMIN_EXTENSIONS="ngrok" # Extensions only admin can access
|
||||
LNBITS_ADMIN_UI=false # Extensions only admin can access
|
||||
|
||||
LNBITS_ALLOWED_USERS="" # Restricts access, User IDs seperated by comma
|
||||
|
||||
LNBITS_DEFAULT_WALLET_NAME="LNbits wallet"
|
||||
|
||||
# csv ad image filepaths or urls, extensions can choose to honor
|
||||
@ -91,4 +93,4 @@ LNBITS_DENOMINATION=sats
|
||||
|
||||
# EclairWallet
|
||||
ECLAIR_URL=http://127.0.0.1:8283
|
||||
ECLAIR_PASS=eclairpw
|
||||
ECLAIR_PASS=eclairpw
|
||||
|
11
lnbits/extensions/admin/README.md
Normal file
11
lnbits/extensions/admin/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
<h1>Example Extension</h1>
|
||||
<h2>*tagline*</h2>
|
||||
This is an example extension to help you organise and build you own.
|
||||
|
||||
Try to include an image
|
||||
<img src="https://i.imgur.com/9i4xcQB.png">
|
||||
|
||||
|
||||
<h2>If your extension has API endpoints, include useful ones here</h2>
|
||||
|
||||
<code>curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "X-Api-Key: YOUR_WALLET-ADMIN/INVOICE-KEY"</code>
|
10
lnbits/extensions/admin/__init__.py
Normal file
10
lnbits/extensions/admin/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
from quart import Blueprint
|
||||
from lnbits.db import Database
|
||||
|
||||
db = Database("ext_admin")
|
||||
|
||||
admin_ext: Blueprint = Blueprint("admin", __name__, static_folder="static", template_folder="templates")
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
from .views import * # noqa
|
6
lnbits/extensions/admin/config.json
Normal file
6
lnbits/extensions/admin/config.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Admin",
|
||||
"short_description": "Manage your LNbits install",
|
||||
"icon": "build",
|
||||
"contributors": ["benarc"]
|
||||
}
|
59
lnbits/extensions/admin/crud.py
Normal file
59
lnbits/extensions/admin/crud.py
Normal file
@ -0,0 +1,59 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from . import db
|
||||
from .models import Admin, Funding
|
||||
from lnbits.settings import *
|
||||
from lnbits.helpers import urlsafe_short_hash
|
||||
from lnbits.core.crud import create_payment
|
||||
from lnbits.db import Connection
|
||||
|
||||
|
||||
def update_wallet_balance(wallet_id: str, amount: int) -> str:
|
||||
temp_id = f"temp_{urlsafe_short_hash()}"
|
||||
internal_id = f"internal_{urlsafe_short_hash()}"
|
||||
create_payment(
|
||||
wallet_id=wallet_id,
|
||||
checking_id=internal_id,
|
||||
payment_request="admin_internal",
|
||||
payment_hash="admin_internal",
|
||||
amount=amount * 1000,
|
||||
memo="Admin top up",
|
||||
pending=False,
|
||||
)
|
||||
return "success"
|
||||
|
||||
|
||||
async def update_admin(
|
||||
) -> Optional[Admin]:
|
||||
if not CLightningWallet:
|
||||
print("poo")
|
||||
await db.execute(
|
||||
"""
|
||||
UPDATE admin
|
||||
SET user = ?, site_title = ?, site_tagline = ?, site_description = ?, allowed_users = ?, default_wallet_name = ?, data_folder = ?, disabled_ext = ?, force_https = ?, service_fee = ?, funding_source = ?
|
||||
WHERE 1
|
||||
""",
|
||||
(
|
||||
|
||||
),
|
||||
)
|
||||
row = await db.fetchone("SELECT * FROM admin WHERE 1")
|
||||
return Admin.from_row(row) if row else None
|
||||
|
||||
async def update_admin(admin_id: str, **kwargs) -> Optional[Admin]:
|
||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||
await db.execute(
|
||||
f"UPDATE jukebox.jukebox SET {q} WHERE id = ?", (*kwargs.values(), juke_id)
|
||||
)
|
||||
row = await db.fetchone("SELECT * FROM jukebox.jukebox WHERE id = ?", (juke_id,))
|
||||
return Jukebox(**row) if row else None
|
||||
|
||||
async def get_admin() -> List[Admin]:
|
||||
row = await db.fetchone("SELECT * FROM admin WHERE 1")
|
||||
return Admin.from_row(row) if row else None
|
||||
|
||||
|
||||
async def get_funding() -> List[Funding]:
|
||||
rows = await db.fetchall("SELECT * FROM funding")
|
||||
|
||||
return [Funding.from_row(row) for row in rows]
|
256
lnbits/extensions/admin/migrations.py
Normal file
256
lnbits/extensions/admin/migrations.py
Normal file
@ -0,0 +1,256 @@
|
||||
from sqlalchemy.exc import OperationalError # type: ignore
|
||||
from os import getenv
|
||||
from lnbits.helpers import urlsafe_short_hash
|
||||
|
||||
|
||||
async def m001_create_admin_table(db):
|
||||
user = None
|
||||
site_title = None
|
||||
site_tagline = None
|
||||
site_description = None
|
||||
allowed_users = None
|
||||
admin_user = None
|
||||
default_wallet_name = None
|
||||
data_folder = None
|
||||
disabled_ext = None
|
||||
force_https = True
|
||||
service_fee = 0
|
||||
funding_source = ""
|
||||
|
||||
if getenv("LNBITS_SITE_TITLE"):
|
||||
site_title = getenv("LNBITS_SITE_TITLE")
|
||||
|
||||
if getenv("LNBITS_SITE_TAGLINE"):
|
||||
site_tagline = getenv("LNBITS_SITE_TAGLINE")
|
||||
|
||||
if getenv("LNBITS_SITE_DESCRIPTION"):
|
||||
site_description = getenv("LNBITS_SITE_DESCRIPTION")
|
||||
|
||||
if getenv("LNBITS_ALLOWED_USERS"):
|
||||
allowed_users = getenv("LNBITS_ALLOWED_USERS")
|
||||
|
||||
if getenv("LNBITS_ADMIN_USER"):
|
||||
admin_user = getenv("LNBITS_ADMIN_USER")
|
||||
|
||||
if getenv("LNBITS_DEFAULT_WALLET_NAME"):
|
||||
default_wallet_name = getenv("LNBITS_DEFAULT_WALLET_NAME")
|
||||
|
||||
if getenv("LNBITS_DATA_FOLDER"):
|
||||
data_folder = getenv("LNBITS_DATA_FOLDER")
|
||||
|
||||
if getenv("LNBITS_DISABLED_EXTENSIONS"):
|
||||
disabled_ext = getenv("LNBITS_DISABLED_EXTENSIONS")
|
||||
|
||||
if getenv("LNBITS_FORCE_HTTPS"):
|
||||
force_https = getenv("LNBITS_FORCE_HTTPS")
|
||||
|
||||
if getenv("LNBITS_SERVICE_FEE"):
|
||||
service_fee = getenv("LNBITS_SERVICE_FEE")
|
||||
|
||||
if getenv("LNBITS_BACKEND_WALLET_CLASS"):
|
||||
funding_source = getenv("LNBITS_BACKEND_WALLET_CLASS")
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS admin (
|
||||
user TEXT,
|
||||
site_title TEXT,
|
||||
site_tagline TEXT,
|
||||
site_description TEXT,
|
||||
admin_user TEXT,
|
||||
allowed_users TEXT,
|
||||
default_wallet_name TEXT,
|
||||
data_folder TEXT,
|
||||
disabled_ext TEXT,
|
||||
force_https BOOLEAN,
|
||||
service_fee INT,
|
||||
funding_source TEXT
|
||||
);
|
||||
"""
|
||||
)
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO admin (user, site_title, site_tagline, site_description, admin_user, allowed_users, default_wallet_name, data_folder, disabled_ext, force_https, service_fee, funding_source)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
user,
|
||||
site_title,
|
||||
site_tagline,
|
||||
site_description,
|
||||
admin_user,
|
||||
allowed_users,
|
||||
default_wallet_name,
|
||||
data_folder,
|
||||
disabled_ext,
|
||||
force_https,
|
||||
service_fee,
|
||||
funding_source,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def m001_create_funding_table(db):
|
||||
|
||||
funding_wallet = getenv("LNBITS_BACKEND_WALLET_CLASS")
|
||||
|
||||
# Make the funding table, if it does not already exist
|
||||
await db.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS funding (
|
||||
id TEXT PRIMARY KEY,
|
||||
backend_wallet TEXT,
|
||||
endpoint TEXT,
|
||||
port INT,
|
||||
read_key TEXT,
|
||||
invoice_key TEXT,
|
||||
admin_key TEXT,
|
||||
cert TEXT,
|
||||
balance INT,
|
||||
selected INT
|
||||
);
|
||||
"""
|
||||
)
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO funding (id, backend_wallet, endpoint, selected)
|
||||
VALUES (?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
urlsafe_short_hash(),
|
||||
"CLightningWallet",
|
||||
getenv("CLIGHTNING_RPC"),
|
||||
1 if funding_wallet == "CLightningWallet" else 0,
|
||||
),
|
||||
)
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
urlsafe_short_hash(),
|
||||
"SparkWallet",
|
||||
getenv("SPARK_URL"),
|
||||
getenv("SPARK_TOKEN"),
|
||||
1 if funding_wallet == "SparkWallet" else 0,
|
||||
),
|
||||
)
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
urlsafe_short_hash(),
|
||||
"LnbitsWallet",
|
||||
getenv("LNBITS_ENDPOINT"),
|
||||
getenv("LNBITS_KEY"),
|
||||
1 if funding_wallet == "LnbitsWallet" else 0,
|
||||
),
|
||||
)
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO funding (id, backend_wallet, endpoint, port, admin_key, cert, selected)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
urlsafe_short_hash(),
|
||||
"LndWallet",
|
||||
getenv("LND_GRPC_ENDPOINT"),
|
||||
getenv("LND_GRPC_PORT"),
|
||||
getenv("LND_GRPC_MACAROON"),
|
||||
getenv("LND_GRPC_CERT"),
|
||||
1 if funding_wallet == "LndWallet" else 0,
|
||||
),
|
||||
)
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
urlsafe_short_hash(),
|
||||
"LndRestWallet",
|
||||
getenv("LND_REST_ENDPOINT"),
|
||||
getenv("LND_REST_MACAROON"),
|
||||
getenv("LND_REST_CERT"),
|
||||
1 if funding_wallet == "LndWallet" else 0,
|
||||
),
|
||||
)
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO funding (id, backend_wallet, endpoint, admin_key, cert, selected)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
urlsafe_short_hash(),
|
||||
"LNPayWallet",
|
||||
getenv("LNPAY_API_ENDPOINT"),
|
||||
getenv("LNPAY_WALLET_KEY"),
|
||||
getenv("LNPAY_API_KEY"), # this is going in as the cert
|
||||
1 if funding_wallet == "LNPayWallet" else 0,
|
||||
),
|
||||
)
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
urlsafe_short_hash(),
|
||||
"LntxbotWallet",
|
||||
getenv("LNTXBOT_API_ENDPOINT"),
|
||||
getenv("LNTXBOT_KEY"),
|
||||
1 if funding_wallet == "LntxbotWallet" else 0,
|
||||
),
|
||||
)
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
urlsafe_short_hash(),
|
||||
"OpenNodeWallet",
|
||||
getenv("OPENNODE_API_ENDPOINT"),
|
||||
getenv("OPENNODE_KEY"),
|
||||
1 if funding_wallet == "OpenNodeWallet" else 0,
|
||||
),
|
||||
)
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
urlsafe_short_hash(),
|
||||
"SparkWallet",
|
||||
getenv("SPARK_URL"),
|
||||
getenv("SPARK_TOKEN"),
|
||||
1 if funding_wallet == "SparkWallet" else 0,
|
||||
),
|
||||
)
|
||||
|
||||
## PLACEHOLDER FOR ECLAIR WALLET
|
||||
# await db.execute(
|
||||
# """
|
||||
# INSERT INTO funding (id, backend_wallet, endpoint, admin_key, selected)
|
||||
# VALUES (?, ?, ?, ?, ?)
|
||||
# """,
|
||||
# (
|
||||
# urlsafe_short_hash(),
|
||||
# "EclairWallet",
|
||||
# getenv("ECLAIR_URL"),
|
||||
# getenv("ECLAIR_PASS"),
|
||||
# 1 if funding_wallet == "EclairWallet" else 0,
|
||||
# ),
|
||||
# )
|
38
lnbits/extensions/admin/models.py
Normal file
38
lnbits/extensions/admin/models.py
Normal file
@ -0,0 +1,38 @@
|
||||
from typing import NamedTuple
|
||||
from sqlite3 import Row
|
||||
|
||||
class Admin(NamedTuple):
|
||||
user: str
|
||||
site_title: str
|
||||
site_tagline: str
|
||||
site_description:str
|
||||
allowed_users: str
|
||||
admin_user: str
|
||||
default_wallet_name: str
|
||||
data_folder: str
|
||||
disabled_ext: str
|
||||
force_https: str
|
||||
service_fee: str
|
||||
funding_source: str
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row: Row) -> "Admin":
|
||||
data = dict(row)
|
||||
return cls(**data)
|
||||
|
||||
class Funding(NamedTuple):
|
||||
id: str
|
||||
backend_wallet: str
|
||||
endpoint: str
|
||||
port: str
|
||||
read_key: str
|
||||
invoice_key: str
|
||||
admin_key: str
|
||||
cert: str
|
||||
balance: int
|
||||
selected: int
|
||||
|
||||
@classmethod
|
||||
def from_row(cls, row: Row) -> "Funding":
|
||||
data = dict(row)
|
||||
return cls(**data)
|
565
lnbits/extensions/admin/templates/admin/index.html
Normal file
565
lnbits/extensions/admin/templates/admin/index.html
Normal file
@ -0,0 +1,565 @@
|
||||
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
|
||||
%} {% block page %}
|
||||
|
||||
<h3 class="q-my-none">Admin</h3>
|
||||
<p></p>
|
||||
<!--
|
||||
Forked from:
|
||||
https://quasar.dev/vue-components/form#Example--Basic
|
||||
-->
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<q-card class="q-mr-md">
|
||||
<q-form @submit="UpdateLNbits" class="q-px-md q-py-md">
|
||||
<h6 class="q-my-md">Settings</h6>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.site_title"
|
||||
label="Site title"
|
||||
class="q-pr-md"
|
||||
hint="To replace the default 'LNbits' name and tagline"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.tagline"
|
||||
label="Tagline"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
class="q-pr-md"
|
||||
type="text"
|
||||
v-model="data.admin.description"
|
||||
label="Description"
|
||||
hint="Short blurb about your lnbits"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-select
|
||||
filled
|
||||
v-model="data.admin.disabled_ext"
|
||||
multiple
|
||||
hint="Disable extensions *amilk disabled by default as resource heavy"
|
||||
:options="options"
|
||||
label="Disable extensions"
|
||||
></q-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
class="q-pr-md"
|
||||
type="number"
|
||||
v-model="data.admin.service_fee"
|
||||
label="Sevice fee"
|
||||
hint="What percentage to charge per transaction *default 0"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.default_wallet_name"
|
||||
label="Default wallet name"
|
||||
hint="Default name for wallets generated without being named"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
class="q-pr-md"
|
||||
v-model="data.admin.data_folder"
|
||||
label="Data folder"
|
||||
hint="Where your databases will be saved"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
class="q-pr-md"
|
||||
v-model="data.admin.admin_user"
|
||||
label="Admin user"
|
||||
hint=""
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<q-list bordered class="rounded-borders">
|
||||
<q-expansion-item
|
||||
expand-separator
|
||||
icon="payments"
|
||||
:label="data.admin.funding.CLightningWallet.backend_wallet"
|
||||
@click="data.admin.funding.CLightningWallet[7] = 1"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.CLightningWallet.endpoint"
|
||||
label="GRPC Endpoint"
|
||||
class="q-pr-md"
|
||||
hint="ie /home/bob/.lightning/bitcoin/lightning-rpc"
|
||||
></q-input>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
|
||||
<q-expansion-item
|
||||
expand-separator
|
||||
icon="payments"
|
||||
:label="data.admin.funding.SparkWallet.backend_wallet"
|
||||
@click="data.admin.funding.SparkWallet[7] = 1"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.SparkWallet.endpoint"
|
||||
label="LND REST Endpoint"
|
||||
class="q-pr-md"
|
||||
hint="ie http://localhost:9737/rpc"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.SparkWallet.admin_key"
|
||||
label="Access token"
|
||||
class="q-pr-md"
|
||||
hint="Your access token"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
|
||||
<q-expansion-item
|
||||
expand-separator
|
||||
icon="payments"
|
||||
:label="data.admin.funding.LndRestWallet.backend_wallet"
|
||||
@click="data.admin.funding.LndRestWallet[7] = 1"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LndRestWallet.endpoint"
|
||||
label="LND REST Endpoint"
|
||||
class="q-pr-md"
|
||||
hint="default 127.0.0.1"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LndRestWallet.cert"
|
||||
label="LND self-signed cert"
|
||||
class="q-pr-md"
|
||||
hint="Location of your ssl cert"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LndRestWallet.admin_key"
|
||||
label="LND admin macaroon"
|
||||
class="q-pr-md"
|
||||
hint="Your admin macaroon as hex or location"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
|
||||
<q-expansion-item
|
||||
expand-separator
|
||||
icon="payments"
|
||||
:label="data.admin.funding.LndWallet.backend_wallet"
|
||||
@click="data.admin.funding.LndWallet[7] = 1"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LndWallet.endpoint"
|
||||
label="LND GRPC Endpoint"
|
||||
class="q-pr-md"
|
||||
hint="default 127.0.0.1"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LndWallet.port"
|
||||
label="LND GRPC port"
|
||||
class="q-pr-md"
|
||||
hint="Deafault 11009"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LndWallet.cert"
|
||||
label="LND self-signed cert"
|
||||
class="q-pr-md"
|
||||
hint="Location of your ssl cert"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LndWallet.admin_key"
|
||||
label="LND admin macaroon"
|
||||
class="q-pr-md"
|
||||
hint="Your admin macaroon as hex or location"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
|
||||
<q-expansion-item
|
||||
expand-separator
|
||||
icon="payments"
|
||||
:label="data.admin.funding.LntxbotWallet.backend_wallet"
|
||||
@click="data.admin.funding.LntxbotWallet[7] = 1"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LntxbotWallet.admin_key"
|
||||
label="Admin key"
|
||||
class="q-pr-md"
|
||||
hint="use /api in LNTXBOT"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
|
||||
<q-expansion-item
|
||||
expand-separator
|
||||
icon="payments"
|
||||
:label="data.admin.funding.LNPayWallet.backend_wallet"
|
||||
@click="data.admin.funding.LNPayWallet[7] = 1"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LNPayWallet.cert"
|
||||
label="API key"
|
||||
class="q-pr-md"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LNPayWallet.admin_key"
|
||||
label="Admin key"
|
||||
class="q-pr-md q-pb-md"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
|
||||
<q-expansion-item
|
||||
expand-separator
|
||||
icon="payments"
|
||||
:label="data.admin.funding.LnbitsWallet.backend_wallet"
|
||||
@click="data.admin.funding.LnbitsWallet[7] = 1"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LnbitsWallet.endpoint"
|
||||
label="LNbits endpoint"
|
||||
class="q-pr-md"
|
||||
hint="ie https://lnbits.com, default 127.0.0.1"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.LnbitsWallet.admin_key"
|
||||
label="Admin key"
|
||||
class="q-pr-md q-pb-md"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
|
||||
<q-expansion-item
|
||||
expand-separator
|
||||
icon="payments"
|
||||
:label="data.admin.funding.OpenNodeWallet.backend_wallet"
|
||||
@click="data.admin.funding.OpenNodeWallet[7] = 1"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<q-input
|
||||
filled
|
||||
v-model="data.admin.funding.OpenNodeWallet.admin_key"
|
||||
label="Admin key"
|
||||
class="q-pr-md"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item> </q-list
|
||||
><br />
|
||||
<q-select
|
||||
outlined
|
||||
v-model="data.admin.funding_source_primary"
|
||||
style="max-width: 300px"
|
||||
class="q-pr-md"
|
||||
label="Select main funding source"
|
||||
:options="data.funding_source"
|
||||
label="Outlined"
|
||||
></q-select
|
||||
><br />
|
||||
<div>
|
||||
<q-btn label="Update" type="submit" color="primary"></q-btn>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<q-card class="q-mr-md">
|
||||
<q-form class="q-px-md q-py-md" @submit="topupWallet">
|
||||
<div class="text-h6" class="q-px-md">Wallet topup</div>
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<q-input
|
||||
type="text"
|
||||
filled
|
||||
v-model="wallet.data.id"
|
||||
label="Wallet ID"
|
||||
class="q-pr-md"
|
||||
hint="Use the wallet ID to topup any wallet"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<q-input
|
||||
type="number"
|
||||
filled
|
||||
v-model="wallet.data.amount"
|
||||
label="Topup amount"
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<q-btn
|
||||
class="q-mt-md"
|
||||
label="Topup"
|
||||
type="submit"
|
||||
color="primary"
|
||||
></q-btn>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %} {% block scripts %} {{ window_vars(user) }}
|
||||
<script>
|
||||
const queryString = window.location.search
|
||||
const urlParams = new URLSearchParams(queryString)
|
||||
const usr = urlParams.get('usr')
|
||||
new Vue({
|
||||
el: '#vue',
|
||||
mixins: [windowMixin],
|
||||
data: function () {
|
||||
return {
|
||||
wallet: {data: {}},
|
||||
cancel: {},
|
||||
data: {
|
||||
funding_source: [
|
||||
'CLightningWallet',
|
||||
'LndRestWallet',
|
||||
'LndWallet',
|
||||
'LntxbotWallet',
|
||||
'LNPayWallet',
|
||||
'LnbitsWallet',
|
||||
'OpenNodeWallet'
|
||||
],
|
||||
|
||||
admin: {
|
||||
user: '{{ user.id }}',
|
||||
site_title: '{{admin.site_title}}',
|
||||
tagline: '{{admin.site_tagline}}',
|
||||
description: '{{admin.site_description}}',
|
||||
admin_user: '{{admin.admin_user}}',
|
||||
service_fee: parseInt('{{admin.service_fee}}'),
|
||||
default_wallet_name: '{{admin.default_wallet_name}}',
|
||||
data_folder: '{{admin.data_folder}}',
|
||||
funding_source_primary: '{{admin.funding_source}}',
|
||||
disabled_ext: '{{admin.disabled_ext}}'.split(','),
|
||||
edited: [],
|
||||
senddata: {}
|
||||
}
|
||||
},
|
||||
|
||||
options: [
|
||||
'bleskomat',
|
||||
'captcha',
|
||||
'events',
|
||||
'example',
|
||||
'livestream',
|
||||
'lndhub',
|
||||
'lnurlp',
|
||||
'offlineshop',
|
||||
'paywall',
|
||||
'splitpayments',
|
||||
'subdomains',
|
||||
'tpos',
|
||||
'usermanager',
|
||||
'watchonly',
|
||||
'withdraw',
|
||||
'copilot',
|
||||
'hivemind',
|
||||
'jukebox',
|
||||
'lnticket',
|
||||
'ngrok',
|
||||
'amilk'
|
||||
]
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
var self = this
|
||||
if (usr != null) {
|
||||
self.cancel.on = true
|
||||
}
|
||||
funding = JSON.parse(String('{{ funding | tojson|safe }}'))
|
||||
var i
|
||||
for (i = 0; i < funding.length; i++) {
|
||||
self.data.admin.funding[funding[i].backend_wallet] = funding[i]
|
||||
}
|
||||
console.log(self.data.admin)
|
||||
},
|
||||
methods: {
|
||||
topupWallet: function () {
|
||||
var self = this
|
||||
LNbits.api
|
||||
.request(
|
||||
'GET',
|
||||
'/admin/api/v1/admin/' +
|
||||
self.wallet.id +
|
||||
'/' +
|
||||
self.wallet.data.amount,
|
||||
self.g.user.wallets[0].adminkey
|
||||
)
|
||||
.then(function (response) {
|
||||
self.$q.notify({
|
||||
type: 'positive',
|
||||
message:
|
||||
'Success! Added ' +
|
||||
self.wallet.amount +
|
||||
' to ' +
|
||||
self.wallet.id,
|
||||
icon: null
|
||||
})
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
|
||||
createWallet: function () {
|
||||
LNbits.href.createWallet(this.walletName)
|
||||
},
|
||||
addSource: function (source) {
|
||||
var self = this
|
||||
self.data.admin.edited.push(source)
|
||||
console.log(self.data.admin.edited)
|
||||
},
|
||||
UpdateLNbits: function () {
|
||||
var self = this
|
||||
console.log(self.data.admin)
|
||||
LNbits.api
|
||||
.request(
|
||||
'POST',
|
||||
'/admin/api/v1/admin/',
|
||||
self.g.user.wallets[0].adminkey,
|
||||
self.data.admin
|
||||
)
|
||||
.then(function (response) {
|
||||
self.$q.notify({
|
||||
type: 'positive',
|
||||
message:
|
||||
'Success! Added ' +
|
||||
self.wallet.amount +
|
||||
' to ' +
|
||||
self.wallet.id,
|
||||
icon: null
|
||||
})
|
||||
})
|
||||
.catch(function (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
})
|
||||
},
|
||||
|
||||
processing: function () {
|
||||
this.$q.notify({
|
||||
timeout: 0,
|
||||
message: 'Processing...',
|
||||
icon: null
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
20
lnbits/extensions/admin/views.py
Normal file
20
lnbits/extensions/admin/views.py
Normal file
@ -0,0 +1,20 @@
|
||||
from quart import g, render_template, request, jsonify
|
||||
import json
|
||||
|
||||
from lnbits.decorators import check_user_exists, validate_uuids
|
||||
from lnbits.extensions.admin import admin_ext
|
||||
from lnbits.core.crud import get_user, create_account
|
||||
from .crud import get_admin, get_funding
|
||||
from lnbits.settings import WALLET
|
||||
|
||||
|
||||
@admin_ext.route("/")
|
||||
@validate_uuids(["usr"], required=True)
|
||||
@check_user_exists()
|
||||
async def index():
|
||||
user_id = g.user
|
||||
admin = await get_admin()
|
||||
|
||||
funding = [{**funding._asdict()} for funding in await get_funding()]
|
||||
|
||||
return await render_template("admin/index.html", user=g.user, admin=admin, funding=funding)
|
41
lnbits/extensions/admin/views_api.py
Normal file
41
lnbits/extensions/admin/views_api.py
Normal file
@ -0,0 +1,41 @@
|
||||
from quart import jsonify, g, request
|
||||
from http import HTTPStatus
|
||||
from .crud import update_wallet_balance
|
||||
from lnbits.extensions.admin import admin_ext
|
||||
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
|
||||
from lnbits.core.crud import get_wallet
|
||||
from .crud import get_admin,update_admin
|
||||
import json
|
||||
|
||||
@admin_ext.route("/api/v1/admin/<wallet_id>/<topup_amount>", methods=["GET"])
|
||||
@api_check_wallet_key("admin")
|
||||
async def api_update_balance(wallet_id, topup_amount):
|
||||
print(g.data.wallet)
|
||||
try:
|
||||
wallet = await get_wallet(wallet_id)
|
||||
except:
|
||||
return (
|
||||
jsonify({"error": "Not allowed: not an admin"}),
|
||||
HTTPStatus.FORBIDDEN,
|
||||
)
|
||||
print(wallet)
|
||||
print(topup_amount)
|
||||
return jsonify({"status": "Success"}), HTTPStatus.OK
|
||||
|
||||
|
||||
@admin_ext.route("/api/v1/admin/", methods=["POST"])
|
||||
@api_check_wallet_key("admin")
|
||||
@api_validate_post_request(schema={})
|
||||
async def api_update_admin():
|
||||
body = await request.get_json()
|
||||
admin = await get_admin()
|
||||
print(g.wallet[2])
|
||||
print(body["admin_user"])
|
||||
if not admin.admin_user == g.wallet[2] and admin.admin_user != None:
|
||||
return (
|
||||
jsonify({"error": "Not allowed: not an admin"}),
|
||||
HTTPStatus.FORBIDDEN,
|
||||
)
|
||||
updated = await update_admin(body)
|
||||
print(updated)
|
||||
return jsonify({"status": "Success"}), HTTPStatus.OK
|
Loading…
Reference in New Issue
Block a user