fix admin
This commit is contained in:
parent
5aa9cdd456
commit
6a2c741478
10 changed files with 576 additions and 1474 deletions
|
|
@ -2,10 +2,11 @@ from typing import List
|
||||||
|
|
||||||
from lnbits.core.crud import create_payment
|
from lnbits.core.crud import create_payment
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
from lnbits.helpers import urlsafe_short_hash
|
||||||
|
from lnbits.settings import Settings
|
||||||
from lnbits.tasks import internal_invoice_queue
|
from lnbits.tasks import internal_invoice_queue
|
||||||
|
|
||||||
from . import db
|
from . import db
|
||||||
from .models import Admin, Funding
|
from .models import Funding
|
||||||
|
|
||||||
|
|
||||||
async def update_wallet_balance(wallet_id: str, amount: int) -> str:
|
async def update_wallet_balance(wallet_id: str, amount: int) -> str:
|
||||||
|
|
@ -23,26 +24,26 @@ async def update_wallet_balance(wallet_id: str, amount: int) -> str:
|
||||||
)
|
)
|
||||||
# manually send this for now
|
# manually send this for now
|
||||||
await internal_invoice_queue.put(internal_id)
|
await internal_invoice_queue.put(internal_id)
|
||||||
return payment
|
|
||||||
|
|
||||||
|
|
||||||
async def update_admin(user: str, **kwargs) -> Admin:
|
async def update_settings(user: str, **kwargs) -> Settings:
|
||||||
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
|
||||||
# print("UPDATE", q)
|
# print("UPDATE", q)
|
||||||
await db.execute(
|
await db.execute(
|
||||||
f'UPDATE admin.admin SET {q} WHERE "user" = ?', (*kwargs.values(), user)
|
f'UPDATE admin.settings SET {q} WHERE "user" = ?', (*kwargs.values(), user)
|
||||||
)
|
)
|
||||||
row = await db.fetchone('SELECT * FROM admin.admin WHERE "user" = ?', (user,))
|
row = await db.fetchone('SELECT * FROM admin.settings WHERE "user" = ?', (user,))
|
||||||
assert row, "Newly updated settings couldn't be retrieved"
|
assert row, "Newly updated settings couldn't be retrieved"
|
||||||
return Admin(**row) if row else None
|
return Settings(**row) if row else None
|
||||||
|
|
||||||
|
|
||||||
async def get_admin() -> Admin:
|
|
||||||
row = await db.fetchone("SELECT * FROM admin.admin")
|
|
||||||
return Admin(**row) if row else None
|
|
||||||
|
|
||||||
|
|
||||||
async def update_funding(data: Funding) -> Funding:
|
async def update_funding(data: Funding) -> Funding:
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
UPDATE admin.settings SET funding_source = ? WHERE user = ?
|
||||||
|
""",
|
||||||
|
(data.backend_wallet, data.user),
|
||||||
|
)
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
UPDATE admin.funding
|
UPDATE admin.funding
|
||||||
|
|
@ -69,5 +70,4 @@ async def update_funding(data: Funding) -> Funding:
|
||||||
|
|
||||||
async def get_funding() -> List[Funding]:
|
async def get_funding() -> List[Funding]:
|
||||||
rows = await db.fetchall("SELECT * FROM admin.funding")
|
rows = await db.fetchall("SELECT * FROM admin.funding")
|
||||||
|
|
||||||
return [Funding(**row) for row in rows]
|
return [Funding(**row) for row in rows]
|
||||||
|
|
|
||||||
|
|
@ -1,292 +1,57 @@
|
||||||
from os import getenv
|
async def m001_create_admin_settings_table(db):
|
||||||
|
|
||||||
from sqlalchemy.exc import OperationalError # type: ignore
|
|
||||||
|
|
||||||
from lnbits.config import conf
|
|
||||||
from lnbits.helpers import urlsafe_short_hash
|
|
||||||
|
|
||||||
|
|
||||||
async def get_admin_user():
|
|
||||||
if len(conf.admin_users) > 0:
|
|
||||||
return conf.admin_users[0]
|
|
||||||
from lnbits.core.crud import create_account, get_user
|
|
||||||
|
|
||||||
print("Seems like there's no admin users yet. Let's create an account for you!")
|
|
||||||
account = await create_account()
|
|
||||||
user = account.id
|
|
||||||
assert user, "Newly created user couldn't be retrieved"
|
|
||||||
print(
|
|
||||||
f"Your newly created account/user id is: {user}. This will be the Super Admin user."
|
|
||||||
)
|
|
||||||
conf.admin_users.insert(0, user)
|
|
||||||
return user
|
|
||||||
|
|
||||||
|
|
||||||
async def m001_create_admin_table(db):
|
|
||||||
|
|
||||||
|
|
||||||
# users/server
|
|
||||||
user = await get_admin_user()
|
|
||||||
admin_users = ",".join(conf.admin_users)
|
|
||||||
allowed_users = ",".join(conf.allowed_users)
|
|
||||||
admin_ext = ",".join(conf.admin_ext)
|
|
||||||
disabled_ext = ",".join(conf.disabled_ext)
|
|
||||||
funding_source = conf.funding_source
|
|
||||||
# operational
|
|
||||||
data_folder = conf.data_folder
|
|
||||||
database_url = conf.database_url
|
|
||||||
force_https = conf.force_https
|
|
||||||
reserve_fee_min = conf.reserve_fee_min
|
|
||||||
reserve_fee_pct = conf.reserve_fee_pct
|
|
||||||
service_fee = conf.service_fee
|
|
||||||
hide_api = conf.hide_api
|
|
||||||
denomination = conf.denomination
|
|
||||||
# Theme'ing
|
|
||||||
site_title = conf.site_title
|
|
||||||
site_tagline = conf.site_tagline
|
|
||||||
site_description = conf.site_description
|
|
||||||
default_wallet_name = conf.default_wallet_name
|
|
||||||
theme = ",".join(conf.theme)
|
|
||||||
custom_logo = conf.custom_logo
|
|
||||||
ad_space = ",".join(conf.ad_space)
|
|
||||||
|
|
||||||
await db.execute(
|
await db.execute(
|
||||||
"""
|
"""
|
||||||
CREATE TABLE IF NOT EXISTS admin.admin (
|
CREATE TABLE IF NOT EXISTS admin.settings (
|
||||||
"user" TEXT PRIMARY KEY,
|
lnbits_admin_ui TEXT,
|
||||||
admin_users TEXT,
|
debug TEXT,
|
||||||
allowed_users TEXT,
|
host TEXT,
|
||||||
admin_ext TEXT,
|
|
||||||
disabled_ext TEXT,
|
|
||||||
funding_source TEXT,
|
|
||||||
data_folder TEXT,
|
|
||||||
database_url TEXT,
|
|
||||||
force_https BOOLEAN,
|
|
||||||
reserve_fee_min INT,
|
|
||||||
reserve_fee_pct REAL,
|
|
||||||
service_fee REAL,
|
|
||||||
hide_api BOOLEAN,
|
|
||||||
denomination TEXT,
|
|
||||||
site_title TEXT,
|
|
||||||
site_tagline TEXT,
|
|
||||||
site_description TEXT,
|
|
||||||
default_wallet_name TEXT,
|
|
||||||
theme TEXT,
|
|
||||||
custom_logo TEXT,
|
|
||||||
ad_space TEXT
|
|
||||||
);
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
await db.execute(
|
|
||||||
"""
|
|
||||||
INSERT INTO admin.admin (
|
|
||||||
"user",
|
|
||||||
admin_users,
|
|
||||||
allowed_users,
|
|
||||||
admin_ext,
|
|
||||||
disabled_ext,
|
|
||||||
funding_source,
|
|
||||||
data_folder,
|
|
||||||
database_url,
|
|
||||||
force_https,
|
|
||||||
reserve_fee_min,
|
|
||||||
reserve_fee_pct,
|
|
||||||
service_fee,
|
|
||||||
hide_api,
|
|
||||||
denomination,
|
|
||||||
site_title,
|
|
||||||
site_tagline,
|
|
||||||
site_description,
|
|
||||||
default_wallet_name,
|
|
||||||
theme,
|
|
||||||
custom_logo,
|
|
||||||
ad_space)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
||||||
""",
|
|
||||||
(
|
|
||||||
user,
|
|
||||||
admin_users,
|
|
||||||
allowed_users,
|
|
||||||
admin_ext,
|
|
||||||
disabled_ext,
|
|
||||||
funding_source,
|
|
||||||
data_folder,
|
|
||||||
database_url,
|
|
||||||
force_https,
|
|
||||||
reserve_fee_min,
|
|
||||||
reserve_fee_pct,
|
|
||||||
service_fee,
|
|
||||||
hide_api,
|
|
||||||
denomination,
|
|
||||||
site_title,
|
|
||||||
site_tagline,
|
|
||||||
site_description,
|
|
||||||
default_wallet_name,
|
|
||||||
theme,
|
|
||||||
custom_logo,
|
|
||||||
ad_space,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
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 admin.funding (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
backend_wallet TEXT,
|
|
||||||
endpoint TEXT,
|
|
||||||
port INT,
|
port INT,
|
||||||
read_key TEXT,
|
lnbits_path TEXT,
|
||||||
invoice_key TEXT,
|
lnbits_commit TEXT,
|
||||||
admin_key TEXT,
|
lnbits_admin_users TEXT,
|
||||||
cert TEXT,
|
lnbits_allowed_users TEXT,
|
||||||
balance INT,
|
lnbits_allowed_funding_sources TEXT,
|
||||||
selected INT
|
lnbits_admin_extensions TEXT,
|
||||||
|
lnbits_disabled_extensions TEXT,
|
||||||
|
lnbits_site_title TEXT,
|
||||||
|
lnbits_site_tagline TEXT,
|
||||||
|
lnbits_site_description TEXT,
|
||||||
|
lnbits_default_wallet_name TEXT,
|
||||||
|
lnbits_theme_options TEXT,
|
||||||
|
lnbits_custom_logo TEXT,
|
||||||
|
lnbits_ad_space TEXT,
|
||||||
|
lnbits_data_folder TEXT,
|
||||||
|
lnbits_database_url TEXT,
|
||||||
|
lnbits_force_https TEXT,
|
||||||
|
lnbits_reserve_fee_min TEXT,
|
||||||
|
lnbits_reserve_fee_percent TEXT,
|
||||||
|
lnbits_service_fee TEXT,
|
||||||
|
lnbits_hide_api TEXT,
|
||||||
|
lnbits_denomination TEXT,
|
||||||
|
lnbits_backend_wallet_class TEXT,
|
||||||
|
fake_wallet_secret TEXT,
|
||||||
|
lnbits_endpoint TEXT,
|
||||||
|
lnbits_key TEXT,
|
||||||
|
cliche_endpoint TEXT,
|
||||||
|
corelightning_rpc TEXT,
|
||||||
|
eclair_url TEXT,
|
||||||
|
eclair_pass TEXT,
|
||||||
|
lnd_rest_endpoint TEXT,
|
||||||
|
lnd_rest_cert TEXT,
|
||||||
|
lnd_rest_macaroon TEXT,
|
||||||
|
lnpay_api_endpoint TEXT,
|
||||||
|
lnpay_api_key TEXT,
|
||||||
|
lnpay_wallet_key TEXT,
|
||||||
|
lntxbot_api_endpoint TEXT,
|
||||||
|
lntxbot_key TEXT,
|
||||||
|
opennode_api_endpoint TEXT,
|
||||||
|
opennode_key TEXT,
|
||||||
|
spark_url TEXT,
|
||||||
|
spark_token TEXT,
|
||||||
|
boltz_network TEXT,
|
||||||
|
boltz_url TEXT,
|
||||||
|
boltz_mempool_space_url TEXT,
|
||||||
|
boltz_mempool_space_url_ws TEXT
|
||||||
);
|
);
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
await db.execute(
|
|
||||||
"""
|
|
||||||
INSERT INTO admin.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 admin.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 admin.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 admin.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 admin.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 admin.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 admin.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 admin.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 admin.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 admin.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,
|
|
||||||
# ),
|
|
||||||
# )
|
|
||||||
|
|
|
||||||
|
|
@ -29,36 +29,36 @@ class UpdateAdminSettings(BaseModel):
|
||||||
ad_space: str = Query(None)
|
ad_space: str = Query(None)
|
||||||
|
|
||||||
|
|
||||||
class Admin(BaseModel):
|
# class Admin(BaseModel):
|
||||||
# users
|
# # users
|
||||||
user: str
|
# user: str
|
||||||
admin_users: Optional[str]
|
# admin_users: Optional[str]
|
||||||
allowed_users: Optional[str]
|
# allowed_users: Optional[str]
|
||||||
admin_ext: Optional[str]
|
# admin_ext: Optional[str]
|
||||||
disabled_ext: Optional[str]
|
# disabled_ext: Optional[str]
|
||||||
funding_source: Optional[str]
|
# funding_source: Optional[str]
|
||||||
# ops
|
# # ops
|
||||||
data_folder: Optional[str]
|
# data_folder: Optional[str]
|
||||||
database_url: Optional[str]
|
# database_url: Optional[str]
|
||||||
force_https: bool = Field(default=True)
|
# force_https: bool = Field(default=True)
|
||||||
reserve_fee_min: Optional[int]
|
# reserve_fee_min: Optional[int]
|
||||||
reserve_fee_pct: Optional[float]
|
# reserve_fee_pct: Optional[float]
|
||||||
service_fee: float = Optional[float]
|
# service_fee: float = Optional[float]
|
||||||
hide_api: bool = Field(default=False)
|
# hide_api: bool = Field(default=False)
|
||||||
# Change theme
|
# # Change theme
|
||||||
site_title: Optional[str]
|
# site_title: Optional[str]
|
||||||
site_tagline: Optional[str]
|
# site_tagline: Optional[str]
|
||||||
site_description: Optional[str]
|
# site_description: Optional[str]
|
||||||
default_wallet_name: Optional[str]
|
# default_wallet_name: Optional[str]
|
||||||
denomination: str = Field(default="sats")
|
# denomination: str = Field(default="sats")
|
||||||
theme: Optional[str]
|
# theme: Optional[str]
|
||||||
custom_logo: Optional[str]
|
# custom_logo: Optional[str]
|
||||||
ad_space: Optional[str]
|
# ad_space: Optional[str]
|
||||||
|
|
||||||
@classmethod
|
# @classmethod
|
||||||
def from_row(cls, row: Row) -> "Admin":
|
# def from_row(cls, row: Row) -> "Admin":
|
||||||
data = dict(row)
|
# data = dict(row)
|
||||||
return cls(**data)
|
# return cls(**data)
|
||||||
|
|
||||||
|
|
||||||
class Funding(BaseModel):
|
class Funding(BaseModel):
|
||||||
|
|
|
||||||
158
lnbits/extensions/admin/templates/admin/_tab_funding.html
Normal file
158
lnbits/extensions/admin/templates/admin/_tab_funding.html
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
<q-tab-panel name="funding">
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<h6 class="q-my-none">Wallets Management</h6>
|
||||||
|
<br />
|
||||||
|
<div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<p>Funding Source Info</p>
|
||||||
|
<ul>
|
||||||
|
{%raw%}
|
||||||
|
<li>
|
||||||
|
Funding Source: {{data.settings.lnbits_backend_wallet_class}}
|
||||||
|
</li>
|
||||||
|
<li>Balance: {{data.balance / 1000}} sats</li>
|
||||||
|
{%endraw%}
|
||||||
|
</ul>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<p>Active Funding<small> (Requires server restart)</small></p>
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
v-model="data.settings.lnbits_backend_wallet_class"
|
||||||
|
hint="Select the active funding wallet"
|
||||||
|
:options="data.settings.lnbits_funding_sources"
|
||||||
|
></q-select>
|
||||||
|
<br />
|
||||||
|
<div>
|
||||||
|
<q-btn
|
||||||
|
class="q-mt-md float-right"
|
||||||
|
label="Restart Server"
|
||||||
|
color="primary"
|
||||||
|
@click="restartServer"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<p>Fee reserve</p>
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-6">
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
type="number"
|
||||||
|
filled
|
||||||
|
v-model="data.settings.lnbits_reserve_fee_min"
|
||||||
|
label="Reserve fee in msats"
|
||||||
|
></q-input>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
type="number"
|
||||||
|
filled
|
||||||
|
v-model="data.settings.lnbits_reserve_fee_percent"
|
||||||
|
label="Reserve fee in percent"
|
||||||
|
step="0.1"
|
||||||
|
></q-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>TopUp a wallet</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
type="text"
|
||||||
|
filled
|
||||||
|
v-model="wallet.data.id"
|
||||||
|
label="Wallet ID"
|
||||||
|
hint="Use the wallet ID to topup any wallet"
|
||||||
|
></q-input>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
type="number"
|
||||||
|
filled
|
||||||
|
v-model="wallet.data.amount"
|
||||||
|
label="Topup amount"
|
||||||
|
></q-input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<q-btn
|
||||||
|
class="q-mt-md float-right"
|
||||||
|
label="Topup"
|
||||||
|
color="primary"
|
||||||
|
@click="topupWallet"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>Funding Sources</p>
|
||||||
|
{% raw %}
|
||||||
|
<q-list v-for="fund in data.settings.funding" :key="fund.id">
|
||||||
|
<q-expansion-item
|
||||||
|
expand-separator
|
||||||
|
icon="payments"
|
||||||
|
:label="fund.backend_wallet"
|
||||||
|
>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<q-input
|
||||||
|
v-if="fund.endpoint"
|
||||||
|
filled
|
||||||
|
type="text"
|
||||||
|
v-model="fund.endpoint"
|
||||||
|
label="Endpoint"
|
||||||
|
class="q-pr-md"
|
||||||
|
></q-input>
|
||||||
|
<q-input
|
||||||
|
v-if="fund.port"
|
||||||
|
filled
|
||||||
|
type="text"
|
||||||
|
v-model="fund.port"
|
||||||
|
label="Port"
|
||||||
|
class="q-pr-md"
|
||||||
|
></q-input>
|
||||||
|
<q-input
|
||||||
|
v-if="fund.admin_key"
|
||||||
|
filled
|
||||||
|
type="text"
|
||||||
|
v-model="fund.admin_key"
|
||||||
|
label="Admin Key"
|
||||||
|
class="q-pr-md"
|
||||||
|
></q-input>
|
||||||
|
<q-input
|
||||||
|
v-if="fund.cert"
|
||||||
|
filled
|
||||||
|
type="text"
|
||||||
|
v-model="fund.cert"
|
||||||
|
label="Location of your ssl cert"
|
||||||
|
class="q-pr-md"
|
||||||
|
></q-input>
|
||||||
|
<q-btn
|
||||||
|
class="q-mt-md"
|
||||||
|
label="Save"
|
||||||
|
flat
|
||||||
|
color="primary"
|
||||||
|
@click="updateFunding(fund.backend_wallet)"
|
||||||
|
></q-btn>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-expansion-item>
|
||||||
|
</q-list>
|
||||||
|
{% endraw %}
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-tab-panel>
|
||||||
78
lnbits/extensions/admin/templates/admin/_tab_server.html
Normal file
78
lnbits/extensions/admin/templates/admin/_tab_server.html
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
<q-tab-panel name="server">
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<h6 class="q-my-none">Server Management</h6>
|
||||||
|
<br />
|
||||||
|
<div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<p>Server Info</p>
|
||||||
|
<ul>
|
||||||
|
{%raw%}
|
||||||
|
<li v-if="data.settings.lnbits_data_folder">
|
||||||
|
SQlite: {{data.settings.lnbits_data_folder}}
|
||||||
|
</li>
|
||||||
|
<li v-if="data.settings.lnbits_database_url">
|
||||||
|
Postgres: {{data.settings.lnbits_database_url}}
|
||||||
|
</li>
|
||||||
|
{%endraw%}
|
||||||
|
</ul>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Service Fee</p>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
type="number"
|
||||||
|
v-model.number="data.settings.lnbits_service_fee"
|
||||||
|
label="Service fee (%)"
|
||||||
|
step="0.1"
|
||||||
|
hint="Fee charged per tx (%)"
|
||||||
|
></q-input>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Miscelaneous</p>
|
||||||
|
<q-item tag="label" v-ripple>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Force HTTPS</q-item-label>
|
||||||
|
<q-item-label caption>Prefer secure URLs</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-toggle
|
||||||
|
size="md"
|
||||||
|
v-model="data.settings.lnbits_force_https"
|
||||||
|
checked-icon="check"
|
||||||
|
color="green"
|
||||||
|
unchecked-icon="clear"
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item tag="label" v-ripple>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Hide API</q-item-label>
|
||||||
|
<q-item-label caption
|
||||||
|
>Hides wallet api, extensions can choose to honor</q-item-label
|
||||||
|
>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-toggle
|
||||||
|
size="md"
|
||||||
|
v-model="data.settings.lnbits_hide_api"
|
||||||
|
checked-icon="check"
|
||||||
|
color="green"
|
||||||
|
unchecked-icon="clear"
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn unelevated color="primary" type="submit">Save</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-tab-panel>
|
||||||
122
lnbits/extensions/admin/templates/admin/_tab_theme.html
Normal file
122
lnbits/extensions/admin/templates/admin/_tab_theme.html
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
<q-tab-panel name="theme">
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<h6 class="q-my-none">UI Management</h6>
|
||||||
|
<br />
|
||||||
|
<div>
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Site Title</p>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
type="text"
|
||||||
|
v-model="data.settings.lnbits_site_title"
|
||||||
|
label="Site title"
|
||||||
|
></q-input>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Site Tagline</p>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
type="text"
|
||||||
|
v-model="data.settings.lnbits_site_tagline"
|
||||||
|
label="Site tagline"
|
||||||
|
></q-input>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>Site Description</p>
|
||||||
|
<q-input
|
||||||
|
v-model="data.settings.lnbits_site_description"
|
||||||
|
filled
|
||||||
|
type="textarea"
|
||||||
|
hint="Use plain text or raw HTML"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Default Wallet Name</p>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
type="text"
|
||||||
|
v-model="data.settings.lnbits_default_wallet_name"
|
||||||
|
label="LNbits wallet"
|
||||||
|
></q-input>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Denomination</p>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
type="text"
|
||||||
|
v-model="data.settings.lnbits_denomination"
|
||||||
|
label="sats"
|
||||||
|
hint="The name for the FakeWallet token"
|
||||||
|
></q-input>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Themes</p>
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
v-model="data.settings.lnbits_theme"
|
||||||
|
multiple
|
||||||
|
hint="Choose themes available for users"
|
||||||
|
:options="themes"
|
||||||
|
label="Themes"
|
||||||
|
></q-select>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Advertisement Slots</p>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
v-model="data.ad_space_add"
|
||||||
|
@keydown.enter="addAdSpace"
|
||||||
|
type="text"
|
||||||
|
label="Ad image URL"
|
||||||
|
hint="Ad image filepaths or urls, extensions can choose to honor"
|
||||||
|
>
|
||||||
|
<q-btn @click="addAdSpace" dense flat icon="add"></q-btn>
|
||||||
|
</q-input>
|
||||||
|
<div>
|
||||||
|
{% raw %}
|
||||||
|
<q-chip
|
||||||
|
v-for="space in data.settings.lnbits_ad_space"
|
||||||
|
:key="space"
|
||||||
|
removable
|
||||||
|
@remove="removeAdSpace(space)"
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
>
|
||||||
|
{{ space.slice(0, 8) + " ... " + space.slice(-8) }}
|
||||||
|
</q-chip>
|
||||||
|
{% endraw %}
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Custom Logo</p>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
type="text"
|
||||||
|
v-model="data.settings.lnbits_custom_logo"
|
||||||
|
label="https://example.com/image.png"
|
||||||
|
hint="URL to logo image"
|
||||||
|
></q-input>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn unelevated color="primary" type="submit">Save</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-tab-panel>
|
||||||
96
lnbits/extensions/admin/templates/admin/_tab_users.html
Normal file
96
lnbits/extensions/admin/templates/admin/_tab_users.html
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
<q-tab-panel name="users">
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<h6 class="q-my-none">User Management</h6>
|
||||||
|
<br />
|
||||||
|
<p class="q-my-none">
|
||||||
|
Super Admin: {% raw %}{{this.data.settings.lnbits_admin_users[0]}}{%
|
||||||
|
endraw %}
|
||||||
|
</p>
|
||||||
|
<br />
|
||||||
|
<div>
|
||||||
|
<p>Admin Users</p>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
v-model="data.admin_users_add"
|
||||||
|
@keydown.enter="addAdminUser"
|
||||||
|
type="text"
|
||||||
|
label="User ID"
|
||||||
|
hint="Users with admin privileges"
|
||||||
|
>
|
||||||
|
<q-btn @click="addAdminUser" dense flat icon="add"></q-btn>
|
||||||
|
</q-input>
|
||||||
|
<div>
|
||||||
|
{% raw %}
|
||||||
|
<q-chip
|
||||||
|
v-for="user in data.settings.lnbits_admin_users.slice(1)"
|
||||||
|
:key="user"
|
||||||
|
removable
|
||||||
|
@remove="removeAdminUser(user)"
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
>
|
||||||
|
{{ user }}
|
||||||
|
</q-chip>
|
||||||
|
{% endraw %}
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>Allowed Users</p>
|
||||||
|
<q-input
|
||||||
|
filled
|
||||||
|
v-model="data.allowed_users_add"
|
||||||
|
@keydown.enter="addAllowedUser"
|
||||||
|
type="text"
|
||||||
|
label="User ID"
|
||||||
|
hint="Only these users can use LNbits"
|
||||||
|
>
|
||||||
|
<q-btn @click="addAllowedUser" dense flat icon="add"></q-btn>
|
||||||
|
</q-input>
|
||||||
|
<div>
|
||||||
|
{% raw %}
|
||||||
|
<q-chip
|
||||||
|
v-for="user in data.settings.lnbits_allowed_users"
|
||||||
|
:key="user"
|
||||||
|
removable
|
||||||
|
@remove="removeAllowedUser(user)"
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
>
|
||||||
|
{{ user }}
|
||||||
|
</q-chip>
|
||||||
|
{% endraw %}
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Admin Extensions</p>
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
v-model="data.settings.admin_extensions"
|
||||||
|
multiple
|
||||||
|
hint="Extensions only user with admin privileges can use"
|
||||||
|
:options="options"
|
||||||
|
label="Admin extensions"
|
||||||
|
></q-select>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p>Disabled Extensions</p>
|
||||||
|
<q-select
|
||||||
|
filled
|
||||||
|
v-model="data.settings.lnbits_disabled_extensions"
|
||||||
|
multiple
|
||||||
|
hint="Disable extensions *amilk disabled by default as resource heavy"
|
||||||
|
:options="options"
|
||||||
|
label="Disable extensions"
|
||||||
|
></q-select>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row q-mt-lg">
|
||||||
|
<q-btn unelevated color="primary" type="submit">Save</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-tab-panel>
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -7,30 +7,26 @@ from fastapi.templating import Jinja2Templates
|
||||||
from starlette.responses import HTMLResponse
|
from starlette.responses import HTMLResponse
|
||||||
|
|
||||||
from lnbits.core.models import User
|
from lnbits.core.models import User
|
||||||
from lnbits.decorators import check_user_exists
|
from lnbits.decorators import check_admin
|
||||||
from lnbits.extensions.admin import admin_ext
|
|
||||||
from lnbits.requestvars import g
|
from lnbits.requestvars import g
|
||||||
|
from lnbits.settings import WALLET, settings
|
||||||
|
|
||||||
from . import admin_ext, admin_renderer
|
from . import admin_ext, admin_renderer
|
||||||
from .crud import get_admin, get_funding
|
from .crud import get_funding
|
||||||
|
|
||||||
templates = Jinja2Templates(directory="templates")
|
templates = Jinja2Templates(directory="templates")
|
||||||
|
|
||||||
|
|
||||||
@admin_ext.get("/", response_class=HTMLResponse)
|
@admin_ext.get("/", response_class=HTMLResponse)
|
||||||
async def index(request: Request, user: User = Depends(check_user_exists)):
|
async def index(request: Request, user: User = Depends(check_admin)):
|
||||||
admin = await get_admin()
|
error, balance = await WALLET.status()
|
||||||
funding = [f.dict() for f in await get_funding()]
|
|
||||||
error, balance = await g().WALLET.status()
|
|
||||||
|
|
||||||
return admin_renderer().TemplateResponse(
|
return admin_renderer().TemplateResponse(
|
||||||
"admin/index.html",
|
"admin/index.html",
|
||||||
{
|
{
|
||||||
"request": request,
|
"request": request,
|
||||||
"user": user.dict(),
|
"user": user.dict(),
|
||||||
"admin": admin.dict(),
|
"settings": settings.dict(),
|
||||||
"funding": funding,
|
|
||||||
"settings": g().admin_conf.dict(),
|
|
||||||
"balance": balance,
|
"balance": balance,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,27 @@
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
# from config import conf
|
|
||||||
from fastapi import Body, Depends, Request
|
from fastapi import Body, Depends, Request
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
|
|
||||||
from lnbits.core.crud import get_wallet
|
from lnbits.core.crud import get_wallet
|
||||||
from lnbits.decorators import WalletTypeInfo, require_admin_key
|
from lnbits.core.models import User
|
||||||
|
from lnbits.decorators import WalletTypeInfo, check_admin, require_admin_key
|
||||||
from lnbits.extensions.admin import admin_ext
|
from lnbits.extensions.admin import admin_ext
|
||||||
from lnbits.extensions.admin.models import Admin, Funding, UpdateAdminSettings
|
from lnbits.extensions.admin.models import Funding, UpdateAdminSettings
|
||||||
from lnbits.helpers import removeEmptyString
|
from lnbits.helpers import removeEmptyString
|
||||||
from lnbits.requestvars import g
|
from lnbits.requestvars import g
|
||||||
|
from lnbits.server import server_restart
|
||||||
|
from lnbits.settings import settings
|
||||||
|
|
||||||
from .crud import get_admin, update_admin, update_funding, update_wallet_balance
|
from .crud import update_funding, update_settings, update_wallet_balance
|
||||||
|
|
||||||
|
|
||||||
|
@admin_ext.get("/api/v1/admin/restart/", status_code=HTTPStatus.OK)
|
||||||
|
async def api_restart_server(
|
||||||
|
g: WalletTypeInfo = Depends(require_admin_key), # type: ignore
|
||||||
|
):
|
||||||
|
server_restart.set()
|
||||||
|
return {"status": "Success"}
|
||||||
|
|
||||||
|
|
||||||
@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK)
|
@admin_ext.get("/api/v1/admin/{wallet_id}/{topup_amount}", status_code=HTTPStatus.OK)
|
||||||
|
|
@ -36,9 +46,7 @@ async def api_update_admin(
|
||||||
data: UpdateAdminSettings = Body(...),
|
data: UpdateAdminSettings = Body(...),
|
||||||
w: WalletTypeInfo = Depends(require_admin_key),
|
w: WalletTypeInfo = Depends(require_admin_key),
|
||||||
):
|
):
|
||||||
admin = await get_admin()
|
if not settings.user == w.wallet.user:
|
||||||
# print(data)
|
|
||||||
if not admin.user == w.wallet.user:
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
|
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
|
||||||
)
|
)
|
||||||
|
|
@ -53,7 +61,6 @@ async def api_update_admin(
|
||||||
|
|
||||||
g().admin_conf = g().admin_conf.copy(update=updated.dict())
|
g().admin_conf = g().admin_conf.copy(update=updated.dict())
|
||||||
|
|
||||||
# print(g().admin_conf)
|
|
||||||
return {"status": "Success"}
|
return {"status": "Success"}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -63,11 +70,10 @@ async def api_update_funding(
|
||||||
data: Funding = Body(...),
|
data: Funding = Body(...),
|
||||||
w: WalletTypeInfo = Depends(require_admin_key),
|
w: WalletTypeInfo = Depends(require_admin_key),
|
||||||
):
|
):
|
||||||
admin = await get_admin()
|
if not settings.user == w.wallet.user:
|
||||||
|
|
||||||
if not admin.user == w.wallet.user:
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
|
status_code=HTTPStatus.FORBIDDEN, detail="Not allowed: not an admin"
|
||||||
)
|
)
|
||||||
|
|
||||||
funding = await update_funding(data=data)
|
funding = await update_funding(data=data)
|
||||||
return funding
|
return funding
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue