diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..ae990f68f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# Top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file, utf-8 charset +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +[/lnbits/static/vendor/*] +end_of_line = unset +insert_final_newline = unset +trim_trailing_whitespace = unset diff --git a/.env.example b/.env.example index 6771412d3..8412d7d24 100644 --- a/.env.example +++ b/.env.example @@ -15,6 +15,7 @@ LNBITS_ALLOWED_IPS="" LNBITS_BLOCKED_IPS="" # Allow users and admins by user IDs (comma separated list) +# if set new users will not be able to create accounts LNBITS_ALLOWED_USERS="" LNBITS_ADMIN_USERS="" # Extensions only admin can access @@ -40,6 +41,11 @@ LNBITS_HIDE_API=false # GitHub has rate-limits for its APIs. The limit can be increased specifying a GITHUB_TOKEN # LNBITS_EXT_GITHUB_TOKEN=github_pat_xxxxxxxxxxxxxxxxxx +# Path where extensions will be installed (defaults to `./lnbits/`). +# Inside this directory the `extensions` and `upgrades` sub-directories will be created. +# LNBITS_EXTENSIONS_PATH="/path/to/some/dir" + + # Extensions to be installed by default. If an extension from this list is uninstalled then it will be re-installed on the next restart. # The extension must be removed from this list in order to not be re-installed. LNBITS_EXTENSIONS_DEFAULT_INSTALL="tpos" @@ -91,6 +97,12 @@ SPARK_TOKEN=myaccesstoken # CoreLightningWallet CORELIGHTNING_RPC="/home/bob/.lightning/bitcoin/lightning-rpc" +# CoreLightningRestWallet +CORELIGHTNING_REST_URL=http://127.0.0.1:8185/ +# Path or BASE64/HEX STRING +CORELIGHTNING_REST_MACAROON="/path/to/clnrest/access.macaroon" +CORELIGHTNING_REST_CERT="/path/to/clnrest/tls.cert" + # LnbitsWallet LNBITS_ENDPOINT=https://legend.lnbits.com LNBITS_KEY=LNBITS_ADMIN_KEY @@ -133,8 +145,3 @@ ECLAIR_PASS=eclairpw # Enter /api in LightningTipBot to get your key LNTIPS_API_KEY=LNTIPS_ADMIN_KEY LNTIPS_API_ENDPOINT=https://ln.tips - -# Cashu Mint -# Use a long-enough random (!) private key. -# Once set, you cannot change this key as for now. -CASHU_PRIVATE_KEY="SuperSecretPrivateKey" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3d2fb213e..5cdd4139b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,9 +2,9 @@ name: codeql on: push: - branches: [main, ] + branches: [main, dev] pull_request: - branches: [main] + branches: [main, dev] schedule: - cron: '0 12 * * 5' diff --git a/Makefile b/Makefile index b1e2b4f68..df5834d24 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,9 @@ checkprettier: checkblack: poetry run black --check . +checkeditorconfig: + editorconfig-checker + dev: poetry run lnbits --reload @@ -48,18 +51,17 @@ test-real-wallet: poetry run pytest test-migration: - rm -rf ./migration-data - mkdir -p ./migration-data - unzip tests/data/mock_data.zip -d ./migration-data + LNBITS_ADMIN_UI=True \ + make test HOST=0.0.0.0 \ PORT=5002 \ - LNBITS_DATA_FOLDER="./migration-data" \ + LNBITS_DATA_FOLDER="./tests/data" \ timeout 5s poetry run lnbits --host 0.0.0.0 --port 5002 || code=$?; if [[ $code -ne 124 && $code -ne 0 ]]; then exit $code; fi HOST=0.0.0.0 \ PORT=5002 \ LNBITS_DATABASE_URL="postgres://lnbits:lnbits@localhost:5432/migration" \ timeout 5s poetry run lnbits --host 0.0.0.0 --port 5002 || code=$?; if [[ $code -ne 124 && $code -ne 0 ]]; then exit $code; fi - LNBITS_DATA_FOLDER="./migration-data" \ + LNBITS_DATA_FOLDER="./tests/data" \ LNBITS_DATABASE_URL="postgres://lnbits:lnbits@localhost:5432/migration" \ poetry run python tools/conv.py diff --git a/README.md b/README.md index a53a83dc7..db0d7122c 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ LNURL has a fallback scheme, so if scanned by a regular QR code reader it can de ![lnurl fallback](https://i.imgur.com/CPBKHIv.png) Using **lnbits.com/?lightning="LNURL-withdraw"** will trigger a withdraw that builds an LNbits wallet. -Example use would be an ATM, which utilises LNURL, if the user scans the QR with a regular QR code scanner app, they will still be able to access the funds. +Example use would be an ATM, which utilizes LNURL, if the user scans the QR with a regular QR code scanner app, they will still be able to access the funds. ![lnurl ATM](https://i.imgur.com/Gi6bn3L.jpg) diff --git a/docs/CNAME b/docs/CNAME index e7e04e603..a3397b934 100644 --- a/docs/CNAME +++ b/docs/CNAME @@ -1 +1 @@ -docs.lnbits.org \ No newline at end of file +docs.lnbits.org diff --git a/docs/devs/extensions.md b/docs/devs/extensions.md index 83ad1cb46..a70f085ff 100644 --- a/docs/devs/extensions.md +++ b/docs/devs/extensions.md @@ -6,13 +6,13 @@ nav_order: 2 --- -Making extensions +Extension set up ================= -Start off by cloning the [example extension](https://github.com/lnbits/example) into your `lnbits/extensions` folder and renaming it to `mysuperplugin`: +Start off by creating a fork of the [example extension](https://github.com/lnbits/example) into own GitHub repository and rename the repository to `mysuperplugin`: ```sh -cd lnbits/extensions -git clone https://github.com/lnbits/example.git --depth=1 mysuperplugin # Let's not use dashes or anything; it doesn't like those. +cd [my-working-folder] +git clone https://github.com/[my-user-name]/mysuperplugin.git --depth=1 # Let's not use dashes or anything; it doesn't like those. cd mysuperplugin rm -rf .git/ find . -type f -print0 | xargs -0 sed -i 's/example/mysuperplugin/g' # Change all occurrences of 'example' to your plugin name 'mysuperplugin'. @@ -20,7 +20,18 @@ mv templates/example templates/mysuperplugin # Rename templates folder. ``` - 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: +1. Edit `manifest.json` and change the organisation name to your GitHub username. +1. Push your changes to GitHub. +1. In GitHub create a new release for your extension repo. Tag the release with `0.0.1` +1. Copy the URL of the extension's raw `manifest.json` URL `https://raw.githubusercontent.com/[my-user-name]/mysuperplugin/master/manifest.json` +1. If you are using the LMNbits Admin UI, go to the Admin UI > Server > Extension Sources, click "Add", paste the URL, then click "Save" +1. If you are configuring LNbits via environment variables, add the URL to the .env file's `LNBITS_EXTENSIONS_MANIFESTS` variable. Restart the LNbits python process +1. You will now see your extension in the LNbits > Extensions list. Click "Enable" to enable it. +1. ... +1. Profit!!! + +Extension structure explained +----------------------------- * 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. * views.py: The `/` path will show up as your plugin's home page in lnbits' UI. Other pages you can define yourself. The `templates` folder should explain itself in relation to this. * migrations.py: Create database tables for your plugin. They'll be created automatically when you start lnbits. diff --git a/docs/devs/websockets.md b/docs/devs/websockets.md index 9ea53a624..c64d044fd 100644 --- a/docs/devs/websockets.md +++ b/docs/devs/websockets.md @@ -9,7 +9,7 @@ nav_order: 2 Websockets ================= -`websockets` are a great way to add a two way instant data channel between server and client. +`websockets` are a great way to add a two way instant data channel between server and client. LNbits has a useful in built websocket tool. With a websocket client connect to (obv change `somespecificid`) `wss://legend.lnbits.com/api/v1/ws/somespecificid` (you can use an online websocket tester). Now make a get to `https://legend.lnbits.com/api/v1/ws/somespecificid/somedata`. You can send data to that websocket by using `from lnbits.core.services import websocketUpdater` and the function `websocketUpdater("somespecificid", "somdata")`. diff --git a/docs/guide/admin_ui.md b/docs/guide/admin_ui.md index 3d3077f36..a5a40dedb 100644 --- a/docs/guide/admin_ui.md +++ b/docs/guide/admin_ui.md @@ -52,8 +52,8 @@ $ sudo nano .env -> set: `LNBITS_ADMIN_UI=true` Now start LNbits once in the terminal window -``` -$ poetry run lnbits +``` +$ poetry run lnbits ``` You can now `cat` the Super User ID: ``` diff --git a/docs/guide/extension-install.md b/docs/guide/extension-install.md index ce421d3e2..8117e993b 100644 --- a/docs/guide/extension-install.md +++ b/docs/guide/extension-install.md @@ -15,7 +15,7 @@ An `Extension Manifest` is a link to a `JSON` file which contains information ab Multiple repositories can be configured. For more information check the [Manifest File](https://github.com/lnbits/lnbits/blob/main/docs/guide/extension-install.md#manifest-file) section. -**LNbits** administrators should configure their instances to use repositories that they trust (like the [lnbits-extensions](https://github.com/lnbits/lnbits-extensions/) one). +**LNbits** administrators should configure their instances to use repositories that they trust (like the [lnbits-extensions](https://github.com/lnbits/lnbits-extensions/) one). > **Warning** > Extensions can have bugs or malicious code, be careful what you install!! @@ -40,9 +40,9 @@ Click the `Manage` button in order to install a particular release of the extens Select the version to be installed (usually the last one) and click `Install`. One can also check the `Release Notes` first. > **Note**: -> +> > For Github repository: the order of the releases is the one in the GitHub releases page -> +> > For Explicit Release: the order of the releases is the one in the "extensions" object @@ -109,12 +109,12 @@ It goes under the `extensions` object and it is of the form: | htmlUrl | string | optional | Link to the extension home page. | | infoNotification | string | optional | Users that have this release installed will see a info message for their extension. For example if the extension support will be terminated soon. | | criticalNotification | string | optional | Reserved for urgent notifications. The admin user will receive a message each time it visits the `Install` page. One example is if the extension has a critical bug. | -| dependencies | list | optional | A list of extension IDs. It signals that those extensions must be installed BEFORE the this one can be installed. +| dependencies | list | optional | A list of extension IDs. It signals that those extensions must be installed BEFORE the this one can be installed. This mode has the advantage of strictly specifying what releases of an extension can be installed. - + ### GitHub Repository It goes under the `repos` object and it is of the form: diff --git a/docs/guide/faq.md b/docs/guide/faq.md index 155fdc208..3d72b5f78 100644 --- a/docs/guide/faq.md +++ b/docs/guide/faq.md @@ -19,24 +19,24 @@ allow-self-payment=1 ``` - +
Funding source only available via tor (e.g. Start9 or Umbrel)

If you want your setup to stay behind tor then only apps, pos and wallets that have tor activated can communicate with your wallets. Most likely you will have trouble when people try to redeem your voucher through onion or when importing your lnbits wallets into a wallet-app that doesnt support tor. If you plan to let LNbits wallets interact with plain internet shops and services you should consider setting up hybrid mode for your node.

- +
Funding source is in a cloud

This means that you might not have access to some files which would allow certain administrative functions. E.g. on Voltage lnd.conf can not be edited. Payments from your node to LNbits wallets can therefore not be configurated in this case atm so you will need to take an extra wallet to send from funding source->wallet x->LNbits wallet (only) for the initial funding of the wallet.

- +
LNbits via clearnet domain

Step by step guide how to convert your Tor only node into a clearnet node to make apps like LNbits accessible via https.

- +
Which funding sources can I use for LNbits?

There are several ways to run a LNbits instance funded from different sources. It is important to choose a source that has a good liquidity and good peers connected. If you use LNbits for public services your users´ payments can then flow happily in both directions. If you would like to fund your LNbits wallet via btc please see section Troubleshooting.

The LNbits manual shows you which sources can be used and how to configure each: CLN, LND, LNPay, Cliche, OpenNode as well as bots.

- + /g,"$1").replace(//g,"$1")),Sa(c,n)&&(n=n.slice(1)),e.chars&&e.chars(n),""}));l+=t.length-h.length,t=h,M(c,l-u,l)}else{var f=t.indexOf("<");if(0===f){if(va.test(t)){var p=t.indexOf("--\x3e");if(p>=0){e.shouldKeepComment&&e.comment(t.substring(4,p),l,l+p+3),k(p+3);continue}}if(ga.test(t)){var m=t.indexOf("]>");if(m>=0){k(m+2);continue}}var v=t.match(ma);if(v){k(v[0].length);continue}var g=t.match(pa);if(g){var _=l;k(g[0].length),M(g[1],_,l);continue}var y=S();if(y){C(y),Sa(y.tagName,t)&&k(1);continue}}var b=void 0,w=void 0,x=void 0;if(f>=0){for(w=t.slice(f);!(pa.test(w)||ha.test(w)||va.test(w)||ga.test(w)||(x=w.indexOf("<",1))<0);)f+=x,w=t.slice(f);b=t.substring(0,f)}f<0&&(b=t),b&&k(b.length),e.chars&&b&&e.chars(b,l-b.length,l)}if(t===n){e.chars&&e.chars(t),!r.length&&e.warn&&e.warn('Mal-formatted tag at end of template: "'+t+'"',{start:l+t.length});break}}function k(e){l+=e,t=t.substring(e)}function S(){var e=t.match(ha);if(e){var n,i,r={tagName:e[1],attrs:[],start:l};for(k(e[0].length);!(n=t.match(fa))&&(i=t.match(ua)||t.match(la));)i.start=l,k(i[0].length),i.end=l,r.attrs.push(i);if(n)return r.unarySlash=n[1],k(n[0].length),r.end=l,r}}function C(t){var n=t.tagName,l=t.unarySlash;o&&("p"===i&&sa(n)&&M(i),s(n)&&i===n&&M(n));for(var u=a(n)||!!l,c=t.attrs.length,d=new Array(c),h=0;h=0&&r[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=r.length-1;u>=a;u--)(u>a||!t&&e.warn)&&e.warn("tag <"+r[u].tag+"> has no matching end tag.",{start:r[u].start,end:r[u].end}),e.end&&e.end(r[u].tag,n,o);r.length=a,i=a&&r[a-1].tag}else"br"===s?e.start&&e.start(t,[],!0,n,o):"p"===s&&(e.start&&e.start(t,[],!1,n,o),e.end&&e.end(t,n,o))}M()}(t,{warn:Ma,expectHTML:e.expectHTML,isUnaryTag:e.isUnaryTag,canBeLeftOpenTag:e.canBeLeftOpenTag,shouldDecodeNewlines:e.shouldDecodeNewlines,shouldDecodeNewlinesForHref:e.shouldDecodeNewlinesForHref,shouldKeepComment:e.comments,outputSourceRange:e.outputSourceRange,start:function(t,n,a,s,c){var d=r&&r.ns||Da(t);K&&"svg"===d&&(n=function(t){for(var e=[],n=0;n, / or =.",{start:t.start+t.name.indexOf("["),end:t.start+t.name.length})})),"style"!==(f=m).tag&&("script"!==f.tag||f.attrsMap.type&&"text/javascript"!==f.attrsMap.type)||rt()||(m.forbidden=!0,Ma("Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <"+t+">, as they will not be parsed.",{start:m.start}));for(var v=0;v cannot be keyed. Place the key on real elements instead.",vr(t,"key")),t.for){var n=t.iterator2||t.iterator1,i=t.parent;n&&n===e&&i&&"transition-group"===i.tag&&Ma("Do not use v-for index as key on children, this is the same as not using keys.",vr(t,"key"),!0)}t.key=e}}(t),t.plain=!t.key&&!t.scopedSlots&&!t.attrsList.length,function(t){var e=gr(t,"ref");e&&(t.ref=e,t.refInFor=function(t){var e=t;for(;e;){if(void 0!==e.for)return!0;e=e.parent}return!1}(t))}(t),function(t){var e;"template"===t.tag?((e=_r(t,"scope"))&&Ma('the "scope" attribute for scoped slots have been deprecated and replaced by "slot-scope" since 2.5. The new "slot-scope" attribute can also be used on plain elements in addition to