diff --git a/lnbits/extensions/copilot/crud.py b/lnbits/extensions/copilot/crud.py
index 955561e4f..a7c36168e 100644
--- a/lnbits/extensions/copilot/crud.py
+++ b/lnbits/extensions/copilot/crud.py
@@ -13,7 +13,7 @@ async def create_copilot(
copilot_id = urlsafe_short_hash()
await db.execute(
"""
- INSERT INTO copilot.copilots (
+ INSERT INTO copilot.newer_copilots (
id,
user,
lnurl_toggle,
@@ -71,24 +71,26 @@ async def update_copilot(
q = ", ".join([f"{field[0]} = ?" for field in data])
items = [f"{field[1]}" for field in data]
items.append(copilot_id)
- await db.execute(f"UPDATE copilot.copilots SET {q} WHERE id = ?", (items))
+ await db.execute(f"UPDATE copilot.newer_copilots SET {q} WHERE id = ?", (items))
row = await db.fetchone(
- "SELECT * FROM copilot.copilots WHERE id = ?", (copilot_id,)
+ "SELECT * FROM copilot.newer_copilots WHERE id = ?", (copilot_id,)
)
return Copilots(**row) if row else None
async def get_copilot(copilot_id: str) -> Copilots:
row = await db.fetchone(
- "SELECT * FROM copilot.copilots WHERE id = ?", (copilot_id,)
+ "SELECT * FROM copilot.newer_copilots WHERE id = ?", (copilot_id,)
)
return Copilots(**row) if row else None
async def get_copilots(user: str) -> List[Copilots]:
- rows = await db.fetchall("SELECT * FROM copilot.copilots WHERE user = ?", (user,))
+ rows = await db.fetchall(
+ "SELECT * FROM copilot.newer_copilots WHERE user = ?", (user,)
+ )
return [Copilots(**row) for row in rows]
async def delete_copilot(copilot_id: str) -> None:
- await db.execute("DELETE FROM copilot.copilots WHERE id = ?", (copilot_id,))
+ await db.execute("DELETE FROM copilot.newer_copilots WHERE id = ?", (copilot_id,))
diff --git a/lnbits/extensions/copilot/migrations.py b/lnbits/extensions/copilot/migrations.py
index 7b23c9368..b1c16dcc8 100644
--- a/lnbits/extensions/copilot/migrations.py
+++ b/lnbits/extensions/copilot/migrations.py
@@ -23,7 +23,7 @@ async def m001_initial(db):
lnurl_title TEXT,
show_message INTEGER,
show_ack INTEGER,
- show_price TEXT,
+ show_price INTEGER,
amount_made INTEGER,
fullscreen_cam INTEGER,
iframe_url TEXT,
@@ -43,37 +43,37 @@ async def m002_fix_data_types(db):
"ALTER TABLE copilot.copilots ALTER COLUMN show_price TYPE TEXT;"
)
- # If needed, migration for SQLite (RENAME not working properly)
- #
- # await db.execute(
- # f"""
- # CREATE TABLE copilot.new_copilots (
- # id TEXT NOT NULL PRIMARY KEY,
- # "user" TEXT,
- # title TEXT,
- # lnurl_toggle INTEGER,
- # wallet TEXT,
- # animation1 TEXT,
- # animation2 TEXT,
- # animation3 TEXT,
- # animation1threshold INTEGER,
- # animation2threshold INTEGER,
- # animation3threshold INTEGER,
- # animation1webhook TEXT,
- # animation2webhook TEXT,
- # animation3webhook TEXT,
- # lnurl_title TEXT,
- # show_message INTEGER,
- # show_ack INTEGER,
- # show_price TEXT,
- # amount_made INTEGER,
- # fullscreen_cam INTEGER,
- # iframe_url TEXT,
- # timestamp TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
- # );
- # """
- # )
- #
- # await db.execute("INSERT INTO copilot.new_copilots SELECT * FROM copilot.copilots;")
- # await db.execute("DROP TABLE IF EXISTS copilot.copilots;")
- # await db.execute("ALTER TABLE copilot.new_copilots RENAME TO copilot.copilots;")
+
+async def m003_fix_data_types(db):
+ await db.execute(
+ f"""
+ CREATE TABLE copilot.newer_copilots (
+ id TEXT NOT NULL PRIMARY KEY,
+ "user" TEXT,
+ title TEXT,
+ lnurl_toggle INTEGER,
+ wallet TEXT,
+ animation1 TEXT,
+ animation2 TEXT,
+ animation3 TEXT,
+ animation1threshold INTEGER,
+ animation2threshold INTEGER,
+ animation3threshold INTEGER,
+ animation1webhook TEXT,
+ animation2webhook TEXT,
+ animation3webhook TEXT,
+ lnurl_title TEXT,
+ show_message INTEGER,
+ show_ack INTEGER,
+ show_price TEXT,
+ amount_made INTEGER,
+ fullscreen_cam INTEGER,
+ iframe_url TEXT,
+ timestamp TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
+ );
+ """
+ )
+
+ await db.execute(
+ "INSERT INTO copilot.newer_copilots SELECT * FROM copilot.copilots"
+ )
diff --git a/lnbits/extensions/copilot/views_api.py b/lnbits/extensions/copilot/views_api.py
index d9342a30a..91b0572a5 100644
--- a/lnbits/extensions/copilot/views_api.py
+++ b/lnbits/extensions/copilot/views_api.py
@@ -5,7 +5,7 @@ from fastapi.param_functions import Query
from fastapi.params import Depends
from starlette.exceptions import HTTPException
-from lnbits.decorators import WalletTypeInfo, get_key_type
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import copilot_ext
from .crud import (
@@ -54,7 +54,7 @@ async def api_copilot_retrieve(
async def api_copilot_create_or_update(
data: CreateCopilotData,
copilot_id: str = Query(None),
- wallet: WalletTypeInfo = Depends(get_key_type),
+ wallet: WalletTypeInfo = Depends(require_admin_key),
):
data.user = wallet.wallet.user
data.wallet = wallet.wallet.id
@@ -67,7 +67,7 @@ async def api_copilot_create_or_update(
@copilot_ext.delete("/api/v1/copilot/{copilot_id}")
async def api_copilot_delete(
- copilot_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)
+ copilot_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key)
):
copilot = await get_copilot(copilot_id)
diff --git a/lnbits/extensions/events/__init__.py b/lnbits/extensions/events/__init__.py
index da29358b6..9d59a8b6e 100644
--- a/lnbits/extensions/events/__init__.py
+++ b/lnbits/extensions/events/__init__.py
@@ -16,4 +16,3 @@ def events_renderer():
from .views import * # noqa
from .views_api import * # noqa
-
diff --git a/lnbits/extensions/events/crud.py b/lnbits/extensions/events/crud.py
index 4a24b7970..dc7db93ad 100644
--- a/lnbits/extensions/events/crud.py
+++ b/lnbits/extensions/events/crud.py
@@ -74,6 +74,9 @@ async def get_tickets(wallet_ids: Union[str, List[str]]) -> List[Tickets]:
async def delete_ticket(payment_hash: str) -> None:
await db.execute("DELETE FROM events.ticket WHERE id = ?", (payment_hash,))
+async def delete_event_tickets(event_id: str) -> None:
+ await db.execute("DELETE FROM events.tickets WHERE event = ?", (event_id,))
+
# EVENTS
diff --git a/lnbits/extensions/events/templates/events/index.html b/lnbits/extensions/events/templates/events/index.html
index c2d81960e..409ed2af3 100644
--- a/lnbits/extensions/events/templates/events/index.html
+++ b/lnbits/extensions/events/templates/events/index.html
@@ -380,14 +380,14 @@
methods: {
getTickets: function () {
var self = this
- console.log('obj')
LNbits.api
- .request(
- 'GET',
- '/events/api/v1/tickets?all_wallets',
- this.g.user.wallets[0].inkey
+ .request(
+ 'GET',
+ '/events/api/v1/tickets?all_wallets=true',
+ this.g.user.wallets[0].inkey
)
.then(function (response) {
+ console.log(response)
self.tickets = response.data.map(function (obj) {
console.log(obj)
return mapEvents(obj)
diff --git a/lnbits/extensions/events/views_api.py b/lnbits/extensions/events/views_api.py
index 5dae31e1d..4ff93c31e 100644
--- a/lnbits/extensions/events/views_api.py
+++ b/lnbits/extensions/events/views_api.py
@@ -15,6 +15,7 @@ from .crud import (
create_event,
create_ticket,
delete_event,
+ delete_event_tickets,
delete_ticket,
get_event,
get_event_tickets,
@@ -81,6 +82,7 @@ async def api_form_delete(event_id, wallet: WalletTypeInfo = Depends(get_key_typ
)
await delete_event(event_id)
+ await delete_event_tickets(event_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
diff --git a/lnbits/extensions/jukebox/views_api.py b/lnbits/extensions/jukebox/views_api.py
index e6403d007..ed92efc9e 100644
--- a/lnbits/extensions/jukebox/views_api.py
+++ b/lnbits/extensions/jukebox/views_api.py
@@ -7,11 +7,11 @@ from fastapi import Request
from fastapi.param_functions import Query
from fastapi.params import Depends
from starlette.exceptions import HTTPException
-from starlette.responses import HTMLResponse, JSONResponse # type: ignore
+from starlette.responses import HTMLResponse # type: ignore
from lnbits.core.crud import get_wallet
from lnbits.core.services import check_invoice_status, create_invoice
-from lnbits.decorators import WalletTypeInfo, get_key_type
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import jukebox_ext
from .crud import (
@@ -30,7 +30,7 @@ from .models import CreateJukeboxPayment, CreateJukeLinkData
@jukebox_ext.get("/api/v1/jukebox")
async def api_get_jukeboxs(
req: Request,
- wallet: WalletTypeInfo = Depends(get_key_type),
+ wallet: WalletTypeInfo = Depends(require_admin_key),
all_wallets: bool = Query(False),
):
wallet_user = wallet.wallet.user
@@ -72,7 +72,7 @@ async def api_check_credentials_callbac(
@jukebox_ext.get("/api/v1/jukebox/{juke_id}")
async def api_check_credentials_check(
- juke_id: str = Query(None), wallet: WalletTypeInfo = Depends(get_key_type)
+ juke_id: str = Query(None), wallet: WalletTypeInfo = Depends(require_admin_key)
):
print(juke_id)
jukebox = await get_jukebox(juke_id)
@@ -85,7 +85,7 @@ async def api_check_credentials_check(
async def api_create_update_jukebox(
data: CreateJukeLinkData,
juke_id: str = Query(None),
- wallet: WalletTypeInfo = Depends(get_key_type),
+ wallet: WalletTypeInfo = Depends(require_admin_key),
):
if juke_id:
jukebox = await update_jukebox(data, juke_id=juke_id)
@@ -95,7 +95,7 @@ async def api_create_update_jukebox(
@jukebox_ext.delete("/api/v1/jukebox/{juke_id}")
-async def api_delete_item(juke_id=None, wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_delete_item(juke_id=None, wallet: WalletTypeInfo = Depends(require_admin_key)):
await delete_jukebox(juke_id)
try:
return [{**jukebox} for jukebox in await get_jukeboxs(wallet.wallet.user)]
diff --git a/lnbits/extensions/lnurlp/__init__.py b/lnbits/extensions/lnurlp/__init__.py
index ea8e509a0..41b7a7b6c 100644
--- a/lnbits/extensions/lnurlp/__init__.py
+++ b/lnbits/extensions/lnurlp/__init__.py
@@ -1,8 +1,7 @@
import asyncio
-from fastapi import APIRouter, FastAPI
+from fastapi import APIRouter
from fastapi.staticfiles import StaticFiles
-from starlette.routing import Mount
from lnbits.db import Database
from lnbits.helpers import template_renderer
@@ -29,10 +28,10 @@ def lnurlp_renderer():
return template_renderer(["lnbits/extensions/lnurlp/templates"])
-from .views_api import * # noqa
-from .views import * # noqa
-from .tasks import wait_for_paid_invoices
from .lnurl import * # noqa
+from .tasks import wait_for_paid_invoices
+from .views import * # noqa
+from .views_api import * # noqa
def lnurlp_start():
diff --git a/lnbits/extensions/lnurlp/lnurl.py b/lnbits/extensions/lnurlp/lnurl.py
index f7a615a4b..173b4823a 100644
--- a/lnbits/extensions/lnurlp/lnurl.py
+++ b/lnbits/extensions/lnurlp/lnurl.py
@@ -1,13 +1,14 @@
import hashlib
import math
from http import HTTPStatus
-from fastapi import FastAPI, Request
-from starlette.exceptions import HTTPException
-from lnurl import (
- LnurlPayResponse,
- LnurlPayActionResponse,
+
+from fastapi import Request
+from lnurl import ( # type: ignore
LnurlErrorResponse,
-) # type: ignore
+ LnurlPayActionResponse,
+ LnurlPayResponse,
+)
+from starlette.exceptions import HTTPException
from lnbits.core.services import create_invoice
from lnbits.utils.exchange_rates import get_fiat_rate_satoshis
diff --git a/lnbits/extensions/lnurlp/views.py b/lnbits/extensions/lnurlp/views.py
index d39a5ebfc..4e9f487ca 100644
--- a/lnbits/extensions/lnurlp/views.py
+++ b/lnbits/extensions/lnurlp/views.py
@@ -1,23 +1,21 @@
from http import HTTPStatus
+from fastapi import Request
+from fastapi.params import Depends
+from fastapi.templating import Jinja2Templates
+from starlette.exceptions import HTTPException
+from starlette.responses import HTMLResponse
+
+from lnbits.core.models import User
from lnbits.decorators import check_user_exists
from . import lnurlp_ext, lnurlp_renderer
from .crud import get_pay_link
-from fastapi import FastAPI, Request
-from fastapi.params import Depends
-from fastapi.templating import Jinja2Templates
-
-from starlette.exceptions import HTTPException
-from starlette.responses import HTMLResponse
-from lnbits.core.models import User
templates = Jinja2Templates(directory="templates")
@lnurlp_ext.get("/", response_class=HTMLResponse)
-# @validate_uuids(["usr"], required=True)
-# @check_user_exists()
async def index(request: Request, user: User = Depends(check_user_exists)):
return lnurlp_renderer().TemplateResponse(
"lnurlp/index.html", {"request": request, "user": user.dict()}
@@ -31,7 +29,6 @@ async def display(request: Request, link_id):
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
)
- # abort(HTTPStatus.NOT_FOUND, "Pay link does not exist.")
ctx = {"request": request, "lnurl": link.lnurl(req=request)}
return lnurlp_renderer().TemplateResponse("lnurlp/display.html", ctx)
@@ -43,6 +40,5 @@ async def print_qr(request: Request, link_id):
raise HTTPException(
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
)
- # abort(HTTPStatus.NOT_FOUND, "Pay link does not exist.")
ctx = {"request": request, "lnurl": link.lnurl(req=request)}
return lnurlp_renderer().TemplateResponse("lnurlp/print_qr.html", ctx)
diff --git a/lnbits/extensions/lnurlp/views_api.py b/lnbits/extensions/lnurlp/views_api.py
index 7558006f9..94e15e3ab 100644
--- a/lnbits/extensions/lnurlp/views_api.py
+++ b/lnbits/extensions/lnurlp/views_api.py
@@ -1,27 +1,24 @@
-from typing import Optional
-from fastapi.params import Depends
-from fastapi.param_functions import Query
-from pydantic.main import BaseModel
-
from http import HTTPStatus
+
+from fastapi import Request
+from fastapi.param_functions import Query
+from fastapi.params import Depends
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl # type: ignore
from starlette.exceptions import HTTPException
-from fastapi import Request
-from starlette.responses import HTMLResponse, JSONResponse # type: ignore
from lnbits.core.crud import get_user
from lnbits.decorators import WalletTypeInfo, get_key_type
from lnbits.utils.exchange_rates import currencies, get_fiat_rate_satoshis
-from .models import CreatePayLinkData
from . import lnurlp_ext
from .crud import (
create_pay_link,
+ delete_pay_link,
get_pay_link,
get_pay_links,
update_pay_link,
- delete_pay_link,
)
+from .models import CreatePayLinkData
@lnurlp_ext.get("/api/v1/currencies")
diff --git a/lnbits/extensions/lnurlpos/views_api.py b/lnbits/extensions/lnurlpos/views_api.py
index 21c8dd124..f2adcf7b3 100644
--- a/lnbits/extensions/lnurlpos/views_api.py
+++ b/lnbits/extensions/lnurlpos/views_api.py
@@ -1,26 +1,23 @@
-import hashlib
-from fastapi import FastAPI, Request
-from fastapi.params import Depends
from http import HTTPStatus
-from fastapi.templating import Jinja2Templates
-from starlette.exceptions import HTTPException
-from starlette.responses import HTMLResponse
-from fastapi.params import Depends
-from fastapi.param_functions import Query
-from lnbits.decorators import check_user_exists, WalletTypeInfo, get_key_type
-from lnbits.core.crud import get_user
-from lnbits.core.models import User, Payment
-from . import lnurlpos_ext
+from fastapi import Request
+from fastapi.param_functions import Query
+from fastapi.params import Depends
+from starlette.exceptions import HTTPException
+
+from lnbits.core.crud import get_user
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from lnbits.extensions.lnurlpos import lnurlpos_ext
+from lnbits.utils.exchange_rates import currencies
+
+from . import lnurlpos_ext
from .crud import (
create_lnurlpos,
- update_lnurlpos,
+ delete_lnurlpos,
get_lnurlpos,
get_lnurlposs,
- delete_lnurlpos,
+ update_lnurlpos,
)
-from lnbits.utils.exchange_rates import currencies
from .models import createLnurlpos
@@ -37,7 +34,7 @@ async def api_list_currencies_available():
async def api_lnurlpos_create_or_update(
request: Request,
data: createLnurlpos,
- wallet: WalletTypeInfo = Depends(get_key_type),
+ wallet: WalletTypeInfo = Depends(require_admin_key),
lnurlpos_id: str = Query(None),
):
if not lnurlpos_id:
@@ -79,7 +76,7 @@ async def api_lnurlpos_retrieve(
@lnurlpos_ext.delete("/api/v1/lnurlpos/{lnurlpos_id}")
async def api_lnurlpos_delete(
request: Request,
- wallet: WalletTypeInfo = Depends(get_key_type),
+ wallet: WalletTypeInfo = Depends(require_admin_key),
lnurlpos_id: str = Query(None),
):
lnurlpos = await get_lnurlpos(lnurlpos_id)
diff --git a/lnbits/extensions/ngrok/config.json b/lnbits/extensions/ngrok/config.json.example
similarity index 100%
rename from lnbits/extensions/ngrok/config.json
rename to lnbits/extensions/ngrok/config.json.example
diff --git a/lnbits/extensions/offlineshop/views_api.py b/lnbits/extensions/offlineshop/views_api.py
index f3968948f..906526517 100644
--- a/lnbits/extensions/offlineshop/views_api.py
+++ b/lnbits/extensions/offlineshop/views_api.py
@@ -1,27 +1,26 @@
import json
-
-from typing import List, Optional
-from fastapi.params import Depends
-from pydantic.main import BaseModel
-
from http import HTTPStatus
+from typing import Optional
+
+from fastapi.params import Depends
from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl
+from pydantic.main import BaseModel
from starlette.exceptions import HTTPException
from starlette.requests import Request
-from starlette.responses import HTMLResponse, JSONResponse # type: ignore
+from starlette.responses import HTMLResponse # type: ignore
from lnbits.decorators import WalletTypeInfo, get_key_type
-from lnbits.utils.exchange_rates import currencies
from lnbits.requestvars import g
+from lnbits.utils.exchange_rates import currencies
from . import offlineshop_ext
from .crud import (
+ add_item,
+ delete_item_from_shop,
+ get_items,
get_or_create_shop_by_wallet,
set_method,
- add_item,
update_item,
- get_items,
- delete_item_from_shop,
)
from .models import ShopCounter
diff --git a/lnbits/extensions/satsdice/views_api.py b/lnbits/extensions/satsdice/views_api.py
index 315d823ce..91ce62dfb 100644
--- a/lnbits/extensions/satsdice/views_api.py
+++ b/lnbits/extensions/satsdice/views_api.py
@@ -7,7 +7,7 @@ from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl # type: ignore
from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user
-from lnbits.decorators import WalletTypeInfo, get_key_type
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import satsdice_ext
from .crud import (
@@ -67,7 +67,7 @@ async def api_link_retrieve(
status_code=HTTPStatus.FORBIDDEN, detail="Not your pay link."
)
- return {**link._asdict(), **{"lnurl": link.lnurl}}
+ return {**link.dict(), **{"lnurl": link.lnurl}}
@satsdice_ext.post("/api/v1/links", status_code=HTTPStatus.CREATED)
@@ -112,7 +112,7 @@ async def api_link_delete(
status_code=HTTPStatus.NOT_FOUND, detail="Pay link does not exist."
)
- if link.wallet != g.wallet.id:
+ if link.wallet != wallet.wallet.id:
raise HTTPException(
status_code=HTTPStatus.FORBIDDEN, detail="Not your pay link."
)
@@ -125,117 +125,6 @@ async def api_link_delete(
##########LNURL withdraw
-@satsdice_ext.get("/api/v1/withdraws")
-async def api_withdraws(
- wallet: WalletTypeInfo = Depends(get_key_type), all_wallets: str = Query(None)
-):
- wallet_ids = [wallet.wallet.id]
-
- if all_wallets:
- wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
- try:
- return (
- jsonify(
- [
- {**withdraw._asdict(), **{"lnurl": withdraw.lnurl}}
- for withdraw in await get_satsdice_withdraws(wallet_ids)
- ]
- ),
- HTTPStatus.OK,
- )
- except LnurlInvalidUrl:
- raise HTTPException(
- status_code=HTTPStatus.UPGRADE_REQUIRED,
- detail="LNURLs need to be delivered over a publically accessible `https` domain or Tor.",
- )
-
-
-@satsdice_ext.get("/api/v1/withdraws/{withdraw_id}")
-async def api_withdraw_retrieve(
- wallet: WalletTypeInfo = Depends(get_key_type), withdraw_id: str = Query(None)
-):
- withdraw = await get_satsdice_withdraw(withdraw_id, 0)
-
- if not withdraw:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="satsdice withdraw does not exist."
- )
-
- if withdraw.wallet != wallet.wallet.id:
- raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not your satsdice withdraw."
- )
-
- return {**withdraw._asdict(), **{"lnurl": withdraw.lnurl}}, HTTPStatus.OK
-
-
-@satsdice_ext.post("/api/v1/withdraws", status_code=HTTPStatus.CREATED)
-@satsdice_ext.put("/api/v1/withdraws/{withdraw_id}", status_code=HTTPStatus.OK)
-async def api_withdraw_create_or_update(
- data: CreateSatsDiceWithdraws,
- wallet: WalletTypeInfo = Depends(get_key_type),
- withdraw_id: str = Query(None),
-):
- if data.max_satsdiceable < data.min_satsdiceable:
- raise HTTPException(
- status_code=HTTPStatus.BAD_REQUEST,
- detail="`max_satsdiceable` needs to be at least `min_satsdiceable`.",
- )
-
- usescsv = ""
- for i in range(data.uses):
- if data.is_unique:
- usescsv += "," + str(i + 1)
- else:
- usescsv += "," + str(1)
- usescsv = usescsv[1:]
-
- if withdraw_id:
- withdraw = await get_satsdice_withdraw(withdraw_id, 0)
- if not withdraw:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND,
- detail="satsdice withdraw does not exist.",
- )
- if withdraw.wallet != wallet.wallet.id:
- raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not your satsdice withdraw."
- )
-
- withdraw = await update_satsdice_withdraw(
- withdraw_id, **data, usescsv=usescsv, used=0
- )
- else:
- withdraw = await create_satsdice_withdraw(
- wallet_id=wallet.wallet.id, **data, usescsv=usescsv
- )
-
- return {**withdraw._asdict(), **{"lnurl": withdraw.lnurl}}
-
-
-@satsdice_ext.delete("/api/v1/withdraws/{withdraw_id}")
-async def api_withdraw_delete(
- data: CreateSatsDiceWithdraws,
- wallet: WalletTypeInfo = Depends(get_key_type),
- withdraw_id: str = Query(None),
-):
- withdraw = await get_satsdice_withdraw(withdraw_id)
-
- if not withdraw:
- raise HTTPException(
- status_code=HTTPStatus.NOT_FOUND, detail="satsdice withdraw does not exist."
- )
-
- if withdraw.wallet != wallet.wallet.id:
- raise HTTPException(
- status_code=HTTPStatus.FORBIDDEN, detail="Not your satsdice withdraw."
- )
-
- await delete_satsdice_withdraw(withdraw_id)
-
- return "", HTTPStatus.NO_CONTENT
-
-
@satsdice_ext.get("/api/v1/withdraws/{the_hash}/{lnurl_id}")
async def api_withdraw_hash_retrieve(
wallet: WalletTypeInfo = Depends(get_key_type),
diff --git a/lnbits/extensions/satspay/views_api.py b/lnbits/extensions/satspay/views_api.py
index 2ca16370c..733e63e6c 100644
--- a/lnbits/extensions/satspay/views_api.py
+++ b/lnbits/extensions/satspay/views_api.py
@@ -1,29 +1,22 @@
-import hashlib
-
from http import HTTPStatus
-import httpx
+import httpx
from fastapi import Query
from fastapi.params import Depends
-
from starlette.exceptions import HTTPException
-from starlette.requests import Request
-from starlette.responses import HTMLResponse, JSONResponse # type: ignore
-
-
-from lnbits.core.crud import get_user
-from lnbits.decorators import WalletTypeInfo, get_key_type
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from lnbits.extensions.satspay import satspay_ext
-from .models import CreateCharge
+
from .crud import (
+ check_address_balance,
create_charge,
- update_charge,
+ delete_charge,
get_charge,
get_charges,
- delete_charge,
- check_address_balance,
+ update_charge,
)
+from .models import CreateCharge
#############################CHARGES##########################
@@ -31,7 +24,7 @@ from .crud import (
@satspay_ext.post("/api/v1/charge")
@satspay_ext.put("/api/v1/charge/{charge_id}")
async def api_charge_create_or_update(
- data: CreateCharge, wallet: WalletTypeInfo = Depends(get_key_type), charge_id=None
+ data: CreateCharge, wallet: WalletTypeInfo = Depends(require_admin_key), charge_id=None
):
if not charge_id:
charge = await create_charge(user=wallet.wallet.user, data=data)
diff --git a/lnbits/extensions/tipjar/README.md b/lnbits/extensions/tipjar/README.md
new file mode 100644
index 000000000..4965ec936
--- /dev/null
+++ b/lnbits/extensions/tipjar/README.md
@@ -0,0 +1,15 @@
+
Tip Jars
+Accept tips in Bitcoin, with small messages attached!
+The TipJar extension allows you to integrate Bitcoin Lightning (and on-chain) tips into your website or social media!
+
+
+
+How to set it up
+
+1. Simply create a new Tip Jar with the desired details (onchain optional):
+
+1. Share the URL you get from this little button:
+
+
+
+And that's it already! Let the sats flow!
diff --git a/lnbits/extensions/tipjar/__init__.py b/lnbits/extensions/tipjar/__init__.py
new file mode 100644
index 000000000..a4b50c041
--- /dev/null
+++ b/lnbits/extensions/tipjar/__init__.py
@@ -0,0 +1,16 @@
+from fastapi import APIRouter
+
+from lnbits.db import Database
+from lnbits.helpers import template_renderer
+
+db = Database("ext_tipjar")
+
+tipjar_ext: APIRouter = APIRouter(prefix="/tipjar", tags=["tipjar"])
+
+
+def tipjar_renderer():
+ return template_renderer(["lnbits/extensions/tipjar/templates"])
+
+
+from .views import * # noqa
+from .views_api import * # noqa
diff --git a/lnbits/extensions/tipjar/config.json b/lnbits/extensions/tipjar/config.json
new file mode 100644
index 000000000..e48eb4ea1
--- /dev/null
+++ b/lnbits/extensions/tipjar/config.json
@@ -0,0 +1,6 @@
+{
+ "name": "Tip Jar",
+ "short_description": "Accept Bitcoin donations, with messages attached!",
+ "icon": "favorite",
+ "contributors": ["Fittiboy"]
+}
diff --git a/lnbits/extensions/tipjar/crud.py b/lnbits/extensions/tipjar/crud.py
new file mode 100644
index 000000000..e9fe53f9c
--- /dev/null
+++ b/lnbits/extensions/tipjar/crud.py
@@ -0,0 +1,130 @@
+from . import db
+from .models import Tip, TipJar, createTip, createTipJar
+
+from ..satspay.crud import delete_charge # type: ignore
+
+from typing import Optional
+
+from lnbits.db import SQLITE
+
+
+async def create_tip(
+ id: int,
+ wallet: str,
+ message: str,
+ name: str,
+ sats: int,
+ tipjar: str,
+) -> Tip:
+ """Create a new Tip"""
+ await db.execute(
+ """
+ INSERT INTO tipjar.Tips (
+ id,
+ wallet,
+ name,
+ message,
+ sats,
+ tipjar
+ )
+ VALUES (?, ?, ?, ?, ?, ?)
+ """,
+ (id, wallet, name, message, sats, tipjar),
+ )
+
+ tip = await get_tip(id)
+ assert tip, "Newly created tip couldn't be retrieved"
+ return tip
+
+
+async def create_tipjar(data: createTipJar) -> TipJar:
+ """Create a new TipJar"""
+
+ returning = "" if db.type == SQLITE else "RETURNING ID"
+ method = db.execute if db.type == SQLITE else db.fetchone
+
+ result = await (method)(
+ f"""
+ INSERT INTO tipjar.TipJars (
+ name,
+ wallet,
+ webhook,
+ onchain
+ )
+ VALUES (?, ?, ?, ?)
+ {returning}
+ """,
+ (data.name, data.wallet, data.webhook, data.onchain),
+ )
+ if db.type == SQLITE:
+ tipjar_id = result._result_proxy.lastrowid
+ else:
+ tipjar_id = result[0]
+
+ tipjar = await get_tipjar(tipjar_id)
+ assert tipjar
+ return tipjar
+
+
+async def get_tipjar(tipjar_id: int) -> Optional[TipJar]:
+ """Return a tipjar by ID"""
+ row = await db.fetchone("SELECT * FROM tipjar.TipJars WHERE id = ?", (tipjar_id,))
+ return TipJar(**row) if row else None
+
+
+async def get_tipjars(wallet_id: str) -> Optional[list]:
+ """Return all TipJars belonging assigned to the wallet_id"""
+ rows = await db.fetchall(
+ "SELECT * FROM tipjar.TipJars WHERE wallet = ?", (wallet_id,)
+ )
+ return [TipJar(**row) for row in rows] if rows else None
+
+
+async def delete_tipjar(tipjar_id: int) -> None:
+ """Delete a TipJar and all corresponding Tips"""
+ await db.execute("DELETE FROM tipjar.TipJars WHERE id = ?", (tipjar_id,))
+ rows = await db.fetchall("SELECT * FROM tipjar.Tips WHERE tipjar = ?", (tipjar_id,))
+ for row in rows:
+ await delete_tip(row["id"])
+
+
+async def get_tip(tip_id: str) -> Optional[Tip]:
+ """Return a Tip"""
+ row = await db.fetchone("SELECT * FROM tipjar.Tips WHERE id = ?", (tip_id,))
+ return Tip(**row) if row else None
+
+
+async def get_tips(wallet_id: str) -> Optional[list]:
+ """Return all Tips assigned to wallet_id"""
+ rows = await db.fetchall("SELECT * FROM tipjar.Tips WHERE wallet = ?", (wallet_id,))
+ return [Tip(**row) for row in rows] if rows else None
+
+
+async def delete_tip(tip_id: str) -> None:
+ """Delete a Tip and its corresponding statspay charge"""
+ await db.execute("DELETE FROM tipjar.Tips WHERE id = ?", (tip_id,))
+ await delete_charge(tip_id)
+
+
+async def update_tip(tip_id: str, **kwargs) -> Tip:
+ """Update a Tip"""
+ q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+ await db.execute(
+ f"UPDATE tipjar.Tips SET {q} WHERE id = ?",
+ (*kwargs.values(), tip_id),
+ )
+ row = await db.fetchone("SELECT * FROM tipjar.Tips WHERE id = ?", (tip_id,))
+ assert row, "Newly updated tip couldn't be retrieved"
+ return Tip(**row)
+
+
+async def update_tipjar(tipjar_id: str, **kwargs) -> TipJar:
+ """Update a tipjar"""
+ q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
+ await db.execute(
+ f"UPDATE tipjar.TipJars SET {q} WHERE id = ?",
+ (*kwargs.values(), tipjar_id),
+ )
+ row = await db.fetchone("SELECT * FROM tipjar.TipJars WHERE id = ?", (tipjar_id,))
+ assert row, "Newly updated tipjar couldn't be retrieved"
+ return TipJar(**row)
diff --git a/lnbits/extensions/tipjar/helpers.py b/lnbits/extensions/tipjar/helpers.py
new file mode 100644
index 000000000..540614057
--- /dev/null
+++ b/lnbits/extensions/tipjar/helpers.py
@@ -0,0 +1,20 @@
+from lnbits.core.crud import get_wallet
+from .crud import get_tipjar
+import json
+
+
+async def get_charge_details(tipjar_id):
+ """Return the default details for a satspay charge"""
+ tipjar = await get_tipjar(tipjar_id)
+ wallet_id = tipjar.wallet
+ wallet = await get_wallet(wallet_id)
+ user = wallet.user
+ details = {
+ "time": 1440,
+ "user": user,
+ "lnbitswallet": wallet_id,
+ "onchainwallet": tipjar.onchain,
+ "completelink": "/tipjar/" + str(tipjar_id),
+ "completelinktext": "Thanks for the tip!",
+ }
+ return details
diff --git a/lnbits/extensions/tipjar/migrations.py b/lnbits/extensions/tipjar/migrations.py
new file mode 100644
index 000000000..6b58fbca2
--- /dev/null
+++ b/lnbits/extensions/tipjar/migrations.py
@@ -0,0 +1,27 @@
+async def m001_initial(db):
+
+ await db.execute(
+ f"""
+ CREATE TABLE IF NOT EXISTS tipjar.TipJars (
+ id {db.serial_primary_key},
+ name TEXT NOT NULL,
+ wallet TEXT NOT NULL,
+ onchain TEXT,
+ webhook TEXT
+ );
+ """
+ )
+
+ await db.execute(
+ f"""
+ CREATE TABLE IF NOT EXISTS tipjar.Tips (
+ id TEXT PRIMARY KEY,
+ wallet TEXT NOT NULL,
+ name TEXT NOT NULL,
+ message TEXT NOT NULL,
+ sats INT NOT NULL,
+ tipjar INT NOT NULL,
+ FOREIGN KEY(tipjar) REFERENCES {db.references_schema}TipJars(id)
+ );
+ """
+ )
diff --git a/lnbits/extensions/tipjar/models.py b/lnbits/extensions/tipjar/models.py
new file mode 100644
index 000000000..3e68f846a
--- /dev/null
+++ b/lnbits/extensions/tipjar/models.py
@@ -0,0 +1,64 @@
+import json
+from lnurl import Lnurl, LnurlWithdrawResponse, encode as lnurl_encode # type: ignore
+from urllib.parse import urlparse, urlunparse, parse_qs, urlencode, ParseResult
+from lnurl.types import LnurlPayMetadata # type: ignore
+from sqlite3 import Row
+from typing import NamedTuple, Optional, Dict
+import shortuuid # type: ignore
+from fastapi.param_functions import Query
+from pydantic.main import BaseModel
+from pydantic import BaseModel
+from typing import Optional, NamedTuple
+from fastapi import FastAPI, Request
+
+
+class createTip(BaseModel):
+ id: str
+ wallet: str
+ sats: int
+ tipjar: int
+ name: str = "Anonymous"
+ message: str = ""
+
+
+class Tip(NamedTuple):
+ """A Tip represents a single donation"""
+
+ id: str # This ID always corresponds to a satspay charge ID
+ wallet: str
+ name: str # Name of the donor
+ message: str # Donation message
+ sats: int
+ tipjar: int # The ID of the corresponding tip jar
+
+ @classmethod
+ def from_row(cls, row: Row) -> "Tip":
+ return cls(**dict(row))
+
+
+class createTipJar(BaseModel):
+ name: str
+ wallet: str
+ webhook: str = None
+ onchain: str = None
+
+
+class createTips(BaseModel):
+ name: str
+ sats: str
+ tipjar: str
+ message: str
+
+
+class TipJar(NamedTuple):
+ """A TipJar represents a user's tip jar"""
+
+ id: int
+ name: str # The name of the donatee
+ wallet: str # Lightning wallet
+ onchain: Optional[str] # Watchonly wallet
+ webhook: Optional[str] # URL to POST tips to
+
+ @classmethod
+ def from_row(cls, row: Row) -> "TipJar":
+ return cls(**dict(row))
diff --git a/lnbits/extensions/tipjar/templates/tipjar/_api_docs.html b/lnbits/extensions/tipjar/templates/tipjar/_api_docs.html
new file mode 100644
index 000000000..42788bad6
--- /dev/null
+++ b/lnbits/extensions/tipjar/templates/tipjar/_api_docs.html
@@ -0,0 +1,16 @@
+
+
+
+ Tip Jar: Receive tips with messages!
+
+
+ Your personal Bitcoin tip page, which supports
+ lightning and on-chain payments.
+ Notifications, including a donation message,
+ can be sent via webhook.
+
+ Created by, Fitti
+
+
+
diff --git a/lnbits/extensions/tipjar/templates/tipjar/display.html b/lnbits/extensions/tipjar/templates/tipjar/display.html
new file mode 100644
index 000000000..80e5c6fe3
--- /dev/null
+++ b/lnbits/extensions/tipjar/templates/tipjar/display.html
@@ -0,0 +1,94 @@
+{% extends "public.html" %} {% block page %}
+
+
+
+
+ Tip {{ donatee }} some sats!
+
+
+
+
+
+
+ Submit
+
+
+
+
+
+
+
+{% endblock %} {% block scripts %}
+
+{% endblock %}
diff --git a/lnbits/extensions/tipjar/templates/tipjar/index.html b/lnbits/extensions/tipjar/templates/tipjar/index.html
new file mode 100644
index 000000000..7c58a74fd
--- /dev/null
+++ b/lnbits/extensions/tipjar/templates/tipjar/index.html
@@ -0,0 +1,447 @@
+{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
+%} {% block page %}
+
+
+
+
+ New TipJar
+
+
+
+
+
+
+
+
TipJars
+
+
+ Export to CSV
+
+
+
+ {% raw %}
+
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+
+
+
+
+ {{ col.value }}
+
+
+
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+
+
+
Tips
+
+
+ Export to CSV
+
+
+
+ {% raw %}
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+ {{ col.value }}
+
+
+
+
+
+
+ {% endraw %}
+
+
+
+
+
+
+
+
+ {{SITE_TITLE}} TipJar extension
+
+
+
+
+ {% include "tipjar/_api_docs.html" %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Watch-Only extension MUST be activated and have a wallet
+
+
+
+
+
+
+
+
+
+
+
+ Update TipJar
+
+ Create TipJar
+ Cancel
+
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(user) }}
+
+{% endblock %}
diff --git a/lnbits/extensions/tipjar/views.py b/lnbits/extensions/tipjar/views.py
new file mode 100644
index 000000000..7038862dc
--- /dev/null
+++ b/lnbits/extensions/tipjar/views.py
@@ -0,0 +1,48 @@
+from .crud import get_tipjar
+
+from http import HTTPStatus
+import httpx
+from collections import defaultdict
+from lnbits.decorators import check_user_exists
+
+from functools import wraps
+import hashlib
+from lnbits.core.services import check_invoice_status
+from lnbits.core.crud import update_payment_status, get_standalone_payment
+from fastapi import FastAPI, Request
+from fastapi.templating import Jinja2Templates
+from starlette.exceptions import HTTPException
+from starlette.responses import HTMLResponse
+from fastapi.params import Depends
+from fastapi.param_functions import Query
+import random
+
+from datetime import datetime
+from http import HTTPStatus
+from . import tipjar_ext, tipjar_renderer
+from lnbits.core.models import User, Payment
+
+templates = Jinja2Templates(directory="templates")
+
+
+@tipjar_ext.get("/")
+async def index(request: Request, user: User = Depends(check_user_exists)):
+ return tipjar_renderer().TemplateResponse(
+ "tipjar/index.html", {"request": request, "user": user.dict()}
+ )
+
+
+@tipjar_ext.get("/{tipjar_id}")
+async def tip(request: Request, tipjar_id: int = Query(None)):
+ """Return the donation form for the Tipjar corresponding to id"""
+ tipjar = await get_tipjar(tipjar_id)
+ print(tipjar_id)
+ if not tipjar:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="TipJar does not exist."
+ )
+
+ return tipjar_renderer().TemplateResponse(
+ "tipjar/display.html",
+ {"request": request, "donatee": tipjar.name, "tipjar": tipjar.id},
+ )
diff --git a/lnbits/extensions/tipjar/views_api.py b/lnbits/extensions/tipjar/views_api.py
new file mode 100644
index 000000000..2f89ec996
--- /dev/null
+++ b/lnbits/extensions/tipjar/views_api.py
@@ -0,0 +1,208 @@
+from http import HTTPStatus
+import json
+from fastapi import Request
+from fastapi.param_functions import Query
+from fastapi.params import Depends
+from lnurl.exceptions import InvalidUrl as LnurlInvalidUrl # type: ignore
+from starlette.exceptions import HTTPException
+
+from lnbits.decorators import (
+ WalletTypeInfo,
+ get_key_type,
+)
+from lnbits.core.crud import get_user
+
+from . import tipjar_ext
+from .helpers import get_charge_details
+from .crud import (
+ create_tipjar,
+ get_tipjar,
+ create_tip,
+ get_tipjars,
+ get_tip,
+ get_tips,
+ update_tip,
+ update_tipjar,
+ delete_tip,
+ delete_tipjar,
+)
+from ..satspay.crud import create_charge
+from .models import createTipJar, createTips, createTip
+
+
+@tipjar_ext.post("/api/v1/tipjars")
+async def api_create_tipjar(data: createTipJar):
+ """Create a tipjar, which holds data about how/where to post tips"""
+ try:
+ tipjar = await create_tipjar(data)
+ except Exception as e:
+ raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e))
+
+ return tipjar.dict()
+
+
+async def user_from_wallet(wallet: WalletTypeInfo = Depends(get_key_type)):
+ return wallet.wallet.user
+
+
+@tipjar_ext.post("/api/v1/tips")
+async def api_create_tip(data: createTips):
+ """Take data from tip form and return satspay charge"""
+ sats = data.sats
+ message = data.message
+ if not message:
+ message = "No message"
+ tipjar_id = data.tipjar
+ tipjar = await get_tipjar(tipjar_id)
+
+ webhook = tipjar.webhook
+ charge_details = await get_charge_details(tipjar.id)
+ print(charge_details["time"])
+ name = data.name
+ # Ensure that description string can be split reliably
+ name = name.replace('"', "''")
+ if not name:
+ name = "Anonymous"
+ description = f'"{name}": {message}'
+
+ charge = await create_charge(
+ user=charge_details["user"],
+ data={
+ "amount": sats,
+ "webhook": webhook,
+ "description": description,
+ "onchainwallet": charge_details["onchainwallet"],
+ "lnbitswallet": charge_details["lnbitswallet"],
+ "completelink": charge_details["completelink"],
+ "completelinktext": charge_details["completelinktext"],
+ "time": charge_details["time"],
+ },
+ )
+
+ await create_tip(
+ id=charge.id,
+ wallet=tipjar.wallet,
+ message=message,
+ name=name,
+ sats=data.sats,
+ tipjar=data.tipjar,
+ )
+
+ return {"redirect_url": f"/satspay/{charge.id}"}
+
+
+@tipjar_ext.get("/api/v1/tipjars")
+async def api_get_tipjars(wallet: WalletTypeInfo = Depends(get_key_type)):
+ """Return list of all tipjars assigned to wallet with given invoice key"""
+ wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
+ tipjars = []
+ for wallet_id in wallet_ids:
+ new_tipjars = await get_tipjars(wallet_id)
+ tipjars += new_tipjars if new_tipjars else []
+ return [tipjar._asdict() for tipjar in tipjars] if tipjars else []
+
+
+@tipjar_ext.get("/api/v1/tips")
+async def api_get_tips(wallet: WalletTypeInfo = Depends(get_key_type)):
+ """Return list of all tips assigned to wallet with given invoice key"""
+ wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids
+ tips = []
+ for wallet_id in wallet_ids:
+ new_tips = await get_tips(wallet_id)
+ tips += new_tips if new_tips else []
+ return [tip._asdict() for tip in tips] if tips else []
+
+
+@tipjar_ext.put("/api/v1/tips/{tip_id}")
+async def api_update_tip(
+ wallet: WalletTypeInfo = Depends(get_key_type), tip_id: str = Query(None)
+):
+ """Update a tip with the data given in the request"""
+ if tip_id:
+ tip = await get_tip(tip_id)
+
+ if not tip:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="Tip does not exist."
+ )
+
+ if tip.wallet != wallet.wallet.id:
+
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN, detail="Not your tip."
+ )
+
+ tip = await update_tip(tip_id, **g.data)
+ else:
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST, detail="No tip ID specified"
+ )
+ return tip.dict()
+
+
+@tipjar_ext.put("/api/v1/tipjars/{tipjar_id}")
+async def api_update_tipjar(
+ wallet: WalletTypeInfo = Depends(get_key_type), tipjar_id: str = Query(None)
+):
+ """Update a tipjar with the data given in the request"""
+ if tipjar_id:
+ tipjar = await get_tipjar(tipjar_id)
+
+ if not tipjar:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="TipJar does not exist."
+ )
+
+ if tipjar.wallet != wallet.wallet.id:
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN, detail="Not your tipjar."
+ )
+
+ tipjar = await update_tipjar(tipjar_id, **data)
+ else:
+ raise HTTPException(
+ status_code=HTTPStatus.BAD_REQUEST, detail="No tipjar ID specified"
+ )
+ return tipjar.dict()
+
+
+@tipjar_ext.delete("/api/v1/tips/{tip_id}")
+async def api_delete_tip(
+ wallet: WalletTypeInfo = Depends(get_key_type), tip_id: str = Query(None)
+):
+ """Delete the tip with the given tip_id"""
+ tip = await get_tip(tip_id)
+ if not tip:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND, detail="No tip with this ID!"
+ )
+ if tip.wallet != g.wallet.id:
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN,
+ detail="Not authorized to delete this tip!",
+ )
+ await delete_tip(tip_id)
+
+ return "", HTTPStatus.NO_CONTENT
+
+
+@tipjar_ext.delete("/api/v1/tipjars/{tipjar_id}")
+async def api_delete_tipjar(
+ wallet: WalletTypeInfo = Depends(get_key_type), tipjar_id: str = Query(None)
+):
+ """Delete the tipjar with the given tipjar_id"""
+ tipjar = await get_tipjar(tipjar_id)
+ if not tipjar:
+ raise HTTPException(
+ status_code=HTTPStatus.NOT_FOUND,
+ detail="No tipjar with this ID!",
+ )
+ if tipjar.wallet != g.wallet.id:
+
+ raise HTTPException(
+ status_code=HTTPStatus.FORBIDDEN,
+ detail="Not authorized to delete this tipjar!",
+ )
+ await delete_tipjar(tipjar_id)
+
+ return "", HTTPStatus.NO_CONTENT
diff --git a/lnbits/extensions/tpos/views_api.py b/lnbits/extensions/tpos/views_api.py
index 8d640a8a4..d7008c2cd 100644
--- a/lnbits/extensions/tpos/views_api.py
+++ b/lnbits/extensions/tpos/views_api.py
@@ -6,7 +6,7 @@ from starlette.exceptions import HTTPException
from lnbits.core.crud import get_user, get_wallet
from lnbits.core.services import check_invoice_status, create_invoice
-from lnbits.decorators import WalletTypeInfo, get_key_type
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import tpos_ext
from .crud import create_tpos, delete_tpos, get_tpos, get_tposs
@@ -33,7 +33,7 @@ async def api_tpos_create(
@tpos_ext.delete("/api/v1/tposs/{tpos_id}")
-async def api_tpos_delete(tpos_id: str, wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_tpos_delete(tpos_id: str, wallet: WalletTypeInfo = Depends(require_admin_key)):
tpos = await get_tpos(tpos_id)
if not tpos:
diff --git a/lnbits/extensions/watchonly/views_api.py b/lnbits/extensions/watchonly/views_api.py
index 61978b743..97641c7ff 100644
--- a/lnbits/extensions/watchonly/views_api.py
+++ b/lnbits/extensions/watchonly/views_api.py
@@ -4,13 +4,20 @@ from fastapi import Query
from fastapi.params import Depends
from starlette.exceptions import HTTPException
-from lnbits.core.crud import get_user
-from lnbits.decorators import WalletTypeInfo, get_key_type
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from lnbits.extensions.watchonly import watchonly_ext
-from .crud import (create_mempool, create_watch_wallet, delete_watch_wallet,
- get_addresses, get_fresh_address, get_mempool,
- get_watch_wallet, get_watch_wallets, update_mempool)
+from .crud import (
+ create_mempool,
+ create_watch_wallet,
+ delete_watch_wallet,
+ get_addresses,
+ get_fresh_address,
+ get_mempool,
+ get_watch_wallet,
+ get_watch_wallets,
+ update_mempool,
+)
from .models import CreateWallet
###################WALLETS#############################
@@ -41,7 +48,7 @@ async def api_wallet_retrieve(
@watchonly_ext.post("/api/v1/wallet")
async def api_wallet_create_or_update(
- data: CreateWallet, wallet_id=None, w: WalletTypeInfo = Depends(get_key_type)
+ data: CreateWallet, wallet_id=None, w: WalletTypeInfo = Depends(require_admin_key)
):
try:
wallet = await create_watch_wallet(
@@ -57,7 +64,7 @@ async def api_wallet_create_or_update(
@watchonly_ext.delete("/api/v1/wallet/{wallet_id}")
-async def api_wallet_delete(wallet_id, w: WalletTypeInfo = Depends(get_key_type)):
+async def api_wallet_delete(wallet_id, w: WalletTypeInfo = Depends(require_admin_key)):
wallet = await get_watch_wallet(wallet_id)
if not wallet:
@@ -105,14 +112,14 @@ async def api_get_addresses(wallet_id, w: WalletTypeInfo = Depends(get_key_type)
@watchonly_ext.put("/api/v1/mempool")
async def api_update_mempool(
- endpoint: str = Query(...), w: WalletTypeInfo = Depends(get_key_type)
+ endpoint: str = Query(...), w: WalletTypeInfo = Depends(require_admin_key)
):
mempool = await update_mempool(endpoint, user=w.wallet.user)
return mempool.dict()
@watchonly_ext.get("/api/v1/mempool")
-async def api_get_mempool(w: WalletTypeInfo = Depends(get_key_type)):
+async def api_get_mempool(w: WalletTypeInfo = Depends(require_admin_key)):
mempool = await get_mempool(w.wallet.user)
if not mempool:
mempool = await create_mempool(user=w.wallet.user)
diff --git a/lnbits/extensions/withdraw/views_api.py b/lnbits/extensions/withdraw/views_api.py
index 678346f95..d287b6cc6 100644
--- a/lnbits/extensions/withdraw/views_api.py
+++ b/lnbits/extensions/withdraw/views_api.py
@@ -7,20 +7,21 @@ from starlette.exceptions import HTTPException
from starlette.requests import Request
from lnbits.core.crud import get_user
-from lnbits.decorators import WalletTypeInfo, get_key_type
+from lnbits.decorators import WalletTypeInfo, get_key_type, require_admin_key
from . import withdraw_ext
-from .crud import (create_withdraw_link,
- delete_withdraw_link, get_hash_check, get_withdraw_link,
- get_withdraw_links, update_withdraw_link)
+from .crud import (
+ create_withdraw_link,
+ delete_withdraw_link,
+ get_hash_check,
+ get_withdraw_link,
+ get_withdraw_links,
+ update_withdraw_link,
+)
from .models import CreateWithdrawData
-# from fastapi import FastAPI, Query, Response
-
-
@withdraw_ext.get("/api/v1/links", status_code=HTTPStatus.OK)
-# @api_check_wallet_key("invoice")
async def api_links(
req: Request,
wallet: WalletTypeInfo = Depends(get_key_type),
@@ -42,58 +43,37 @@ async def api_links(
status_code=HTTPStatus.UPGRADE_REQUIRED,
detail="LNURLs need to be delivered over a publically accessible `https` domain or Tor.",
)
- # response.status_code = HTTPStatus.UPGRADE_REQUIRED
- # return { "message": "LNURLs need to be delivered over a publically accessible `https` domain or Tor." }
@withdraw_ext.get("/api/v1/links/{link_id}", status_code=HTTPStatus.OK)
-# @api_check_wallet_key("invoice")
-async def api_link_retrieve(link_id, wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_link_retrieve(link_id, request: Request, wallet: WalletTypeInfo = Depends(get_key_type)):
link = await get_withdraw_link(link_id, 0)
if not link:
raise HTTPException(
detail="Withdraw link does not exist.", status_code=HTTPStatus.NOT_FOUND
)
- # response.status_code = HTTPStatus.NOT_FOUND
- # return {"message": "Withdraw link does not exist."}
if link.wallet != wallet.wallet.id:
raise HTTPException(
detail="Not your withdraw link.", status_code=HTTPStatus.FORBIDDEN
)
- # response.status_code = HTTPStatus.FORBIDDEN
- # return {"message": "Not your withdraw link."}
return {**link, **{"lnurl": link.lnurl(request)}}
-# class CreateData(BaseModel):
-# title: str = Query(...)
-# min_withdrawable: int = Query(..., ge=1)
-# max_withdrawable: int = Query(..., ge=1)
-# uses: int = Query(..., ge=1)
-# wait_time: int = Query(..., ge=1)
-# is_unique: bool
-
-
@withdraw_ext.post("/api/v1/links", status_code=HTTPStatus.CREATED)
@withdraw_ext.put("/api/v1/links/{link_id}", status_code=HTTPStatus.OK)
-# @api_check_wallet_key("admin")
async def api_link_create_or_update(
req: Request,
data: CreateWithdrawData,
link_id: str = None,
- wallet: WalletTypeInfo = Depends(get_key_type),
+ wallet: WalletTypeInfo = Depends(require_admin_key),
):
if data.max_withdrawable < data.min_withdrawable:
raise HTTPException(
detail="`max_withdrawable` needs to be at least `min_withdrawable`.",
status_code=HTTPStatus.BAD_REQUEST,
)
- # response.status_code = HTTPStatus.BAD_REQUEST
- # return {
- # "message": "`max_withdrawable` needs to be at least `min_withdrawable`."
- # }
usescsv = ""
for i in range(data.uses):
@@ -109,50 +89,37 @@ async def api_link_create_or_update(
raise HTTPException(
detail="Withdraw link does not exist.", status_code=HTTPStatus.NOT_FOUND
)
- # response.status_code = HTTPStatus.NOT_FOUND
- # return {"message": "Withdraw link does not exist."}
if link.wallet != wallet.wallet.id:
raise HTTPException(
detail="Not your withdraw link.", status_code=HTTPStatus.FORBIDDEN
)
- # response.status_code = HTTPStatus.FORBIDDEN
- # return {"message": "Not your withdraw link."}
link = await update_withdraw_link(link_id, data=data, usescsv=usescsv, used=0)
else:
link = await create_withdraw_link(
wallet_id=wallet.wallet.id, data=data, usescsv=usescsv
)
- # if link_id:
- # response.status_code = HTTPStatus.OK
return {**link.dict(), **{"lnurl": link.lnurl(req)}}
@withdraw_ext.delete("/api/v1/links/{link_id}")
-# @api_check_wallet_key("admin")
-async def api_link_delete(link_id, wallet: WalletTypeInfo = Depends(get_key_type)):
+async def api_link_delete(link_id, wallet: WalletTypeInfo = Depends(require_admin_key)):
link = await get_withdraw_link(link_id)
if not link:
raise HTTPException(
detail="Withdraw link does not exist.", status_code=HTTPStatus.NOT_FOUND
)
- # response.status_code = HTTPStatus.NOT_FOUND
- # return {"message": "Withdraw link does not exist."}
if link.wallet != wallet.wallet.id:
raise HTTPException(
detail="Not your withdraw link.", status_code=HTTPStatus.FORBIDDEN
)
- # response.status_code = HTTPStatus.FORBIDDEN
- # return {"message": "Not your withdraw link."}
await delete_withdraw_link(link_id)
raise HTTPException(status_code=HTTPStatus.NO_CONTENT)
- # return ""
@withdraw_ext.get("/api/v1/links/{the_hash}/{lnurl_id}", status_code=HTTPStatus.OK)
-# @api_check_wallet_key("invoice")
async def api_hash_retrieve(
the_hash, lnurl_id, wallet: WalletTypeInfo = Depends(get_key_type)
):