mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-02-22 22:25:47 +01:00
Merge branch 'main' into matthewcroughan/nixify
This commit is contained in:
commit
244d6f23bf
114 changed files with 1720 additions and 988 deletions
|
@ -1,6 +1,21 @@
|
|||
.git
|
||||
docs
|
||||
data
|
||||
docker
|
||||
docs
|
||||
tests
|
||||
venv
|
||||
tools
|
||||
|
||||
# ignore all the markdown
|
||||
*.md
|
||||
*.log
|
||||
|
||||
.env
|
||||
|
||||
.gitignore
|
||||
.prettierrc
|
||||
LICENSE
|
||||
Makefile
|
||||
mypy.ini
|
||||
package-lock.json
|
||||
package.json
|
||||
pytest.ini
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.py]
|
||||
indent_size = 4
|
||||
indent_style = space
|
|
@ -1,7 +1,7 @@
|
|||
HOST=127.0.0.1
|
||||
PORT=5000
|
||||
|
||||
DEBUG=true
|
||||
DEBUG=false
|
||||
|
||||
LNBITS_ALLOWED_USERS=""
|
||||
LNBITS_ADMIN_USERS=""
|
||||
|
|
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
@ -1 +1 @@
|
|||
custom: https://lnbits.com/paywall/GAqKguK5S8f6w5VNjS9DfK
|
||||
custom: https://legend.lnbits.com/paywall/GAqKguK5S8f6w5VNjS9DfK
|
||||
|
|
12
.github/workflows/formatting.yml
vendored
12
.github/workflows/formatting.yml
vendored
|
@ -15,6 +15,16 @@ jobs:
|
|||
- run: python3 -m venv venv
|
||||
- run: ./venv/bin/pip install black
|
||||
- run: make checkblack
|
||||
isort:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- run: sudo apt-get install python3-venv
|
||||
- run: python3 -m venv venv
|
||||
- run: ./venv/bin/pip install isort
|
||||
- run: make checkisort
|
||||
|
||||
prettier:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -23,4 +33,4 @@ jobs:
|
|||
- run: sudo apt-get install python3-venv
|
||||
- run: python3 -m venv venv
|
||||
- run: npm install prettier
|
||||
- run: ./node_modules/.bin/prettier --check lnbits/static/js/*.js lnbits/core/static/js/*.js lnbits/extensions/*/templates/*/*.html ./lnbits/core/templates/core/*.html lnbits/templates/*.html lnbits/extensions/*/static/js/*.js
|
||||
- run: ./node_modules/.bin/prettier --check lnbits/static/js/*.js lnbits/core/static/js/*.js lnbits/extensions/*/templates/*/*.html ./lnbits/core/templates/core/*.html lnbits/templates/*.html lnbits/extensions/*/static/js/*.js
|
||||
|
|
49
.github/workflows/migrations.yml
vendored
Normal file
49
.github/workflows/migrations.yml
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
name: migrations
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
sqlite-to-postgres:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: postgres
|
||||
ports:
|
||||
# maps tcp port 5432 on service container to the host
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.8]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
env:
|
||||
VIRTUAL_ENV: ./venv
|
||||
PATH: ${{ env.VIRTUAL_ENV }}/bin:${{ env.PATH }}
|
||||
run: |
|
||||
python -m venv ${{ env.VIRTUAL_ENV }}
|
||||
./venv/bin/python -m pip install --upgrade pip
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
./venv/bin/pip install pytest pytest-asyncio pytest-cov requests mock
|
||||
- name: Run migrations
|
||||
run: |
|
||||
rm -rf ./data
|
||||
mkdir -p ./data
|
||||
export LNBITS_DATA_FOLDER="./data"
|
||||
timeout 5s ./venv/bin/uvicorn lnbits.__main__:app --host 0.0.0.0 --port 5001 || code=$?; if [[ $code -ne 124 && $code -ne 0 ]]; then exit $code; fi
|
||||
export LNBITS_DATABASE_URL="postgres://postgres:postgres@0.0.0.0:5432/postgres"
|
||||
timeout 5s ./venv/bin/uvicorn lnbits.__main__:app --host 0.0.0.0 --port 5001 || code=$?; if [[ $code -ne 124 && $code -ne 0 ]]; then exit $code; fi
|
||||
./venv/bin/python tools/conv.py --dont-ignore-missing
|
97
.github/workflows/regtest.yml
vendored
Normal file
97
.github/workflows/regtest.yml
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
name: regtest
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
LndRestWallet:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.8]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Setup Regtest
|
||||
run: |
|
||||
docker build -t lnbits-legend .
|
||||
git clone https://github.com/lnbits/legend-regtest-enviroment.git docker
|
||||
cd docker
|
||||
source docker-scripts.sh
|
||||
lnbits-regtest-start
|
||||
echo "sleeping 60 seconds"
|
||||
sleep 60
|
||||
echo "continue"
|
||||
lnbits-regtest-init
|
||||
bitcoin-cli-sim -generate 1
|
||||
lncli-sim 1 listpeers
|
||||
sudo chmod -R a+rwx .
|
||||
- name: Install dependencies
|
||||
env:
|
||||
VIRTUAL_ENV: ./venv
|
||||
PATH: ${{ env.VIRTUAL_ENV }}/bin:${{ env.PATH }}
|
||||
run: |
|
||||
python -m venv ${{ env.VIRTUAL_ENV }}
|
||||
./venv/bin/python -m pip install --upgrade pip
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
./venv/bin/pip install pylightning
|
||||
./venv/bin/pip install pytest pytest-asyncio pytest-cov requests mock
|
||||
- name: Run tests
|
||||
env:
|
||||
PYTHONUNBUFFERED: 1
|
||||
PORT: 5123
|
||||
LNBITS_DATA_FOLDER: ./data
|
||||
LNBITS_BACKEND_WALLET_CLASS: LndRestWallet
|
||||
LND_REST_ENDPOINT: https://localhost:8081/
|
||||
LND_REST_CERT: docker/data/lnd-1/tls.cert
|
||||
LND_REST_MACAROON: docker/data/lnd-1/data/chain/bitcoin/regtest/admin.macaroon
|
||||
run: |
|
||||
sudo chmod -R a+rwx . && rm -rf ./data && mkdir -p ./data
|
||||
make test-real-wallet
|
||||
CLightningWallet:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.8]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Setup Regtest
|
||||
run: |
|
||||
docker build -t lnbits-legend .
|
||||
git clone https://github.com/lnbits/legend-regtest-enviroment.git docker
|
||||
cd docker
|
||||
source docker-scripts.sh
|
||||
lnbits-regtest-start
|
||||
echo "sleeping 60 seconds"
|
||||
sleep 60
|
||||
echo "continue"
|
||||
lnbits-regtest-init
|
||||
bitcoin-cli-sim -generate 1
|
||||
lncli-sim 1 listpeers
|
||||
sudo chmod -R a+rwx .
|
||||
- name: Install dependencies
|
||||
env:
|
||||
VIRTUAL_ENV: ./venv
|
||||
PATH: ${{ env.VIRTUAL_ENV }}/bin:${{ env.PATH }}
|
||||
run: |
|
||||
python -m venv ${{ env.VIRTUAL_ENV }}
|
||||
./venv/bin/python -m pip install --upgrade pip
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
./venv/bin/pip install pylightning
|
||||
./venv/bin/pip install pytest pytest-asyncio pytest-cov requests mock
|
||||
- name: Run tests
|
||||
env:
|
||||
PYTHONUNBUFFERED: 1
|
||||
PORT: 5123
|
||||
LNBITS_DATA_FOLDER: ./data
|
||||
LNBITS_BACKEND_WALLET_CLASS: CLightningWallet
|
||||
CLIGHTNING_RPC: docker/data/clightning-1/regtest/lightning-rpc
|
||||
run: |
|
||||
sudo chmod -R a+rwx . && rm -rf ./data && mkdir -p ./data
|
||||
make test-real-wallet
|
61
.github/workflows/tests.yml
vendored
61
.github/workflows/tests.yml
vendored
|
@ -3,19 +3,17 @@ name: tests
|
|||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
sqlite:
|
||||
venv-sqlite:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7, 3.8]
|
||||
python-version: [3.7, 3.8, 3.9]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: psycopg2 prerequisites
|
||||
run: sudo apt-get install python-dev libpq-dev
|
||||
- name: Install dependencies
|
||||
env:
|
||||
VIRTUAL_ENV: ./venv
|
||||
|
@ -27,7 +25,7 @@ jobs:
|
|||
./venv/bin/pip install pytest pytest-asyncio pytest-cov requests mock
|
||||
- name: Run tests
|
||||
run: make test
|
||||
postgres:
|
||||
venv-postgres:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
|
@ -46,15 +44,13 @@ jobs:
|
|||
--health-retries 5
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7, 3.8]
|
||||
python-version: [3.8]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: psycopg2 prerequisites
|
||||
run: sudo apt-get install python-dev libpq-dev
|
||||
- name: Install dependencies
|
||||
env:
|
||||
VIRTUAL_ENV: ./venv
|
||||
|
@ -72,34 +68,21 @@ jobs:
|
|||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
file: ./coverage.xml
|
||||
# build:
|
||||
# runs-on: ubuntu-latest
|
||||
# strategy:
|
||||
# matrix:
|
||||
# python-version: [3.7, 3.8]
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Set up Python ${{ matrix.python-version }}
|
||||
# uses: actions/setup-python@v1
|
||||
# with:
|
||||
# python-version: ${{ matrix.python-version }}
|
||||
# - name: Install dependencies
|
||||
# run: |
|
||||
# python -m pip install --upgrade pip
|
||||
# pip install -r requirements.txt
|
||||
# - name: Test with pytest
|
||||
# env:
|
||||
# LNBITS_BACKEND_WALLET_CLASS: LNPayWallet
|
||||
# LNBITS_FORCE_HTTPS: 0
|
||||
# LNPAY_API_ENDPOINT: https://api.lnpay.co/v1/
|
||||
# LNPAY_API_KEY: sak_gG5pSFZhFgOLHm26a8hcWvXKt98yd
|
||||
# LNPAY_ADMIN_KEY: waka_HqWfOoNE0TPqmQHSYErbF4n9
|
||||
# LNPAY_INVOICE_KEY: waki_ZqFEbhrTyopuPlOZButZUw
|
||||
# LNPAY_READ_KEY: wakr_6IyTaNrvSeu3jbojSWt4ou6h
|
||||
# run: |
|
||||
# pip install pytest pytest-cov
|
||||
# pytest --cov=lnbits --cov-report=xml
|
||||
# - name: Upload coverage to Codecov
|
||||
# uses: codecov/codecov-action@v1
|
||||
# with:
|
||||
# file: ./coverage.xml
|
||||
pipenv-sqlite:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.7, 3.8, 3.9]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install pipenv
|
||||
pipenv install --dev
|
||||
pipenv install importlib-metadata
|
||||
- name: Run tests
|
||||
run: make test-pipenv
|
27
Makefile
27
Makefile
|
@ -2,7 +2,7 @@
|
|||
|
||||
all: format check requirements.txt
|
||||
|
||||
format: prettier black
|
||||
format: prettier isort black
|
||||
|
||||
check: mypy checkprettier checkblack
|
||||
|
||||
|
@ -17,12 +17,18 @@ mypy: $(shell find lnbits -name "*.py")
|
|||
./venv/bin/mypy lnbits/core
|
||||
./venv/bin/mypy lnbits/extensions/*
|
||||
|
||||
isort: $(shell find lnbits -name "*.py")
|
||||
./venv/bin/isort --profile black lnbits
|
||||
|
||||
checkprettier: $(shell find lnbits -name "*.js" -name ".html")
|
||||
./node_modules/.bin/prettier --check lnbits/static/js/*.js lnbits/core/static/js/*.js lnbits/extensions/*/templates/*/*.html ./lnbits/core/templates/core/*.html lnbits/templates/*.html lnbits/extensions/*/static/js/*.js
|
||||
|
||||
checkblack: $(shell find lnbits -name "*.py")
|
||||
./venv/bin/black --check lnbits
|
||||
|
||||
checkisort: $(shell find lnbits -name "*.py")
|
||||
./venv/bin/isort --profile black --check-only lnbits
|
||||
|
||||
Pipfile.lock: Pipfile
|
||||
./venv/bin/pipenv lock
|
||||
|
||||
|
@ -32,10 +38,27 @@ requirements.txt: Pipfile.lock
|
|||
test:
|
||||
rm -rf ./tests/data
|
||||
mkdir -p ./tests/data
|
||||
LNBITS_BACKEND_WALLET_CLASS="FakeWallet" \
|
||||
FAKE_WALLET_SECRET="ToTheMoon1" \
|
||||
LNBITS_DATA_FOLDER="./tests/data" \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
./venv/bin/pytest --durations=1 -s --cov=lnbits --cov-report=xml
|
||||
./venv/bin/pytest --durations=1 -s --cov=lnbits --cov-report=xml tests
|
||||
|
||||
test-real-wallet:
|
||||
rm -rf ./tests/data
|
||||
mkdir -p ./tests/data
|
||||
LNBITS_DATA_FOLDER="./tests/data" \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
./venv/bin/pytest --durations=1 -s --cov=lnbits --cov-report=xml tests
|
||||
|
||||
test-pipenv:
|
||||
rm -rf ./tests/data
|
||||
mkdir -p ./tests/data
|
||||
LNBITS_BACKEND_WALLET_CLASS="FakeWallet" \
|
||||
FAKE_WALLET_SECRET="ToTheMoon1" \
|
||||
LNBITS_DATA_FOLDER="./tests/data" \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
pipenv run pytest --durations=1 -s --cov=lnbits --cov-report=xml tests
|
||||
|
||||
bak:
|
||||
# LNBITS_DATABASE_URL=postgres://postgres:postgres@0.0.0.0:5432/postgres
|
||||
|
|
12
Pipfile
12
Pipfile
|
@ -4,7 +4,7 @@ url = "https://pypi.org/simple"
|
|||
verify_ssl = true
|
||||
|
||||
[requires]
|
||||
python_version = "3.7"
|
||||
python_version = "3.8"
|
||||
|
||||
[packages]
|
||||
bitstring = "*"
|
||||
|
@ -28,13 +28,17 @@ asyncio = "*"
|
|||
fastapi = "*"
|
||||
uvicorn = {extras = ["standard"], version = "*"}
|
||||
sse-starlette = "*"
|
||||
jinja2 = "3.0.1"
|
||||
jinja2 = "==3.0.1"
|
||||
pyngrok = "*"
|
||||
secp256k1 = "*"
|
||||
secp256k1 = "==0.14.0"
|
||||
cffi = "==1.15.0"
|
||||
pycryptodomex = "*"
|
||||
|
||||
[dev-packages]
|
||||
black = "==20.8b1"
|
||||
pytest = "*"
|
||||
pytest-cov = "*"
|
||||
mypy = "latest"
|
||||
mypy = "*"
|
||||
pytest-asyncio = "*"
|
||||
requests = "*"
|
||||
mock = "*"
|
||||
|
|
1015
Pipfile.lock
generated
1015
Pipfile.lock
generated
File diff suppressed because it is too large
Load diff
1
Procfile
1
Procfile
|
@ -1 +0,0 @@
|
|||
web: hypercorn -k trio --bind 0.0.0.0:5000 'lnbits.app:create_app()'
|
|
@ -13,7 +13,7 @@ LNbits
|
|||
|
||||
(LNbits is beta, for responsible disclosure of any concerns please contact lnbits@pm.me)
|
||||
|
||||
Use [lnbits.com](https://lnbits.com), or run your own LNbits server!
|
||||
Use [legend.lnbits.com](https://legend.lnbits.com), or run your own LNbits server!
|
||||
|
||||
LNbits is a very simple Python server that sits on top of any funding source, and can be used as:
|
||||
|
||||
|
@ -33,7 +33,7 @@ LNbits is inspired by all the great work of [opennode.com](https://www.opennode.
|
|||
|
||||
## Running LNbits
|
||||
|
||||
See the [install guide](docs/devs/installation.md) for details on installation and setup.
|
||||
See the [install guide](docs/guide/installation.md) for details on installation and setup.
|
||||
|
||||
## LNbits as an account system
|
||||
|
||||
|
@ -67,7 +67,7 @@ Wallets can be easily generated and given out to people at events (one click mul
|
|||
|
||||
## Tip us
|
||||
|
||||
If you like this project and might even use or extend it, why not [send some tip love](https://lnbits.com/paywall/GAqKguK5S8f6w5VNjS9DfK)!
|
||||
If you like this project and might even use or extend it, why not [send some tip love](https://legend.lnbits.com/paywall/GAqKguK5S8f6w5VNjS9DfK)!
|
||||
|
||||
|
||||
[docs]: https://lnbits.org/
|
||||
|
|
7
app.json
7
app.json
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"scripts": {
|
||||
"dokku": {
|
||||
"predeploy": "quart migrate && quart assets"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ cp lnbits/extensions/example lnbits/extensions/mysuperplugin -r # Let's not use
|
|||
cd lnbits/extensions/mysuperplugin
|
||||
find . -type f -print0 | xargs -0 sed -i 's/example/mysuperplugin/g' # Change all occurrences of 'example' to your plugin name 'mysuperplugin'.
|
||||
```
|
||||
- if you are on macOS and having difficulty with 'sed', consider `brew install gnu-sed` and use 'gsed', without -0 option after xargs.
|
||||
|
||||
Going over the example extension's structure:
|
||||
* views_api.py: This is where your public API would go. It will be exposed at "$DOMAIN/$PLUGIN/$ROUTE". For example: https://lnbits.com/mysuperplugin/api/v1/tools.
|
||||
|
|
|
@ -7,46 +7,10 @@ nav_order: 1
|
|||
|
||||
# Installation
|
||||
|
||||
LNbits uses [Pipenv][pipenv] to manage Python packages.
|
||||
This guide has been moved to the [installation guide](../guide/installation.md).
|
||||
To install the developer packages, use `pipenv install --dev`.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/lnbits/lnbits-legend.git
|
||||
cd lnbits-legend/
|
||||
## Notes:
|
||||
|
||||
sudo apt-get install pipenv
|
||||
pipenv shell
|
||||
# pipenv --python 3.9 shell (if you wish to use a version of Python higher than 3.7)
|
||||
pipenv install --dev
|
||||
# pipenv --python 3.9 install --dev (if you wish to use a version of Python higher than 3.7)
|
||||
|
||||
# If any of the modules fails to install, try checking and upgrading your setupTool module
|
||||
# pip install -U setuptools
|
||||
|
||||
# install libffi/libpq in case "pipenv install" fails
|
||||
# sudo apt-get install -y libffi-dev libpq-dev
|
||||
```
|
||||
## Running the server
|
||||
|
||||
Create the data folder and edit the .env file:
|
||||
|
||||
mkdir data
|
||||
cp .env.example .env
|
||||
sudo nano .env
|
||||
|
||||
To then run the server for development purposes (includes hot-reload), use:
|
||||
|
||||
pipenv run python -m uvicorn lnbits.__main__:app --host 0.0.0.0 --reload
|
||||
|
||||
For production, use:
|
||||
|
||||
pipenv run python -m uvicorn lnbits.__main__:app --host 0.0.0.0
|
||||
|
||||
You might also need to install additional packages, depending on the [backend wallet](../guide/wallets.md) you use.
|
||||
E.g. when you want to use LND you have to `pipenv run pip install lndgrpc` and `pipenv run pip install purerpc`.
|
||||
|
||||
Take a look at [Polar][polar] for an excellent way of spinning up a Lightning Network dev environment.
|
||||
|
||||
**Notes**:
|
||||
|
||||
* We reccomend using <a href="https://caddyserver.com/docs/install#debian-ubuntu-raspbian">Caddy</a> for a reverse-proxy if you want to serve your install through a domain, alternatively you can use [ngrok](https://ngrok.com/).
|
||||
* We recommend using <a href="https://caddyserver.com/docs/install#debian-ubuntu-raspbian">Caddy</a> for a reverse-proxy if you want to serve your install through a domain, alternatively you can use [ngrok](https://ngrok.com/).
|
||||
* <a href="https://linuxize.com/post/how-to-use-linux-screen/#starting-linux-screen">Screen</a> works well if you want LNbits to continue running when you close your terminal session.
|
||||
|
|
|
@ -4,8 +4,88 @@ title: Basic installation
|
|||
nav_order: 2
|
||||
---
|
||||
|
||||
|
||||
|
||||
# Basic installation
|
||||
Install Postgres and setup a database for LNbits:
|
||||
|
||||
You can choose between two python package managers, `venv` and `pipenv`. Both are fine but if you don't know what you're doing, just go for the first option.
|
||||
|
||||
By default, LNbits will use SQLite as its database. You can also use PostgreSQL which is recommended for applications with a high load (see guide below).
|
||||
|
||||
## Option 1: pipenv
|
||||
|
||||
You can also use Pipenv to manage your python packages.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/lnbits/lnbits-legend.git
|
||||
cd lnbits-legend/
|
||||
|
||||
sudo apt update && sudo apt install -y pipenv
|
||||
pipenv install --dev
|
||||
# pipenv --python 3.9 install --dev (if you wish to use a version of Python higher than 3.7)
|
||||
pipenv shell
|
||||
# pipenv --python 3.9 shell (if you wish to use a version of Python higher than 3.7)
|
||||
|
||||
# If any of the modules fails to install, try checking and upgrading your setupTool module
|
||||
# pip install -U setuptools wheel
|
||||
|
||||
# install libffi/libpq in case "pipenv install" fails
|
||||
# sudo apt-get install -y libffi-dev libpq-dev
|
||||
|
||||
mkdir data && cp .env.example .env
|
||||
```
|
||||
|
||||
#### Running the server
|
||||
|
||||
```sh
|
||||
pipenv run python -m uvicorn lnbits.__main__:app --port 5000 --host 0.0.0.0
|
||||
```
|
||||
|
||||
Add the flag `--reload` for development (includes hot-reload).
|
||||
|
||||
|
||||
## Option 2: venv
|
||||
|
||||
Download this repo and install the dependencies:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/lnbits/lnbits-legend.git
|
||||
cd lnbits-legend/
|
||||
# ensure you have virtualenv installed, on debian/ubuntu 'apt install python3-venv'
|
||||
python3 -m venv venv
|
||||
# If you have problems here, try `sudo apt install -y pkg-config libpq-dev`
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
# create the data folder and the .env file
|
||||
mkdir data && cp .env.example .env
|
||||
```
|
||||
|
||||
#### Running the server
|
||||
|
||||
```sh
|
||||
./venv/bin/uvicorn lnbits.__main__:app --port 5000
|
||||
```
|
||||
|
||||
If you want to host LNbits on the internet, run with the option `--host 0.0.0.0`.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Problems installing? These commands have helped us install LNbits.
|
||||
|
||||
```sh
|
||||
sudo apt install pkg-config libffi-dev libpq-dev
|
||||
|
||||
# if the secp256k1 build fails:
|
||||
# if you used pipenv (option 1)
|
||||
pipenv install setuptools wheel
|
||||
# if you used venv (option 2)
|
||||
./venv/bin/pip install setuptools wheel
|
||||
# build essentials for debian/ubuntu
|
||||
sudo apt install python3-dev gcc build-essential
|
||||
```
|
||||
|
||||
### Optional: PostgreSQL database
|
||||
|
||||
If you want to use LNbits at scale, we recommend using PostgreSQL as the backend database. Install Postgres and setup a database for LNbits:
|
||||
|
||||
```sh
|
||||
# on debian/ubuntu 'sudo apt-get -y install postgresql'
|
||||
|
@ -22,34 +102,35 @@ createdb lnbits
|
|||
exit
|
||||
```
|
||||
|
||||
Download this repo and install the dependencies:
|
||||
You need to edit the `.env` file.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/lnbits/lnbits-legend.git
|
||||
cd lnbits-legend/
|
||||
# ensure you have virtualenv installed, on debian/ubuntu 'apt install python3-venv' should work
|
||||
python3 -m venv venv
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
cp .env.example .env
|
||||
# add the database connection string to .env 'nano .env' LNBITS_DATABASE_URL=
|
||||
# postgres://<user>:<myPassword>@<host>/<lnbits> - alter line bellow with your user, password and db name
|
||||
LNBITS_DATABASE_URL="postgres://postgres:postgres@localhost/lnbits"
|
||||
# save and exit
|
||||
./venv/bin/uvicorn lnbits.__main__:app --port 5000
|
||||
```
|
||||
|
||||
# Using LNbits
|
||||
|
||||
Now you can visit your LNbits at http://localhost:5000/.
|
||||
|
||||
Now modify the `.env` file with any settings you prefer and add a proper [funding source](./wallets.md) by modifying the value of `LNBITS_BACKEND_WALLET_CLASS` and providing the extra information and credentials related to the chosen funding source.
|
||||
Now modify the `.env` file with any settings you prefer and add a proper [funding source](./wallets.md) by modifying the value of `LNBITS_BACKEND_WALLET_CLASS` and providing the extra information and credentials related to the chosen funding source.
|
||||
|
||||
Then you can restart it and it will be using the new settings.
|
||||
|
||||
You might also need to install additional packages or perform additional setup steps, depending on the chosen backend. See [the short guide](./wallets.md) on each different funding source.
|
||||
You might also need to install additional packages or perform additional setup steps, depending on the chosen backend. See [the short guide](./wallets.md) on each different funding source.
|
||||
|
||||
## Important note
|
||||
If you already have LNbits installed and running, on an SQLite database, we **HIGHLY** recommend you migrate to postgres!
|
||||
Take a look at [Polar](https://lightningpolar.com/) for an excellent way of spinning up a Lightning Network dev environment.
|
||||
|
||||
There's a script included that can do the migration easy. You should have Postgres already installed and there should be a password for the user, check the guide above. Additionally, your lnbits instance should run once on postgres to implement the database schema before the migration works:
|
||||
|
||||
|
||||
# Additional guides
|
||||
|
||||
## SQLite to PostgreSQL migration
|
||||
If you already have LNbits installed and running, on an SQLite database, we **highly** recommend you migrate to postgres if you are planning to run LNbits on scale.
|
||||
|
||||
There's a script included that can do the migration easy. You should have Postgres already installed and there should be a password for the user (see Postgres install guide above). Additionally, your LNbits instance should run once on postgres to implement the database schema before the migration works:
|
||||
|
||||
```sh
|
||||
# STOP LNbits
|
||||
|
@ -61,17 +142,14 @@ LNBITS_DATABASE_URL="postgres://postgres:postgres@localhost/lnbits"
|
|||
|
||||
# START LNbits
|
||||
# STOP LNbits
|
||||
# on the LNBits folder, locate and edit 'conv.py' with the relevant credentials
|
||||
python3 conv.py
|
||||
# on the LNBits folder, locate and edit 'tools/conv.py' with the relevant credentials
|
||||
python3 tools/conv.py
|
||||
```
|
||||
|
||||
Hopefully, everything works and get migrated... Launch LNbits again and check if everything is working properly.
|
||||
|
||||
|
||||
|
||||
# Additional guides
|
||||
|
||||
### LNbits as a systemd service
|
||||
## LNbits as a systemd service
|
||||
|
||||
Systemd is great for taking care of your LNbits instance. It will start it on boot and restart it in case it crashes. If you want to run LNbits as a systemd service on your Debian/Ubuntu/Raspbian server, create a file at `/etc/systemd/system/lnbits.service` with the following content:
|
||||
|
||||
|
@ -110,11 +188,40 @@ sudo systemctl enable lnbits.service
|
|||
sudo systemctl start lnbits.service
|
||||
```
|
||||
|
||||
### LNbits running on Umbrel behind Tor
|
||||
## Using https without reverse proxy
|
||||
The most common way of using LNbits via https is to use a reverse proxy such as Caddy, nginx, or ngriok. However, you can also run LNbits via https without additional software. This is useful for development purposes or if you want to use LNbits in your local network.
|
||||
|
||||
We have to create a self-signed certificate using `mkcert`. Note that this certiciate is not "trusted" by most browsers but that's fine (since you know that you have created it) and encryption is always better than clear text.
|
||||
|
||||
#### Install mkcert
|
||||
You can find the install instructions for `mkcert` [here](https://github.com/FiloSottile/mkcert).
|
||||
|
||||
Install mkcert on Ubuntu:
|
||||
```sh
|
||||
sudo apt install libnss3-tools
|
||||
curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
|
||||
chmod +x mkcert-v*-linux-amd64
|
||||
sudo cp mkcert-v*-linux-amd64 /usr/local/bin/mkcert
|
||||
```
|
||||
#### Create certificate
|
||||
To create a certificate, first `cd` into your lnbits folder and execute the following command ([more info](https://kifarunix.com/how-to-create-self-signed-ssl-certificate-with-mkcert-on-ubuntu-18-04/))
|
||||
```sh
|
||||
# add your local IP (192.x.x.x) as well if you want to use it in your local network
|
||||
mkcert localhost 127.0.0.1 ::1
|
||||
```
|
||||
|
||||
This will create two new files (`localhost-key.pem` and `localhost.pem `) which you can then pass to uvicorn when you start LNbits:
|
||||
|
||||
```sh
|
||||
./venv/bin/uvicorn lnbits.__main__:app --host 0.0.0.0 --port 5000 --ssl-keyfile ./localhost-key.pem --ssl-certfile ./localhost.pem
|
||||
```
|
||||
|
||||
|
||||
## LNbits running on Umbrel behind Tor
|
||||
|
||||
If you want to run LNbits on your Umbrel but want it to be reached through clearnet, _Uxellodunum_ made an extensive [guide](https://community.getumbrel.com/t/guide-lnbits-without-tor/604) on how to do it.
|
||||
|
||||
### Docker installation
|
||||
## Docker installation
|
||||
|
||||
To install using docker you first need to build the docker image as:
|
||||
|
||||
|
@ -146,9 +253,3 @@ docker run --detach --publish 5000:5000 --name lnbits --volume ${PWD}/.env:/app/
|
|||
```
|
||||
|
||||
Finally you can access your lnbits on your machine at port 5000.
|
||||
|
||||
# Additional guides
|
||||
|
||||
## LNbits running on Umbrel behind Tor
|
||||
|
||||
If you want to run LNbits on your Umbrel but want it to be reached through clearnet, _Uxellodunum_ made an extensive [guide](https://community.getumbrel.com/t/guide-lnbits-without-tor/604) on how to do it.
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
import asyncio
|
||||
|
||||
import uvloop
|
||||
from starlette.requests import Request
|
||||
|
||||
from loguru import logger
|
||||
from starlette.requests import Request
|
||||
|
||||
from .commands import migrate_databases
|
||||
from .settings import (
|
||||
DEBUG,
|
||||
HOST,
|
||||
LNBITS_COMMIT,
|
||||
LNBITS_DATA_FOLDER,
|
||||
LNBITS_DATABASE_URL,
|
||||
LNBITS_SITE_TITLE,
|
||||
HOST,
|
||||
PORT,
|
||||
WALLET,
|
||||
LNBITS_DATABASE_URL,
|
||||
LNBITS_DATA_FOLDER,
|
||||
)
|
||||
|
||||
uvloop.install()
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import asyncio
|
||||
import importlib
|
||||
import logging
|
||||
import sys
|
||||
import traceback
|
||||
import warnings
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from http import HTTPStatus
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
|
@ -14,6 +12,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|||
from fastapi.middleware.gzip import GZipMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from loguru import logger
|
||||
|
||||
import lnbits.settings
|
||||
from lnbits.core.tasks import register_task_listeners
|
||||
|
@ -199,8 +198,33 @@ def register_exception_handlers(app: FastAPI):
|
|||
def configure_logger() -> None:
|
||||
logger.remove()
|
||||
log_level: str = "DEBUG" if lnbits.settings.DEBUG else "INFO"
|
||||
if lnbits.settings.DEBUG:
|
||||
fmt: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level: <6}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | <level>{message}</level>"
|
||||
else:
|
||||
fmt: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level}</level> | <level>{message}</level>"
|
||||
logger.add(sys.stderr, level=log_level, format=fmt)
|
||||
formatter = Formatter()
|
||||
logger.add(sys.stderr, level=log_level, format=formatter.format)
|
||||
|
||||
logging.getLogger("uvicorn").handlers = [InterceptHandler()]
|
||||
logging.getLogger("uvicorn.access").handlers = [InterceptHandler()]
|
||||
|
||||
|
||||
class Formatter:
|
||||
def __init__(self):
|
||||
self.padding = 0
|
||||
self.minimal_fmt: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level}</level> | <level>{message}</level>\n"
|
||||
if lnbits.settings.DEBUG:
|
||||
self.fmt: str = "<green>{time:YYYY-MM-DD HH:mm:ss.SS}</green> | <level>{level: <4}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | <level>{message}</level>\n"
|
||||
else:
|
||||
self.fmt: str = self.minimal_fmt
|
||||
|
||||
def format(self, record):
|
||||
function = "{function}".format(**record)
|
||||
if function == "emit": # uvicorn logs
|
||||
return self.minimal_fmt
|
||||
return self.fmt
|
||||
|
||||
|
||||
class InterceptHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
try:
|
||||
level = logger.level(record.levelname).name
|
||||
except ValueError:
|
||||
level = record.levelno
|
||||
logger.log(level, record.getMessage())
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import bitstring # type: ignore
|
||||
import re
|
||||
import hashlib
|
||||
from typing import List, NamedTuple, Optional
|
||||
from bech32 import bech32_encode, bech32_decode, CHARSET
|
||||
from ecdsa import SECP256k1, VerifyingKey # type: ignore
|
||||
from ecdsa.util import sigdecode_string # type: ignore
|
||||
from binascii import unhexlify
|
||||
import re
|
||||
import time
|
||||
from binascii import unhexlify
|
||||
from decimal import Decimal
|
||||
from typing import List, NamedTuple, Optional
|
||||
|
||||
import bitstring # type: ignore
|
||||
import embit
|
||||
import secp256k1
|
||||
from bech32 import CHARSET, bech32_decode, bech32_encode
|
||||
from ecdsa import SECP256k1, VerifyingKey # type: ignore
|
||||
from ecdsa.util import sigdecode_string # type: ignore
|
||||
|
||||
|
||||
class Route(NamedTuple):
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import asyncio
|
||||
import warnings
|
||||
import click
|
||||
import importlib
|
||||
import re
|
||||
import os
|
||||
import re
|
||||
import warnings
|
||||
|
||||
import click
|
||||
from loguru import logger
|
||||
|
||||
from .db import SQLITE, POSTGRES, COCKROACH
|
||||
from .core import db as core_db, migrations as core_migrations
|
||||
from .core import db as core_db
|
||||
from .core import migrations as core_migrations
|
||||
from .db import COCKROACH, POSTGRES, SQLITE
|
||||
from .helpers import (
|
||||
get_valid_extensions,
|
||||
get_css_vendored,
|
||||
get_js_vendored,
|
||||
get_valid_extensions,
|
||||
url_for_vendored,
|
||||
)
|
||||
from .settings import LNBITS_PATH
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import json
|
||||
import datetime
|
||||
from uuid import uuid4
|
||||
from typing import List, Optional, Dict, Any
|
||||
import json
|
||||
from typing import Any, Dict, List, Optional
|
||||
from urllib.parse import urlparse
|
||||
from uuid import uuid4
|
||||
|
||||
from lnbits import bolt11
|
||||
from lnbits.db import Connection, POSTGRES, COCKROACH
|
||||
from lnbits.db import COCKROACH, POSTGRES, Connection
|
||||
from lnbits.settings import DEFAULT_WALLET_NAME, LNBITS_ADMIN_USERS
|
||||
|
||||
from . import db
|
||||
from .models import User, Wallet, Payment, BalanceCheck
|
||||
from .models import BalanceCheck, Payment, User, Wallet
|
||||
|
||||
# accounts
|
||||
# --------
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import json
|
||||
import hmac
|
||||
import hashlib
|
||||
from lnbits.helpers import url_for
|
||||
import hmac
|
||||
import json
|
||||
from sqlite3 import Row
|
||||
from typing import Dict, List, NamedTuple, Optional
|
||||
|
||||
from ecdsa import SECP256k1, SigningKey # type: ignore
|
||||
from lnurl import encode as lnurl_encode # type: ignore
|
||||
from typing import List, NamedTuple, Optional, Dict
|
||||
from sqlite3 import Row
|
||||
from loguru import logger
|
||||
from pydantic import BaseModel
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.helpers import url_for
|
||||
from lnbits.settings import WALLET
|
||||
|
||||
|
||||
|
|
|
@ -3,20 +3,25 @@ import json
|
|||
from binascii import unhexlify
|
||||
from io import BytesIO
|
||||
from typing import Dict, Optional, Tuple
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
import httpx
|
||||
from fastapi import Depends
|
||||
from lnurl import LnurlErrorResponse
|
||||
from lnurl import decode as decode_lnurl # type: ignore
|
||||
from loguru import logger
|
||||
|
||||
from lnbits import bolt11
|
||||
from lnbits.db import Connection
|
||||
from lnbits.decorators import (
|
||||
WalletTypeInfo,
|
||||
get_key_type,
|
||||
require_admin_key,
|
||||
require_invoice_key,
|
||||
)
|
||||
from lnbits.helpers import url_for, urlsafe_short_hash
|
||||
from lnbits.requestvars import g
|
||||
from lnbits.settings import WALLET
|
||||
from lnbits.settings import FAKE_WALLET, WALLET
|
||||
from lnbits.wallets.base import PaymentResponse, PaymentStatus
|
||||
|
||||
from . import db
|
||||
|
@ -51,15 +56,19 @@ async def create_invoice(
|
|||
description_hash: Optional[bytes] = None,
|
||||
extra: Optional[Dict] = None,
|
||||
webhook: Optional[str] = None,
|
||||
internal: Optional[bool] = False,
|
||||
conn: Optional[Connection] = None,
|
||||
) -> Tuple[str, str]:
|
||||
invoice_memo = None if description_hash else memo
|
||||
|
||||
ok, checking_id, payment_request, error_message = await WALLET.create_invoice(
|
||||
# use the fake wallet if the invoice is for internal use only
|
||||
wallet = FAKE_WALLET if internal else WALLET
|
||||
|
||||
ok, checking_id, payment_request, error_message = await wallet.create_invoice(
|
||||
amount=amount, memo=invoice_memo, description_hash=description_hash
|
||||
)
|
||||
if not ok:
|
||||
raise InvoiceFailure(error_message or "Unexpected backend error.")
|
||||
raise InvoiceFailure(error_message or "unexpected backend error.")
|
||||
|
||||
invoice = bolt11.decode(payment_request)
|
||||
|
||||
|
@ -229,7 +238,7 @@ async def redeem_lnurl_withdraw(
|
|||
conn=conn,
|
||||
)
|
||||
except:
|
||||
logger.warn(
|
||||
logger.warning(
|
||||
f"failed to create invoice on redeem_lnurl_withdraw from {lnurl}. params: {res}"
|
||||
)
|
||||
return None
|
||||
|
@ -256,12 +265,14 @@ async def redeem_lnurl_withdraw(
|
|||
|
||||
|
||||
async def perform_lnurlauth(
|
||||
callback: str, conn: Optional[Connection] = None
|
||||
callback: str,
|
||||
wallet: WalletTypeInfo = Depends(require_admin_key),
|
||||
conn: Optional[Connection] = None,
|
||||
) -> Optional[LnurlErrorResponse]:
|
||||
cb = urlparse(callback)
|
||||
|
||||
k1 = unhexlify(parse_qs(cb.query)["k1"][0])
|
||||
key = g().wallet.lnurlauth_key(cb.netloc)
|
||||
key = wallet.wallet.lnurlauth_key(cb.netloc)
|
||||
|
||||
def int_to_bytes_suitable_der(x: int) -> bytes:
|
||||
"""for strict DER we need to encode the integer with some quirks"""
|
||||
|
|
|
@ -1,4 +1,36 @@
|
|||
new Vue({
|
||||
el: '#vue',
|
||||
data: function () {
|
||||
return {
|
||||
searchTerm: '',
|
||||
filteredExtensions: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.filteredExtensions = this.g.extensions
|
||||
},
|
||||
watch: {
|
||||
searchTerm(term) {
|
||||
// Reset the filter
|
||||
this.filteredExtensions = this.g.extensions
|
||||
if (term !== '') {
|
||||
// Filter the extensions list
|
||||
function extensionNameContains(searchTerm) {
|
||||
return function (extension) {
|
||||
return (
|
||||
extension.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
extension.shortDescription
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
this.filteredExtensions = this.filteredExtensions.filter(
|
||||
extensionNameContains(term)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
mixins: [windowMixin]
|
||||
})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import asyncio
|
||||
import httpx
|
||||
from typing import List
|
||||
|
||||
import httpx
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.tasks import register_invoice_listener
|
||||
|
|
|
@ -49,7 +49,8 @@
|
|||
<h5 class="text-caption q-mt-sm q-mb-none">Body (application/json)</h5>
|
||||
<code
|
||||
>{"out": false, "amount": <int>, "memo": <string>, "unit":
|
||||
<string>, "webhook": <url:string>}</code
|
||||
<string>, "webhook": <url:string>, "internal":
|
||||
<bool>}</code
|
||||
>
|
||||
<h5 class="text-caption q-mt-sm q-mb-none">
|
||||
Returns 201 CREATED (application/json)
|
||||
|
|
|
@ -2,10 +2,23 @@
|
|||
%} {% block scripts %} {{ window_vars(user) }}
|
||||
<script src="/core/static/js/extensions.js"></script>
|
||||
{% endblock %} {% block page %}
|
||||
<div class="row q-col-gutter-md q-mb-md">
|
||||
<div class="col-sm-3 col-xs-8 q-ml-auto">
|
||||
<q-input v-model="searchTerm" label="Search extensions">
|
||||
<q-icon
|
||||
v-if="searchTerm !== ''"
|
||||
name="close"
|
||||
@click="searchTerm = ''"
|
||||
class="cursor-pointer q-mt-lg"
|
||||
/>
|
||||
</q-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row q-col-gutter-md">
|
||||
<div
|
||||
class="col-6 col-md-4 col-lg-3"
|
||||
v-for="extension in g.extensions"
|
||||
v-for="extension in filteredExtensions"
|
||||
:key="extension.code"
|
||||
>
|
||||
<q-card>
|
||||
|
|
|
@ -7,30 +7,23 @@ from typing import Dict, List, Optional, Union
|
|||
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
|
||||
|
||||
import httpx
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from fastapi import Header, Query, Request
|
||||
from fastapi import Depends, Header, Query, Request
|
||||
from fastapi.exceptions import HTTPException
|
||||
from fastapi.param_functions import Depends
|
||||
from fastapi.params import Body
|
||||
from loguru import logger
|
||||
from pydantic import BaseModel
|
||||
from pydantic.fields import Field
|
||||
from sse_starlette.sse import EventSourceResponse
|
||||
|
||||
from lnbits import bolt11, lnurl
|
||||
from lnbits.bolt11 import Invoice
|
||||
from lnbits.core.models import Payment, Wallet
|
||||
from lnbits.decorators import (
|
||||
WalletAdminKeyChecker,
|
||||
WalletInvoiceKeyChecker,
|
||||
WalletTypeInfo,
|
||||
get_key_type,
|
||||
require_admin_key,
|
||||
require_invoice_key,
|
||||
)
|
||||
from lnbits.helpers import url_for, urlsafe_short_hash
|
||||
from lnbits.requestvars import g
|
||||
from lnbits.settings import LNBITS_ADMIN_USERS, LNBITS_SITE_TITLE
|
||||
from lnbits.utils.exchange_rates import (
|
||||
currencies,
|
||||
|
@ -149,6 +142,7 @@ class CreateInvoiceData(BaseModel):
|
|||
lnurl_balance_check: Optional[str] = None
|
||||
extra: Optional[dict] = None
|
||||
webhook: Optional[str] = None
|
||||
internal: Optional[bool] = False
|
||||
bolt11: Optional[str] = None
|
||||
|
||||
|
||||
|
@ -175,6 +169,7 @@ async def api_payments_create_invoice(data: CreateInvoiceData, wallet: Wallet):
|
|||
description_hash=description_hash,
|
||||
extra=data.extra,
|
||||
webhook=data.webhook,
|
||||
internal=data.internal,
|
||||
conn=conn,
|
||||
)
|
||||
except InvoiceFailure as e:
|
||||
|
@ -395,7 +390,7 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
|
|||
wallet = None
|
||||
try:
|
||||
if X_Api_Key.extra:
|
||||
logger.warn("No key")
|
||||
logger.warning("No key")
|
||||
except:
|
||||
wallet = await get_wallet_for_key(X_Api_Key)
|
||||
payment = await get_standalone_payment(
|
||||
|
@ -435,10 +430,8 @@ async def api_payment(payment_hash, X_Api_Key: Optional[str] = Header(None)):
|
|||
return {"paid": not payment.pending, "preimage": payment.preimage}
|
||||
|
||||
|
||||
@core_app.get(
|
||||
"/api/v1/lnurlscan/{code}", dependencies=[Depends(WalletInvoiceKeyChecker())]
|
||||
)
|
||||
async def api_lnurlscan(code: str):
|
||||
@core_app.get("/api/v1/lnurlscan/{code}")
|
||||
async def api_lnurlscan(code: str, wallet: WalletTypeInfo = Depends(get_key_type)):
|
||||
try:
|
||||
url = lnurl.decode(code)
|
||||
domain = urlparse(url).netloc
|
||||
|
@ -466,7 +459,7 @@ async def api_lnurlscan(code: str):
|
|||
params.update(kind="auth")
|
||||
params.update(callback=url) # with k1 already in it
|
||||
|
||||
lnurlauth_key = g().wallet.lnurlauth_key(domain)
|
||||
lnurlauth_key = wallet.wallet.lnurlauth_key(domain)
|
||||
params.update(pubkey=lnurlauth_key.verifying_key.to_string("compressed").hex())
|
||||
else:
|
||||
async with httpx.AsyncClient() as client:
|
||||
|
@ -582,14 +575,19 @@ async def api_payments_decode(data: DecodePayment):
|
|||
return {"message": "Failed to decode"}
|
||||
|
||||
|
||||
@core_app.post("/api/v1/lnurlauth", dependencies=[Depends(WalletAdminKeyChecker())])
|
||||
async def api_perform_lnurlauth(callback: str):
|
||||
err = await perform_lnurlauth(callback)
|
||||
class Callback(BaseModel):
|
||||
callback: str = Query(...)
|
||||
|
||||
|
||||
@core_app.post("/api/v1/lnurlauth")
|
||||
async def api_perform_lnurlauth(
|
||||
callback: Callback, wallet: WalletTypeInfo = Depends(require_admin_key)
|
||||
):
|
||||
err = await perform_lnurlauth(callback.callback, wallet=wallet)
|
||||
if err:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.SERVICE_UNAVAILABLE, detail=err.reason
|
||||
)
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
|
|
|
@ -7,11 +7,10 @@ from fastapi.exceptions import HTTPException
|
|||
from fastapi.params import Depends, Query
|
||||
from fastapi.responses import FileResponse, RedirectResponse
|
||||
from fastapi.routing import APIRouter
|
||||
from loguru import logger
|
||||
from pydantic.types import UUID4
|
||||
from starlette.responses import HTMLResponse, JSONResponse
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.core import db
|
||||
from lnbits.core.models import User
|
||||
from lnbits.decorators import check_user_exists
|
||||
|
@ -113,7 +112,7 @@ async def wallet(
|
|||
|
||||
if not user_id:
|
||||
user = await get_user((await create_account()).id)
|
||||
logger.info(f"Created new account for user {user.id}")
|
||||
logger.info(f"Create user {user.id}")
|
||||
else:
|
||||
user = await get_user(user_id)
|
||||
if not user:
|
||||
|
@ -140,7 +139,7 @@ async def wallet(
|
|||
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
|
||||
)
|
||||
|
||||
logger.info(f"Access wallet {wallet_name} of user {user.id}")
|
||||
logger.debug(f"Access wallet {wallet_name}{'of user '+ user.id if user else ''}")
|
||||
wallet = user.get_wallet(wallet_id)
|
||||
if not wallet:
|
||||
return template_renderer().TemplateResponse(
|
||||
|
|
|
@ -4,11 +4,10 @@ from http import HTTPStatus
|
|||
from urllib.parse import urlparse
|
||||
|
||||
from fastapi import HTTPException
|
||||
from loguru import logger
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import HTMLResponse
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from lnbits import bolt11
|
||||
|
||||
from .. import core_app
|
||||
|
|
|
@ -6,7 +6,6 @@ from contextlib import asynccontextmanager
|
|||
from typing import Optional
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy_aio.base import AsyncConnection
|
||||
from sqlalchemy_aio.strategy import ASYNCIO_STRATEGY # type: ignore
|
||||
|
|
|
@ -14,9 +14,9 @@ from lnbits.core.crud import get_user, get_wallet_for_key
|
|||
from lnbits.core.models import User, Wallet
|
||||
from lnbits.requestvars import g
|
||||
from lnbits.settings import (
|
||||
LNBITS_ALLOWED_USERS,
|
||||
LNBITS_ADMIN_USERS,
|
||||
LNBITS_ADMIN_EXTENSIONS,
|
||||
LNBITS_ADMIN_USERS,
|
||||
LNBITS_ALLOWED_USERS,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import httpx
|
||||
import json
|
||||
import os
|
||||
|
||||
import httpx
|
||||
|
||||
fiat_currencies = json.load(
|
||||
open(
|
||||
os.path.join(
|
||||
|
|
|
@ -3,9 +3,8 @@ import math
|
|||
import traceback
|
||||
from http import HTTPStatus
|
||||
|
||||
from starlette.requests import Request
|
||||
|
||||
from loguru import logger
|
||||
from starlette.requests import Request
|
||||
|
||||
from . import bleskomat_ext
|
||||
from .crud import (
|
||||
|
|
|
@ -3,13 +3,12 @@ import time
|
|||
from typing import Dict
|
||||
|
||||
from fastapi.params import Query
|
||||
from loguru import logger
|
||||
from pydantic import BaseModel, validator
|
||||
from starlette.requests import Request
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from lnbits import bolt11
|
||||
from lnbits.core.services import pay_invoice, PaymentFailure
|
||||
from lnbits.core.services import PaymentFailure, pay_invoice
|
||||
|
||||
from . import db
|
||||
from .exchange_rates import exchange_rate_providers, fiat_currencies
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
from http import HTTPStatus
|
||||
|
||||
from fastapi import Depends, Query
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
from loguru import logger
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
from lnbits.core.crud import get_user
|
||||
from lnbits.decorators import WalletTypeInfo, require_admin_key
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode, ParseResult
|
||||
from starlette.requests import Request
|
||||
from fastapi.param_functions import Query
|
||||
from typing import Optional, Dict
|
||||
from lnbits.lnurl import encode as lnurl_encode # type: ignore
|
||||
from lnurl.types import LnurlPayMetadata # type: ignore
|
||||
from pydantic import BaseModel
|
||||
import json
|
||||
from sqlite3 import Row
|
||||
from typing import Dict, Optional
|
||||
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
|
||||
|
||||
from fastapi.param_functions import Query
|
||||
from lnurl.types import LnurlPayMetadata # type: ignore
|
||||
from pydantic import BaseModel
|
||||
from starlette.requests import Request
|
||||
|
||||
from lnbits.lnurl import encode as lnurl_encode # type: ignore
|
||||
|
||||
|
||||
class CreateCopilotData(BaseModel):
|
||||
|
|
|
@ -25,7 +25,7 @@ async def wait_for_paid_invoices():
|
|||
async def on_invoice_paid(payment: Payment) -> None:
|
||||
webhook = None
|
||||
data = None
|
||||
if "copilot" != payment.extra.get("tag"):
|
||||
if payment.extra.get("tag") != "copilot":
|
||||
# not an copilot invoice
|
||||
return
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from sqlite3 import Row
|
||||
from typing import Optional
|
||||
|
||||
from fastapi.param_functions import Query
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class CreateUserData(BaseModel):
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from typing import NamedTuple
|
||||
from sqlite3 import Row
|
||||
from typing import NamedTuple, Optional
|
||||
|
||||
from fastapi.param_functions import Query
|
||||
from pydantic.main import BaseModel
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from pydantic.main import BaseModel
|
||||
|
||||
|
||||
class CreateJukeLinkData(BaseModel):
|
||||
|
|
|
@ -16,7 +16,7 @@ async def wait_for_paid_invoices():
|
|||
|
||||
|
||||
async def on_invoice_paid(payment: Payment) -> None:
|
||||
if "jukebox" != payment.extra.get("tag"):
|
||||
if payment.extra.get("tag") != "jukebox":
|
||||
# not a jukebox invoice
|
||||
return
|
||||
await update_jukebox_payment(payment.payment_hash, paid=True)
|
||||
|
|
|
@ -117,7 +117,7 @@
|
|||
>
|
||||
<q-step
|
||||
:name="1"
|
||||
title="Pick wallet, price"
|
||||
title="1. Pick Wallet and Price"
|
||||
icon="account_balance_wallet"
|
||||
:done="step > 1"
|
||||
>
|
||||
|
@ -170,16 +170,25 @@
|
|||
<br />
|
||||
</q-step>
|
||||
|
||||
<q-step :name="2" title="Add api keys" icon="vpn_key" :done="step > 2">
|
||||
<q-step
|
||||
:name="2"
|
||||
title="2. Add API keys"
|
||||
icon="vpn_key"
|
||||
:done="step > 2"
|
||||
>
|
||||
<img src="/jukebox/static/spotapi.gif" />
|
||||
To use this extension you need a Spotify client ID and client secret.
|
||||
You get these by creating an app in the Spotify developers dashboard
|
||||
<a
|
||||
You get these by creating an app in the Spotify Developer Dashboard
|
||||
<br />
|
||||
<br />
|
||||
<q-btn
|
||||
type="a"
|
||||
target="_blank"
|
||||
style="color: #43a047"
|
||||
color="primary"
|
||||
href="https://developer.spotify.com/dashboard/applications"
|
||||
>here</a
|
||||
>.
|
||||
>Open the Spotify Developer Dashboard</q-btn
|
||||
>
|
||||
|
||||
<q-input
|
||||
filled
|
||||
class="q-pb-md q-pt-md"
|
||||
|
@ -231,28 +240,39 @@
|
|||
<br />
|
||||
</q-step>
|
||||
|
||||
<q-step :name="3" title="Add Redirect URI" icon="link" :done="step > 3">
|
||||
<q-step
|
||||
:name="3"
|
||||
title="3. Add Redirect URI"
|
||||
icon="link"
|
||||
:done="step > 3"
|
||||
>
|
||||
<img src="/jukebox/static/spotapi1.gif" />
|
||||
In the app go to edit-settings, set the redirect URI to this link
|
||||
<p>
|
||||
In the app go to edit-settings, set the redirect URI to this link
|
||||
</p>
|
||||
<q-card
|
||||
class="cursor-pointer word-break"
|
||||
@click="copyText(locationcb + jukeboxDialog.data.sp_id, 'Link copied to clipboard!')"
|
||||
>
|
||||
<q-card-section style="word-break: break-all">
|
||||
{% raw %}{{ locationcb }}{{ jukeboxDialog.data.sp_id }}{% endraw
|
||||
%}
|
||||
</q-card-section>
|
||||
<q-tooltip> Click to copy URL </q-tooltip>
|
||||
</q-card>
|
||||
<br />
|
||||
<q-btn
|
||||
dense
|
||||
outline
|
||||
unelevated
|
||||
color="primary"
|
||||
size="xs"
|
||||
@click="copyText(locationcb + jukeboxDialog.data.sp_id, 'Link copied to clipboard!')"
|
||||
>{% raw %}{{ locationcb }}{{ jukeboxDialog.data.sp_id }}{% endraw
|
||||
%}<q-tooltip> Click to copy URL </q-tooltip>
|
||||
</q-btn>
|
||||
<br />
|
||||
Settings can be found
|
||||
<a
|
||||
type="a"
|
||||
target="_blank"
|
||||
style="color: #43a047"
|
||||
color="primary"
|
||||
href="https://developer.spotify.com/dashboard/applications"
|
||||
>here</a
|
||||
>.
|
||||
>Open the Spotify Application Settings</q-btn
|
||||
>
|
||||
<br /><br />
|
||||
<p>
|
||||
After adding the redirect URI, click the "Authorise access" button
|
||||
below.
|
||||
</p>
|
||||
|
||||
<div class="row q-mt-md">
|
||||
<div class="col-4">
|
||||
|
@ -281,7 +301,7 @@
|
|||
|
||||
<q-step
|
||||
:name="4"
|
||||
title="Select playlists"
|
||||
title="4. Select Device and Playlists"
|
||||
icon="queue_music"
|
||||
active-color="primary"
|
||||
:done="step > 4"
|
||||
|
|
|
@ -22,7 +22,7 @@ async def wait_for_paid_invoices():
|
|||
|
||||
|
||||
async def on_invoice_paid(payment: Payment) -> None:
|
||||
if "livestream" != payment.extra.get("tag"):
|
||||
if payment.extra.get("tag") != "livestream":
|
||||
# not a livestream invoice
|
||||
return
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
from http import HTTPStatus
|
||||
|
||||
# from mmap import MAP_DENYWRITE
|
||||
|
||||
from fastapi.param_functions import Depends
|
||||
from fastapi.params import Query
|
||||
from starlette.exceptions import HTTPException
|
||||
|
@ -15,6 +13,8 @@ from lnbits.decorators import check_user_exists
|
|||
from . import livestream_ext, livestream_renderer
|
||||
from .crud import get_livestream_by_track, get_track
|
||||
|
||||
# from mmap import MAP_DENYWRITE
|
||||
|
||||
|
||||
@livestream_ext.get("/", response_class=HTMLResponse)
|
||||
async def index(request: Request, user: User = Depends(check_user_exists)):
|
||||
|
|
|
@ -3,15 +3,13 @@ import json
|
|||
from datetime import datetime, timedelta
|
||||
|
||||
import httpx
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from fastapi.params import Query
|
||||
from lnurl import ( # type: ignore
|
||||
LnurlErrorResponse,
|
||||
LnurlPayActionResponse,
|
||||
LnurlPayResponse,
|
||||
)
|
||||
from loguru import logger
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import HTMLResponse
|
||||
|
||||
|
|
|
@ -43,13 +43,13 @@ async def call_webhook_on_paid(payment_hash):
|
|||
|
||||
|
||||
async def on_invoice_paid(payment: Payment) -> None:
|
||||
if "lnaddress" == payment.extra.get("tag"):
|
||||
if payment.extra.get("tag") == "lnaddress":
|
||||
|
||||
await payment.set_pending(False)
|
||||
await set_address_paid(payment_hash=payment.payment_hash)
|
||||
await call_webhook_on_paid(payment_hash=payment.payment_hash)
|
||||
|
||||
elif "renew lnaddress" == payment.extra.get("tag"):
|
||||
elif payment.extra.get("tag") == "renew lnaddress":
|
||||
|
||||
await payment.set_pending(False)
|
||||
await set_address_renewed(
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
from base64 import b64decode
|
||||
from fastapi.param_functions import Security
|
||||
|
||||
from fastapi.security.api_key import APIKeyHeader
|
||||
|
||||
from fastapi import Request, status
|
||||
from fastapi.param_functions import Security
|
||||
from fastapi.security.api_key import APIKeyHeader
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
from lnbits.decorators import WalletTypeInfo, get_key_type # type: ignore
|
||||
|
||||
|
||||
api_key_header_auth = APIKeyHeader(
|
||||
name="AUTHORIZATION",
|
||||
auto_error=False,
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from lnbits.decorators import check_user_exists
|
||||
from . import lndhub_ext, lndhub_renderer
|
||||
from fastapi import Request
|
||||
from fastapi.params import Depends
|
||||
|
||||
from lnbits.core.models import User
|
||||
from lnbits.decorators import check_user_exists
|
||||
|
||||
from . import lndhub_ext, lndhub_renderer
|
||||
|
||||
|
||||
@lndhub_ext.get("/")
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import time
|
||||
import asyncio
|
||||
|
||||
import time
|
||||
from base64 import urlsafe_b64encode
|
||||
from http import HTTPStatus
|
||||
|
||||
|
@ -13,7 +12,7 @@ from lnbits import bolt11
|
|||
from lnbits.core.crud import delete_expired_invoices, get_payments
|
||||
from lnbits.core.services import create_invoice, pay_invoice
|
||||
from lnbits.decorators import WalletTypeInfo
|
||||
from lnbits.settings import WALLET, LNBITS_SITE_TITLE
|
||||
from lnbits.settings import LNBITS_SITE_TITLE, WALLET
|
||||
|
||||
from . import lndhub_ext
|
||||
from .decorators import check_wallet, require_admin_key
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from lnbits.core.models import Wallet
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import httpx
|
||||
|
||||
from lnbits.core.models import Wallet
|
||||
from lnbits.helpers import urlsafe_short_hash
|
||||
|
||||
from . import db
|
||||
from .models import CreateFormData, CreateTicketData, Tickets, Forms
|
||||
import httpx
|
||||
from .models import CreateFormData, CreateTicketData, Forms, Tickets
|
||||
|
||||
|
||||
async def create_ticket(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi.param_functions import Query
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ async def wait_for_paid_invoices():
|
|||
|
||||
|
||||
async def on_invoice_paid(payment: Payment) -> None:
|
||||
if "lnticket" != payment.extra.get("tag"):
|
||||
if payment.extra.get("tag") != "lnticket":
|
||||
# not a lnticket invoice
|
||||
return
|
||||
|
||||
|
|
|
@ -1,30 +1,26 @@
|
|||
import base64
|
||||
import hashlib
|
||||
import hmac
|
||||
from http import HTTPStatus
|
||||
from io import BytesIO
|
||||
from typing import Optional
|
||||
|
||||
from embit import bech32
|
||||
from embit import compact
|
||||
import base64
|
||||
from io import BytesIO
|
||||
import hmac
|
||||
|
||||
from embit import bech32, compact
|
||||
from fastapi import Request
|
||||
from fastapi.param_functions import Query
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
from lnbits.core.services import create_invoice
|
||||
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
|
||||
from lnbits.core.views.api import pay_invoice
|
||||
|
||||
from lnbits.utils.exchange_rates import fiat_amount_as_satoshis
|
||||
|
||||
from . import lnurldevice_ext
|
||||
from .crud import (
|
||||
create_lnurldevicepayment,
|
||||
get_lnurldevice,
|
||||
get_lnurldevicepayment,
|
||||
update_lnurldevicepayment,
|
||||
get_lnurlpayload,
|
||||
update_lnurldevicepayment,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ async def m001_initial(db):
|
|||
|
||||
async def m002_redux(db):
|
||||
"""
|
||||
Moves everything from lnurlpos to lnurldevices
|
||||
Moves everything from lnurlpos to lnurldevice
|
||||
"""
|
||||
try:
|
||||
for row in [
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
<q-card-section>
|
||||
<code
|
||||
><span class="text-blue">GET</span>
|
||||
/lnurldevice/api/v1/lnurlposs</code
|
||||
/lnurldevice/api/v1/lnurlpos</code
|
||||
>
|
||||
<h5 class="text-caption q-mt-sm q-mb-none">Headers</h5>
|
||||
<code>{"X-Api-Key": <invoice_key>}</code><br />
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
from typing import List, Optional, Union
|
||||
|
||||
from lnbits.db import SQLITE
|
||||
|
||||
from . import db
|
||||
from .models import PayLink, CreatePayLinkData
|
||||
from .models import CreatePayLinkData, PayLink
|
||||
|
||||
|
||||
async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import json
|
||||
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode, ParseResult
|
||||
from starlette.requests import Request
|
||||
from fastapi.param_functions import Query
|
||||
from typing import Optional, Dict
|
||||
from lnbits.lnurl import encode as lnurl_encode # type: ignore
|
||||
from lnurl.types import LnurlPayMetadata # type: ignore
|
||||
from sqlite3 import Row
|
||||
from typing import Dict, Optional
|
||||
from urllib.parse import ParseResult, parse_qs, urlencode, urlparse, urlunparse
|
||||
|
||||
from fastapi.param_functions import Query
|
||||
from lnurl.types import LnurlPayMetadata # type: ignore
|
||||
from pydantic import BaseModel
|
||||
from starlette.requests import Request
|
||||
|
||||
from lnbits.lnurl import encode as lnurl_encode # type: ignore
|
||||
|
||||
|
||||
class CreatePayLinkData(BaseModel):
|
||||
|
|
|
@ -35,6 +35,7 @@ new Vue({
|
|||
rowsPerPage: 10
|
||||
}
|
||||
},
|
||||
nfcTagWriting: false,
|
||||
formDialog: {
|
||||
show: false,
|
||||
fixedAmount: true,
|
||||
|
@ -205,6 +206,42 @@ new Vue({
|
|||
.catch(err => {
|
||||
LNbits.utils.notifyApiError(err)
|
||||
})
|
||||
},
|
||||
writeNfcTag: async function (lnurl) {
|
||||
try {
|
||||
if (typeof NDEFReader == 'undefined') {
|
||||
throw {
|
||||
toString: function () {
|
||||
return 'NFC not supported on this device or browser.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ndef = new NDEFReader()
|
||||
|
||||
this.nfcTagWriting = true
|
||||
this.$q.notify({
|
||||
message: 'Tap your NFC tag to write the LNURL-pay link to it.'
|
||||
})
|
||||
|
||||
await ndef.write({
|
||||
records: [{recordType: 'url', data: 'lightning:' + lnurl, lang: 'en'}]
|
||||
})
|
||||
|
||||
this.nfcTagWriting = false
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'NFC tag written successfully.'
|
||||
})
|
||||
} catch (error) {
|
||||
this.nfcTagWriting = false
|
||||
this.$q.notify({
|
||||
type: 'negative',
|
||||
message: error
|
||||
? error.toString()
|
||||
: 'An unexpected error has occurred.'
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import asyncio
|
||||
import json
|
||||
|
||||
import httpx
|
||||
|
||||
from lnbits.core import db as core_db
|
||||
|
@ -19,7 +20,7 @@ async def wait_for_paid_invoices():
|
|||
|
||||
|
||||
async def on_invoice_paid(payment: Payment) -> None:
|
||||
if "lnurlp" != payment.extra.get("tag"):
|
||||
if payment.extra.get("tag") != "lnurlp":
|
||||
# not an lnurlp invoice
|
||||
return
|
||||
|
||||
|
|
|
@ -14,10 +14,17 @@
|
|||
</q-responsive>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row q-mt-lg">
|
||||
<div class="row q-mt-lg q-gutter-sm">
|
||||
<q-btn outline color="grey" @click="copyText('{{ lnurl }}')"
|
||||
>Copy LNURL</q-btn
|
||||
>
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
icon="nfc"
|
||||
@click="writeNfcTag(' {{ lnurl }} ')"
|
||||
:disable="nfcTagWriting"
|
||||
></q-btn>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
|
|
@ -99,7 +99,8 @@
|
|||
@click="openUpdateDialog(props.row.id)"
|
||||
icon="edit"
|
||||
color="light-blue"
|
||||
></q-btn>
|
||||
>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
|
@ -153,7 +154,8 @@
|
|||
v-model.trim="formDialog.data.description"
|
||||
type="text"
|
||||
label="Item description *"
|
||||
></q-input>
|
||||
>
|
||||
</q-input>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<q-input
|
||||
filled
|
||||
|
@ -171,7 +173,8 @@
|
|||
type="number"
|
||||
:step="formDialog.data.currency && formDialog.data.currency !== 'satoshis' ? '0.01' : '1'"
|
||||
label="Max *"
|
||||
></q-input>
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col">
|
||||
|
@ -200,7 +203,8 @@
|
|||
type="number"
|
||||
label="Comment maximum characters"
|
||||
hint="Tell wallets to prompt users for a comment that will be sent along with the payment. LNURLp will store the comment and send it in the webhook."
|
||||
></q-input>
|
||||
>
|
||||
</q-input>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
|
@ -224,7 +228,8 @@
|
|||
type="text"
|
||||
label="Success URL (optional)"
|
||||
hint="Will be shown as a clickable link to the user in his wallet after a successful payment, appended by the payment_hash as a query string."
|
||||
></q-input>
|
||||
>
|
||||
</q-input>
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
v-if="formDialog.data.id"
|
||||
|
@ -294,6 +299,14 @@
|
|||
@click="copyText(qrCodeDialog.data.pay_url, 'Link copied to clipboard!')"
|
||||
>Shareable link</q-btn
|
||||
>
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
icon="nfc"
|
||||
@click="writeNfcTag(qrCodeDialog.data.lnurl)"
|
||||
:disable="nfcTagWriting"
|
||||
>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
|
|
|
@ -73,6 +73,7 @@ async def api_link_retrieve(
|
|||
@lnurlp_ext.put("/api/v1/links/{link_id}", status_code=HTTPStatus.OK)
|
||||
async def api_link_create_or_update(
|
||||
data: CreatePayLinkData,
|
||||
request: Request,
|
||||
link_id=None,
|
||||
wallet: WalletTypeInfo = Depends(get_key_type),
|
||||
):
|
||||
|
@ -117,7 +118,7 @@ async def api_link_create_or_update(
|
|||
link = await update_pay_link(**data.dict(), link_id=link_id)
|
||||
else:
|
||||
link = await create_pay_link(data, wallet_id=wallet.wallet.id)
|
||||
return {**link.dict(), "lnurl": link.lnurl}
|
||||
return {**link.dict(), "lnurl": link.lnurl(request)}
|
||||
|
||||
|
||||
@lnurlp_ext.delete("/api/v1/links/{link_id}")
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import asyncio
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from lnbits.db import Database
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import List, Optional, Union
|
|||
from lnbits.helpers import urlsafe_short_hash
|
||||
|
||||
from . import db
|
||||
from .models import lnurlpayout, CreateLnurlPayoutData
|
||||
from .models import CreateLnurlPayoutData, lnurlpayout
|
||||
|
||||
|
||||
async def create_lnurlpayout(
|
||||
|
|
|
@ -2,9 +2,7 @@ import asyncio
|
|||
from http import HTTPStatus
|
||||
|
||||
import httpx
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
from lnbits.core import db as core_db
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from lnbits.db import SQLITE
|
||||
|
||||
from . import db
|
||||
from .models import Item, Shop
|
||||
from .wordlists import animals
|
||||
from .models import Shop, Item
|
||||
|
||||
|
||||
async def create_shop(*, wallet_id: str) -> int:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import base64
|
||||
import struct
|
||||
import hmac
|
||||
import struct
|
||||
import time
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import json
|
||||
import base64
|
||||
import hashlib
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from typing import Optional, List, Dict
|
||||
from lnurl import encode as lnurl_encode # type: ignore
|
||||
from lnurl.types import LnurlPayMetadata # type: ignore
|
||||
from lnurl.models import LnurlPaySuccessAction, UrlAction # type: ignore
|
||||
from lnurl.types import LnurlPayMetadata # type: ignore
|
||||
from pydantic import BaseModel
|
||||
from starlette.requests import Request
|
||||
|
||||
from .helpers import totp
|
||||
|
||||
shop_counters: Dict = {}
|
||||
|
|
|
@ -3,18 +3,18 @@ from datetime import datetime
|
|||
from http import HTTPStatus
|
||||
from typing import List
|
||||
|
||||
from fastapi import HTTPException, Request
|
||||
from fastapi.params import Depends, Query
|
||||
from starlette.responses import HTMLResponse
|
||||
|
||||
from lnbits.decorators import check_user_exists
|
||||
from lnbits.core.models import Payment, User
|
||||
from lnbits.core.crud import get_standalone_payment
|
||||
from lnbits.core.models import Payment, User
|
||||
from lnbits.core.views.api import api_payment
|
||||
from lnbits.decorators import check_user_exists
|
||||
|
||||
from . import offlineshop_ext, offlineshop_renderer
|
||||
from .models import Item
|
||||
from .crud import get_item, get_shop
|
||||
from fastapi import Request, HTTPException
|
||||
from .models import Item
|
||||
|
||||
|
||||
@offlineshop_ext.get("/", response_class=HTMLResponse)
|
||||
|
|
|
@ -19,7 +19,7 @@ async def wait_for_paid_invoices():
|
|||
|
||||
|
||||
async def on_invoice_paid(payment: Payment) -> None:
|
||||
if "charge" != payment.extra.get("tag"):
|
||||
if payment.extra.get("tag") != "charge":
|
||||
# not a charge invoice
|
||||
return
|
||||
|
||||
|
|
|
@ -14,3 +14,41 @@ async def m001_initial(db):
|
|||
);
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
async def m002_float_percent(db):
|
||||
"""
|
||||
Add float percent and migrates the existing data.
|
||||
"""
|
||||
await db.execute("ALTER TABLE splitpayments.targets RENAME TO splitpayments_old")
|
||||
await db.execute(
|
||||
"""
|
||||
CREATE TABLE splitpayments.targets (
|
||||
wallet TEXT NOT NULL,
|
||||
source TEXT NOT NULL,
|
||||
percent REAL NOT NULL CHECK (percent >= 0 AND percent <= 100),
|
||||
alias TEXT,
|
||||
|
||||
UNIQUE (source, wallet)
|
||||
);
|
||||
"""
|
||||
)
|
||||
|
||||
for row in [
|
||||
list(row)
|
||||
for row in await db.fetchall("SELECT * FROM splitpayments.splitpayments_old")
|
||||
]:
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO splitpayments.targets (
|
||||
wallet,
|
||||
source,
|
||||
percent,
|
||||
alias
|
||||
)
|
||||
VALUES (?, ?, ?, ?)
|
||||
""",
|
||||
(row[0], row[1], row[2], row[3]),
|
||||
)
|
||||
|
||||
await db.execute("DROP TABLE splitpayments.splitpayments_old")
|
||||
|
|
|
@ -7,14 +7,14 @@ from pydantic import BaseModel
|
|||
class Target(BaseModel):
|
||||
wallet: str
|
||||
source: str
|
||||
percent: int
|
||||
percent: float
|
||||
alias: Optional[str]
|
||||
|
||||
|
||||
class TargetPutList(BaseModel):
|
||||
wallet: str = Query(...)
|
||||
alias: str = Query("")
|
||||
percent: int = Query(..., ge=1)
|
||||
percent: float = Query(..., ge=0.01)
|
||||
|
||||
|
||||
class TargetPut(BaseModel):
|
||||
|
|
|
@ -105,7 +105,7 @@ new Vue({
|
|||
if (currentTotal > 100 && isPercent) {
|
||||
let diff = (currentTotal - 100) / (100 - this.targets[index].percent)
|
||||
this.targets.forEach((target, t) => {
|
||||
if (t !== index) target.percent -= Math.round(diff * target.percent)
|
||||
if (t !== index) target.percent -= +(diff * target.percent).toFixed(2)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ async def wait_for_paid_invoices():
|
|||
|
||||
|
||||
async def on_invoice_paid(payment: Payment) -> None:
|
||||
if "splitpayments" == payment.extra.get("tag") or payment.extra.get("splitted"):
|
||||
if payment.extra.get("tag") == "splitpayments" or payment.extra.get("splitted"):
|
||||
# already splitted, ignore
|
||||
return
|
||||
|
||||
|
|
|
@ -58,14 +58,14 @@
|
|||
></q-input>
|
||||
</div>
|
||||
|
||||
<q-row class="row justify-evenly q-pa-lg">
|
||||
<q-col>
|
||||
<div class="row justify-evenly q-pa-lg">
|
||||
<div>
|
||||
<q-btn unelevated outline color="secondary" @click="clearTargets">
|
||||
Clear
|
||||
</q-btn>
|
||||
</q-col>
|
||||
</div>
|
||||
|
||||
<q-col>
|
||||
<div>
|
||||
<q-btn
|
||||
unelevated
|
||||
color="primary"
|
||||
|
@ -74,8 +74,8 @@
|
|||
>
|
||||
Save Targets
|
||||
</q-btn>
|
||||
</q-col>
|
||||
</q-row>
|
||||
</div>
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import json
|
||||
|
||||
import httpx
|
||||
|
||||
from lnbits.extensions.subdomains.models import Domains
|
||||
import httpx, json
|
||||
|
||||
|
||||
async def cloudflare_create_subdomain(
|
||||
|
|
|
@ -19,7 +19,7 @@ async def wait_for_paid_invoices():
|
|||
|
||||
|
||||
async def on_invoice_paid(payment: Payment) -> None:
|
||||
if "lnsubdomain" != payment.extra.get("tag"):
|
||||
if payment.extra.get("tag") != "lnsubdomain":
|
||||
# not an lnurlp invoice
|
||||
return
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@ def tpos_renderer():
|
|||
|
||||
|
||||
from .tasks import wait_for_paid_invoices
|
||||
from .views_api import * # noqa
|
||||
from .views import * # noqa
|
||||
from .views_api import * # noqa
|
||||
|
||||
|
||||
def tpos_start():
|
||||
|
|
|
@ -20,7 +20,7 @@ async def wait_for_paid_invoices():
|
|||
|
||||
|
||||
async def on_invoice_paid(payment: Payment) -> None:
|
||||
if "tpos" == payment.extra.get("tag") and payment.extra.get("tipSplitted"):
|
||||
if payment.extra.get("tag") == "tpos" and payment.extra.get("tipSplitted"):
|
||||
# already splitted, ignore
|
||||
return
|
||||
|
||||
|
|
|
@ -8,10 +8,7 @@ from starlette.responses import HTMLResponse
|
|||
|
||||
from lnbits.core.models import User
|
||||
from lnbits.decorators import check_user_exists
|
||||
from lnbits.settings import (
|
||||
LNBITS_CUSTOM_LOGO,
|
||||
LNBITS_SITE_TITLE,
|
||||
)
|
||||
from lnbits.settings import LNBITS_CUSTOM_LOGO, LNBITS_SITE_TITLE
|
||||
|
||||
from . import tpos_ext, tpos_renderer
|
||||
from .crud import get_tpos
|
||||
|
|
|
@ -2,9 +2,8 @@ from http import HTTPStatus
|
|||
|
||||
from fastapi import Query
|
||||
from fastapi.params import Depends
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
from loguru import logger
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
from lnbits.core.crud import get_user
|
||||
from lnbits.core.services import create_invoice
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from sqlite3 import Row
|
||||
from typing import Optional
|
||||
|
||||
from fastapi.param_functions import Query
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class CreateUserData(BaseModel):
|
||||
|
|
|
@ -75,7 +75,7 @@ async def api_usermanager_activate_extension(
|
|||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND, detail="User does not exist."
|
||||
)
|
||||
update_user_extension(user_id=userid, extension=extension, active=active)
|
||||
await update_user_extension(user_id=userid, extension=extension, active=active)
|
||||
return {"extension": "updated"}
|
||||
|
||||
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import json
|
||||
import traceback
|
||||
import httpx
|
||||
|
||||
from datetime import datetime
|
||||
from http import HTTPStatus
|
||||
|
||||
from loguru import logger
|
||||
|
||||
import httpx
|
||||
import shortuuid # type: ignore
|
||||
from fastapi import HTTPException
|
||||
from fastapi.param_functions import Query
|
||||
from loguru import logger
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import HTMLResponse # type: ignore
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ new Vue({
|
|||
rowsPerPage: 10
|
||||
}
|
||||
},
|
||||
nfcTagWriting: false,
|
||||
formDialog: {
|
||||
show: false,
|
||||
secondMultiplier: 'seconds',
|
||||
|
@ -231,6 +232,42 @@ new Vue({
|
|||
})
|
||||
})
|
||||
},
|
||||
writeNfcTag: async function (lnurl) {
|
||||
try {
|
||||
if (typeof NDEFReader == 'undefined') {
|
||||
throw {
|
||||
toString: function () {
|
||||
return 'NFC not supported on this device or browser.'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ndef = new NDEFReader()
|
||||
|
||||
this.nfcTagWriting = true
|
||||
this.$q.notify({
|
||||
message: 'Tap your NFC tag to write the LNURL-withdraw link to it.'
|
||||
})
|
||||
|
||||
await ndef.write({
|
||||
records: [{recordType: 'url', data: 'lightning:' + lnurl, lang: 'en'}]
|
||||
})
|
||||
|
||||
this.nfcTagWriting = false
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'NFC tag written successfully.'
|
||||
})
|
||||
} catch (error) {
|
||||
this.nfcTagWriting = false
|
||||
this.$q.notify({
|
||||
type: 'negative',
|
||||
message: error
|
||||
? error.toString()
|
||||
: 'An unexpected error has occurred.'
|
||||
})
|
||||
}
|
||||
},
|
||||
exportCSV: function () {
|
||||
LNbits.utils.exportCSV(this.paywallsTable.columns, this.paywalls)
|
||||
}
|
||||
|
|
|
@ -13,14 +13,22 @@
|
|||
:value="this.here + '/?lightning={{lnurl }}'"
|
||||
:options="{width: 800}"
|
||||
class="rounded-borders"
|
||||
></qrcode>
|
||||
>
|
||||
</qrcode>
|
||||
</q-responsive>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row q-mt-lg">
|
||||
<div class="row q-mt-lg q-gutter-sm">
|
||||
<q-btn outline color="grey" @click="copyText('{{ lnurl }}')"
|
||||
>Copy LNURL</q-btn
|
||||
>
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
icon="nfc"
|
||||
@click="writeNfcTag(' {{ lnurl }} ')"
|
||||
:disable="nfcTagWriting"
|
||||
></q-btn>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
@ -51,7 +59,8 @@
|
|||
mixins: [windowMixin],
|
||||
data: function () {
|
||||
return {
|
||||
here: location.protocol + '//' + location.host
|
||||
here: location.protocol + '//' + location.host,
|
||||
nfcTagWriting: false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -369,6 +369,13 @@
|
|||
@click="copyText(qrCodeDialog.data.withdraw_url, 'Link copied to clipboard!')"
|
||||
>Shareable link</q-btn
|
||||
>
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
icon="nfc"
|
||||
@click="writeNfcTag(qrCodeDialog.data.lnurl)"
|
||||
:disable="nfcTagWriting"
|
||||
></q-btn>
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from typing import Optional, List, Callable
|
||||
from functools import partial
|
||||
from urllib.request import parse_http_list as _parse_list_header
|
||||
from typing import Callable, List, Optional
|
||||
from urllib.parse import urlparse
|
||||
from werkzeug.datastructures import Headers
|
||||
from urllib.request import parse_http_list as _parse_list_header
|
||||
|
||||
from quart import Request
|
||||
from quart_trio.asgi import TrioASGIHTTPConnection
|
||||
from werkzeug.datastructures import Headers
|
||||
|
||||
|
||||
class ASGIProxyFix(TrioASGIHTTPConnection):
|
||||
|
|
|
@ -51,6 +51,7 @@ LNBITS_THEME_OPTIONS: List[str] = env.list(
|
|||
LNBITS_CUSTOM_LOGO = env.str("LNBITS_CUSTOM_LOGO", default="")
|
||||
|
||||
WALLET = wallet_class()
|
||||
FAKE_WALLET = getattr(wallets_module, "FakeWallet")()
|
||||
DEFAULT_WALLET_NAME = env.str("LNBITS_DEFAULT_WALLET_NAME", default="LNbits wallet")
|
||||
PREFER_SECURE_URLS = env.bool("LNBITS_FORCE_HTTPS", default=True)
|
||||
|
||||
|
|
|
@ -392,7 +392,7 @@ window.windowMixin = {
|
|||
}
|
||||
if (window.extensions) {
|
||||
var user = this.g.user
|
||||
this.g.extensions = Object.freeze(
|
||||
const extensions = Object.freeze(
|
||||
window.extensions
|
||||
.map(function (data) {
|
||||
return window.LNbits.map.extension(data)
|
||||
|
@ -413,9 +413,13 @@ window.windowMixin = {
|
|||
return obj
|
||||
})
|
||||
.sort(function (a, b) {
|
||||
return a.name > b.name
|
||||
const nameA = a.name.toUpperCase()
|
||||
const nameB = b.name.toUpperCase()
|
||||
return nameA < nameB ? -1 : nameA > nameB ? 1 : 0
|
||||
})
|
||||
)
|
||||
|
||||
this.g.extensions = extensions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
import time
|
||||
import asyncio
|
||||
import time
|
||||
import traceback
|
||||
from http import HTTPStatus
|
||||
from typing import List, Callable
|
||||
|
||||
from loguru import logger
|
||||
from typing import Callable, List
|
||||
|
||||
from fastapi.exceptions import HTTPException
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.settings import WALLET
|
||||
from lnbits.core.crud import (
|
||||
get_payments,
|
||||
get_standalone_payment,
|
||||
delete_expired_invoices,
|
||||
get_balance_checks,
|
||||
get_payments,
|
||||
get_standalone_payment,
|
||||
)
|
||||
from lnbits.core.services import redeem_lnurl_withdraw
|
||||
|
||||
from lnbits.settings import WALLET
|
||||
|
||||
deferred_async: List[Callable] = []
|
||||
|
||||
|
|
|
@ -228,7 +228,6 @@
|
|||
<script type="text/javascript">
|
||||
const themes = {{ LNBITS_THEME_OPTIONS | tojson }}
|
||||
const LNBITS_DENOMINATION = {{ LNBITS_DENOMINATION | tojson}}
|
||||
console.log(LNBITS_DENOMINATION)
|
||||
if(themes && themes.length) {
|
||||
window.allowedThemes = themes.map(str => str.trim())
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import asyncio
|
||||
from typing import Callable, NamedTuple
|
||||
|
||||
from loguru import logger
|
||||
|
||||
import httpx
|
||||
from loguru import logger
|
||||
|
||||
currencies = {
|
||||
"AED": "United Arab Emirates Dirham",
|
||||
|
@ -282,7 +281,7 @@ async def btc_price(currency: str) -> float:
|
|||
if not rates:
|
||||
return 9999999999
|
||||
elif len(rates) == 1:
|
||||
logger.warn("Could only fetch one Bitcoin price.")
|
||||
logger.warning("Could only fetch one Bitcoin price.")
|
||||
|
||||
return sum([rate for rate in rates]) / len(rates)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import NamedTuple, Optional, AsyncGenerator, Coroutine
|
||||
from typing import AsyncGenerator, Coroutine, NamedTuple, Optional
|
||||
|
||||
|
||||
class StatusResponse(NamedTuple):
|
||||
|
|
|
@ -5,10 +5,12 @@ except ImportError: # pragma: nocover
|
|||
|
||||
import asyncio
|
||||
import random
|
||||
import time
|
||||
from functools import partial, wraps
|
||||
from os import getenv
|
||||
from typing import AsyncGenerator, Optional
|
||||
import time
|
||||
|
||||
from lnbits import bolt11 as lnbits_bolt11
|
||||
|
||||
from .base import (
|
||||
InvoiceResponse,
|
||||
|
@ -18,7 +20,6 @@ from .base import (
|
|||
Unsupported,
|
||||
Wallet,
|
||||
)
|
||||
from lnbits import bolt11 as lnbits_bolt11
|
||||
|
||||
|
||||
def async_wrap(func):
|
||||
|
|
|
@ -5,9 +5,8 @@ import urllib.parse
|
|||
from os import getenv
|
||||
from typing import AsyncGenerator, Dict, Optional
|
||||
|
||||
from loguru import logger
|
||||
|
||||
import httpx
|
||||
from loguru import logger
|
||||
from websockets import connect
|
||||
from websockets.exceptions import (
|
||||
ConnectionClosed,
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
import asyncio
|
||||
|
||||
from os import getenv
|
||||
from datetime import datetime
|
||||
from typing import Optional, Dict, AsyncGenerator
|
||||
import hashlib
|
||||
import random
|
||||
from datetime import datetime
|
||||
from os import getenv
|
||||
from typing import AsyncGenerator, Dict, Optional
|
||||
|
||||
from environs import Env # type: ignore
|
||||
from loguru import logger
|
||||
|
||||
from lnbits.helpers import urlsafe_short_hash
|
||||
import hashlib
|
||||
from ..bolt11 import encode, decode
|
||||
|
||||
from ..bolt11 import decode, encode
|
||||
from .base import (
|
||||
StatusResponse,
|
||||
InvoiceResponse,
|
||||
PaymentResponse,
|
||||
PaymentStatus,
|
||||
StatusResponse,
|
||||
Wallet,
|
||||
)
|
||||
|
||||
env = Env()
|
||||
env.read_env()
|
||||
|
||||
|
||||
class FakeWallet(Wallet):
|
||||
async def status(self) -> StatusResponse:
|
||||
|
@ -32,7 +36,9 @@ class FakeWallet(Wallet):
|
|||
memo: Optional[str] = None,
|
||||
description_hash: Optional[bytes] = None,
|
||||
) -> InvoiceResponse:
|
||||
secret = getenv("FAKE_WALLET_SECRET")
|
||||
# we set a default secret since FakeWallet is used for internal=True invoices
|
||||
# and the user might not have configured a secret yet
|
||||
secret = env.str("FAKE_WALLET_SECTRET", default="ToTheMoon1")
|
||||
data: Dict = {
|
||||
"out": False,
|
||||
"amount": amount,
|
||||
|
@ -85,10 +91,10 @@ class FakeWallet(Wallet):
|
|||
)
|
||||
|
||||
async def get_invoice_status(self, checking_id: str) -> PaymentStatus:
|
||||
return PaymentStatus(False)
|
||||
return PaymentStatus(None)
|
||||
|
||||
async def get_payment_status(self, checking_id: str) -> PaymentStatus:
|
||||
return PaymentStatus(False)
|
||||
return PaymentStatus(None)
|
||||
|
||||
async def paid_invoices_stream(self) -> AsyncGenerator[str, None]:
|
||||
self.queue = asyncio.Queue(0)
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import asyncio
|
||||
import json
|
||||
import httpx
|
||||
from os import getenv
|
||||
from typing import Optional, Dict, AsyncGenerator
|
||||
from typing import AsyncGenerator, Dict, Optional
|
||||
|
||||
import httpx
|
||||
from loguru import logger
|
||||
|
||||
from .base import (
|
||||
StatusResponse,
|
||||
InvoiceResponse,
|
||||
PaymentResponse,
|
||||
PaymentStatus,
|
||||
StatusResponse,
|
||||
Wallet,
|
||||
)
|
||||
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: lightning.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import enum_type_wrapper
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import enum_type_wrapper
|
||||
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue