diff --git a/lnbits/db.py b/lnbits/db.py
index f52b03914..321b23d0a 100644
--- a/lnbits/db.py
+++ b/lnbits/db.py
@@ -1,6 +1,7 @@
import asyncio
import datetime
import os
+import re
import time
from contextlib import asynccontextmanager
from typing import Optional
@@ -73,18 +74,39 @@ class Connection(Compat):
query = query.replace("?", "%s")
return query
+ def rewrite_values(self, values):
+ # strip html
+ CLEANR = re.compile("<.*?>|&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});")
+
+ def cleanhtml(raw_html):
+ if isinstance(raw_html, str):
+ cleantext = re.sub(CLEANR, "", raw_html)
+ return cleantext
+ else:
+ return raw_html
+
+ # tuple to list and back to tuple
+ values = tuple([cleanhtml(l) for l in list(values)])
+ return values
+
async def fetchall(self, query: str, values: tuple = ()) -> list:
- result = await self.conn.execute(self.rewrite_query(query), values)
+ result = await self.conn.execute(
+ self.rewrite_query(query), self.rewrite_values(values)
+ )
return await result.fetchall()
async def fetchone(self, query: str, values: tuple = ()):
- result = await self.conn.execute(self.rewrite_query(query), values)
+ result = await self.conn.execute(
+ self.rewrite_query(query), self.rewrite_values(values)
+ )
row = await result.fetchone()
await result.close()
return row
async def execute(self, query: str, values: tuple = ()):
- return await self.conn.execute(self.rewrite_query(query), values)
+ return await self.conn.execute(
+ self.rewrite_query(query), self.rewrite_values(values)
+ )
class Database(Compat):
diff --git a/lnbits/extensions/satspay/config.json b/lnbits/extensions/satspay/config.json
index beb0071cb..fe9e3df49 100644
--- a/lnbits/extensions/satspay/config.json
+++ b/lnbits/extensions/satspay/config.json
@@ -2,7 +2,5 @@
"name": "SatsPay Server",
"short_description": "Create onchain and LN charges",
"icon": "payment",
- "contributors": [
- "arcbtc"
- ]
+ "contributors": ["arcbtc"]
}
diff --git a/lnbits/extensions/satspay/crud.py b/lnbits/extensions/satspay/crud.py
index 968c9ab01..7e34f6f80 100644
--- a/lnbits/extensions/satspay/crud.py
+++ b/lnbits/extensions/satspay/crud.py
@@ -10,7 +10,7 @@ from lnbits.helpers import urlsafe_short_hash
from ..watchonly.crud import get_config, get_fresh_address
from . import db
from .helpers import fetch_onchain_balance
-from .models import Charges, CreateCharge
+from .models import Charges, CreateCharge, SatsPayThemes
###############CHARGES##########################
@@ -53,9 +53,10 @@ async def create_charge(user: str, data: CreateCharge) -> Charges:
time,
amount,
balance,
- extra
+ extra,
+ custom_css
)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
charge_id,
@@ -73,6 +74,7 @@ async def create_charge(user: str, data: CreateCharge) -> Charges:
data.amount,
0,
data.extra,
+ data.custom_css,
),
)
return await get_charge(charge_id)
@@ -121,3 +123,101 @@ async def check_address_balance(charge_id: str) -> Optional[Charges]:
if invoice_status["paid"]:
return await update_charge(charge_id=charge_id, balance=charge.amount)
return await get_charge(charge_id)
+
+
+################## SETTINGS ###################
+
+
+async def save_theme(data: SatsPayThemes, css_id: str = None):
+ # insert or update
+ if css_id:
+ await db.execute(
+ """
+ UPDATE satspay.themes SET custom_css = ?, title = ? WHERE css_id = ?
+ """,
+ (data.custom_css, data.title, css_id),
+ )
+ else:
+ css_id = urlsafe_short_hash()
+ await db.execute(
+ """
+ INSERT INTO satspay.themes (
+ css_id,
+ title,
+ user,
+ custom_css
+ )
+ VALUES (?, ?, ?, ?)
+ """,
+ (
+ css_id,
+ data.title,
+ data.user,
+ data.custom_css,
+ ),
+ )
+ return await get_theme(css_id)
+
+
+async def get_theme(css_id: str) -> SatsPayThemes:
+ row = await db.fetchone("SELECT * FROM satspay.themes WHERE css_id = ?", (css_id,))
+ return SatsPayThemes.from_row(row) if row else None
+
+
+async def get_themes(user_id: str) -> List[SatsPayThemes]:
+ rows = await db.fetchall(
+ """SELECT * FROM satspay.themes WHERE "user" = ? ORDER BY "timestamp" DESC """,
+ (user_id,),
+ )
+ return await get_config(row.user)
+
+
+################## SETTINGS ###################
+
+
+async def save_theme(data: SatsPayThemes, css_id: str = None):
+ # insert or update
+ if css_id:
+ await db.execute(
+ """
+ UPDATE satspay.themes SET custom_css = ?, title = ? WHERE css_id = ?
+ """,
+ (data.custom_css, data.title, css_id),
+ )
+ else:
+ css_id = urlsafe_short_hash()
+ await db.execute(
+ """
+ INSERT INTO satspay.themes (
+ css_id,
+ title,
+ "user",
+ custom_css
+ )
+ VALUES (?, ?, ?, ?)
+ """,
+ (
+ css_id,
+ data.title,
+ data.user,
+ data.custom_css,
+ ),
+ )
+ return await get_theme(css_id)
+
+
+async def get_theme(css_id: str) -> SatsPayThemes:
+ row = await db.fetchone("SELECT * FROM satspay.themes WHERE css_id = ?", (css_id,))
+ return SatsPayThemes.from_row(row) if row else None
+
+
+async def get_themes(user_id: str) -> List[SatsPayThemes]:
+ rows = await db.fetchall(
+ """SELECT * FROM satspay.themes WHERE "user" = ? ORDER BY "title" DESC """,
+ (user_id,),
+ )
+ return [SatsPayThemes.from_row(row) for row in rows]
+
+
+async def delete_theme(theme_id: str) -> None:
+ await db.execute("DELETE FROM satspay.themes WHERE css_id = ?", (theme_id,))
diff --git a/lnbits/extensions/satspay/helpers.py b/lnbits/extensions/satspay/helpers.py
index 2aa83e1f5..60c5ba4ab 100644
--- a/lnbits/extensions/satspay/helpers.py
+++ b/lnbits/extensions/satspay/helpers.py
@@ -19,10 +19,12 @@ def public_charge(charge: Charges):
"time_elapsed": charge.time_elapsed,
"time_left": charge.time_left,
"paid": charge.paid,
+ "custom_css": charge.custom_css,
}
if charge.paid:
c["completelink"] = charge.completelink
+ c["completelinktext"] = charge.completelinktext
return c
diff --git a/lnbits/extensions/satspay/migrations.py b/lnbits/extensions/satspay/migrations.py
index 2579961f5..ff6b44be8 100644
--- a/lnbits/extensions/satspay/migrations.py
+++ b/lnbits/extensions/satspay/migrations.py
@@ -37,3 +37,28 @@ async def m002_add_charge_extra_data(db):
ADD COLUMN extra TEXT DEFAULT '{"mempool_endpoint": "https://mempool.space", "network": "Mainnet"}';
"""
)
+
+
+async def m003_add_themes_table(db):
+ """
+ Themes table
+ """
+
+ await db.execute(
+ """
+ CREATE TABLE satspay.themes (
+ css_id TEXT NOT NULL PRIMARY KEY,
+ "user" TEXT,
+ title TEXT,
+ custom_css TEXT
+ );
+ """
+ )
+
+
+async def m004_add_custom_css_to_charges(db):
+ """
+ Add custom css option column to the 'charges' table
+ """
+
+ await db.execute("ALTER TABLE satspay.charges ADD COLUMN custom_css TEXT;")
diff --git a/lnbits/extensions/satspay/models.py b/lnbits/extensions/satspay/models.py
index 1e7c95c99..cfb3c7aca 100644
--- a/lnbits/extensions/satspay/models.py
+++ b/lnbits/extensions/satspay/models.py
@@ -14,6 +14,7 @@ class CreateCharge(BaseModel):
webhook: str = Query(None)
completelink: str = Query(None)
completelinktext: str = Query(None)
+ custom_css: Optional[str]
time: int = Query(..., ge=1)
amount: int = Query(..., ge=1)
extra: str = "{}"
@@ -38,6 +39,7 @@ class Charges(BaseModel):
completelink: Optional[str]
completelinktext: Optional[str] = "Back to Merchant"
extra: str = "{}"
+ custom_css: Optional[str]
time: int
amount: int
balance: int
@@ -72,3 +74,14 @@ class Charges(BaseModel):
def must_call_webhook(self):
return self.webhook and self.paid and self.config.webhook_success == False
+
+
+class SatsPayThemes(BaseModel):
+ css_id: str = Query(None)
+ title: str = Query(None)
+ custom_css: str = Query(None)
+ user: Optional[str]
+
+ @classmethod
+ def from_row(cls, row: Row) -> "SatsPayThemes":
+ return cls(**dict(row))
diff --git a/lnbits/extensions/satspay/static/js/utils.js b/lnbits/extensions/satspay/static/js/utils.js
index 929279554..2b1be8bdc 100644
--- a/lnbits/extensions/satspay/static/js/utils.js
+++ b/lnbits/extensions/satspay/static/js/utils.js
@@ -26,5 +26,10 @@ const mapCharge = (obj, oldObj = {}) => {
return charge
}
+const mapCSS = (obj, oldObj = {}) => {
+ const theme = _.clone(obj)
+ return theme
+}
+
const minutesToTime = min =>
min > 0 ? new Date(min * 1000).toISOString().substring(14, 19) : ''
diff --git a/lnbits/extensions/satspay/templates/satspay/_api_docs.html b/lnbits/extensions/satspay/templates/satspay/_api_docs.html
index ed6587357..6d5ae661e 100644
--- a/lnbits/extensions/satspay/templates/satspay/_api_docs.html
+++ b/lnbits/extensions/satspay/templates/satspay/_api_docs.html
@@ -5,7 +5,13 @@
WatchOnly extension, we highly reccomend using a fresh extended public Key
specifically for SatsPayServer!
- Created by, Ben ArcBen Arc,
+ motorina0