commit
f24b1a5d19
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "LNEVENTS",
|
||||
"short_description": "Make LNURL withdraw links.",
|
||||
"ion_icon": "calendar-outline"
|
||||
"ion_icon": "calendar"
|
||||
}
|
|
@ -6,15 +6,16 @@ CREATE TABLE IF NOT EXISTS events (
|
|||
walinvkey INTEGER,
|
||||
uni TEXT,
|
||||
tit TEXT,
|
||||
amt INTEGER,
|
||||
sold INTEGER,
|
||||
dat TEXT,
|
||||
tme TEXT,
|
||||
price INTEGER
|
||||
cldate TEXT,
|
||||
notickets INTEGER,
|
||||
sold INTEGER DEFAULT 0,
|
||||
prtick INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS eventssold (
|
||||
key INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
uni TEXT,
|
||||
email TEXT,
|
||||
name TEXT,
|
||||
hash TEXT
|
||||
);
|
||||
);
|
||||
|
|
|
@ -53,8 +53,8 @@
|
|||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Withdraw link maker
|
||||
<small>powered by LNURL</small>
|
||||
Events
|
||||
<small>bitcoin tickets</small>
|
||||
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
|
@ -70,7 +70,14 @@
|
|||
</ol>
|
||||
<br /><br />
|
||||
</section>
|
||||
<style>
|
||||
#ui-datepicker-div{
|
||||
|
||||
background-color: #1f2234;
|
||||
padding: 0 10px 10px 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<!-- Small boxes (Stat box) -->
|
||||
|
@ -80,7 +87,7 @@
|
|||
<!-- general form elements -->
|
||||
<div class="box box-primary">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title"> Make a wave</h3>
|
||||
<h3 class="box-title"> Make a eve</h3>
|
||||
</div><!-- /.box-header -->
|
||||
<!-- form start -->
|
||||
<form role="form">
|
||||
|
@ -104,17 +111,24 @@
|
|||
|
||||
<div class="form-group">
|
||||
<label for="nooftickets">No. of tickets</label>
|
||||
<input id="notickets" type="number" class="form-control" placeholder="1" max="86400"></input>
|
||||
<input id="notickets" type="number" class="form-control" placeholder="10" max="86400"></input>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="nooftickets">Date</label>
|
||||
<input type="text" class="form-control" data-inputmask="'alias': 'dd/mm/yyyy'" data-mask/>
|
||||
</div><!-- /.input group -->
|
||||
<div class="form-group">
|
||||
<label>Close date:</label>
|
||||
|
||||
<div class="input-group date">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-calendar"></i>
|
||||
</div>
|
||||
<input type="text" class="form-control pull-right" id="datepicker">
|
||||
</div>
|
||||
<!-- /.input group -->
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="prpertick">Price per ticket</label>
|
||||
<input id="prtickets" type="number" class="form-control" placeholder="1"></input>
|
||||
<input id="prtickets" type="number" class="form-control" placeholder="10"></input>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -122,7 +136,7 @@
|
|||
|
||||
<div class="box-footer">
|
||||
|
||||
<button onclick="postwave()" type="button" class="btn btn-info">Create wave</button><p style="color:red;" id="error"></p>
|
||||
<button onclick="postev()" type="button" class="btn btn-info">Create Wave</button><p style="color:red;" id="error"></p>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /.box -->
|
||||
|
@ -136,7 +150,7 @@
|
|||
<div class="col-md-6">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">Ticket waves<b id="withdraws"></b></h3>
|
||||
<h3 class="box-title">Ticket Waves<b id="withdraws"></b></h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body no-padding">
|
||||
|
@ -145,7 +159,7 @@
|
|||
<th>Title</th>
|
||||
<th style="width:15%">Amt</th>
|
||||
<th style="width:15%">Sold</th>
|
||||
<th style="width:15%">Date</th>
|
||||
<th style="width:15%">Closing</th>
|
||||
<th style="width:15%">Price</th>
|
||||
<th style="width:15%">Wallet</th>
|
||||
<th style="width:10%">Edit</th>
|
||||
|
@ -168,36 +182,46 @@
|
|||
</section>
|
||||
|
||||
<script>
|
||||
|
||||
//Date picker
|
||||
$('#datepicker').datepicker({
|
||||
autoclose: true
|
||||
})
|
||||
|
||||
|
||||
|
||||
window.user = {{ user | megajson | safe }}
|
||||
window.user_wallets = {{ user_wallets | megajson | safe }}
|
||||
window.user_ext = {{ user_ext | megajson | safe }}
|
||||
window.user_wav = {{ user_fau | megajson | safe }}
|
||||
window.user_ev = {{ user_ev | megajson | safe }}
|
||||
|
||||
const user_wav = window.user_wav
|
||||
console.log(user_wav)
|
||||
const user_ev = window.user_ev
|
||||
console.log(user_ev)
|
||||
|
||||
|
||||
function drawChart(user_wav) {
|
||||
function drawChart(user_ev) {
|
||||
var transactionsHTML = ''
|
||||
|
||||
for (var i = 0; i < user_wav.length; i++) {
|
||||
var wv = user_wav[i]
|
||||
for (var i = 0; i < user_ev.length; i++) {
|
||||
var ev = user_ev[i]
|
||||
|
||||
transactionsHTML =
|
||||
"<tr><td style='width: 50%'>" +
|
||||
wv.tit +
|
||||
ev.tit +
|
||||
'</td><td>' +
|
||||
wv.nosold +
|
||||
ev.notickets +
|
||||
'</td><td>' +
|
||||
wv.noavail +
|
||||
ev.sold +
|
||||
'</td><td>' +
|
||||
wv.prpertick +
|
||||
ev.cldate +
|
||||
'</td><td>' +
|
||||
"<a href='{{ url_for('wallet') }}?usr="+ user +"'>" + wv.uni.substring(0, 4) + "...</a>" +
|
||||
ev.prtick +
|
||||
'</td><td>' +
|
||||
"<a href='{{ url_for('wallet') }}?usr="+ ev.usr +"'>" + ev.wal.substring(0, 4) + "...</a>" +
|
||||
'</td><td>' +
|
||||
"<i onclick='editlink("+ i +")'' class='fa fa-edit'></i>" +
|
||||
'</td><td>' +
|
||||
"<b><a style='color:red;' href='" + "{{ url_for('withdraw.index') }}?del=" + wv.uni + "&usr=" + user +"'>" + "<i class='fa fa-trash'></i>" + "</a></b>" +
|
||||
"<b><a style='color:red;' href='" + "{{ url_for('withdraw.index') }}?del=" + ev.uni + "&usr=" + ev.usr +"'>" + "<i class='fa fa-trash'></i>" + "</a></b>" +
|
||||
'</td></tr>' +
|
||||
transactionsHTML
|
||||
document.getElementById('ticketwaves').innerHTML = transactionsHTML
|
||||
|
@ -205,12 +229,17 @@ function drawChart(user_wav) {
|
|||
}
|
||||
}
|
||||
|
||||
function postwav(){
|
||||
if (user_ev.length) {
|
||||
drawChart(user_ev)
|
||||
}
|
||||
|
||||
function postev(){
|
||||
|
||||
wal = document.getElementById('wal').value
|
||||
tit = document.getElementById('tit').value
|
||||
nooftickets = document.getElementById('nooftickets').value
|
||||
prtick = document.getElementById('prtick').value
|
||||
cldate = document.getElementById('datepicker').value
|
||||
notickets = document.getElementById('notickets').value
|
||||
prtickets = document.getElementById('prtickets').value
|
||||
|
||||
if (tit == "") {
|
||||
document.getElementById("error").innerHTML = "Only use letters in title"
|
||||
|
@ -220,36 +249,40 @@ function postwav(){
|
|||
document.getElementById("error").innerHTML = "No wallet selected"
|
||||
return amt
|
||||
}
|
||||
if (cldate == "") {
|
||||
document.getElementById("error").innerHTML = "No date selected"
|
||||
return amt
|
||||
}
|
||||
|
||||
if (isNaN(notickets) || notickets < 1) {
|
||||
document.getElementById("error").innerHTML = "Must be more than 1"
|
||||
return amt
|
||||
}
|
||||
if (isNaN(prpertickets) || prpertickets < 10) {
|
||||
if (isNaN(prtickets) || prtickets < 10) {
|
||||
document.getElementById("error").innerHTML = "Must be higher 10"
|
||||
return amt
|
||||
}
|
||||
|
||||
postAjax(
|
||||
"{{ url_for('wave.create') }}",
|
||||
JSON.stringify({"tit": tit, "nooftickets": nooftickets, "nooftickets": nooftickets, "prtick": prtick}),
|
||||
"{{ url_for('events.create') }}",
|
||||
JSON.stringify({"tit": tit, "usr": user, "wal": wal, "notickets": notickets,"cldate": cldate, "prtickets": prtickets}),
|
||||
"filla",
|
||||
|
||||
function(data) { location.replace("{{ url_for('wav.index') }}?usr=" + user)
|
||||
function(data) { location.replace("{{ url_for('events.index') }}?usr=" + user)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
function editlink(wavnum){
|
||||
function editlink(evnum){
|
||||
|
||||
wavdetails = user_wav[wavnum]
|
||||
evdetails = user_ev[evnum]
|
||||
|
||||
console.log(wavdetails)
|
||||
console.log(evdetails)
|
||||
wallpick = ""
|
||||
|
||||
checkbox = ""
|
||||
if (wavdetails.uniq == 1){
|
||||
if (evdetails.uniq == 1){
|
||||
checkbox = "checked"}
|
||||
|
||||
document.getElementById('editlink').innerHTML = "<div class='row'>"+
|
||||
|
@ -257,7 +290,7 @@ document.getElementById('editlink').innerHTML = "<div class='row'>"+
|
|||
" <!-- general form elements -->"+
|
||||
"<div class='box box-primary'>"+
|
||||
"<div class='box-header'>"+
|
||||
"<h3 class='box-title'> Edit: <i id='unid'>" + wavdetails.tit + "-" + wavdetails.uni + "</i> </h3>"+
|
||||
"<h3 class='box-title'> Edit: <i id='unid'>" + evdetails.tit + "-" + evdetails.uni + "</i> </h3>"+
|
||||
"<div class='box-tools pull-right'>" +
|
||||
"<button class='btn btn-box-tool' data-widget='remove'><i class='fa fa-times'></i></button>" +
|
||||
"</div>" +
|
||||
|
@ -269,7 +302,7 @@ document.getElementById('editlink').innerHTML = "<div class='row'>"+
|
|||
"<div class='form-group'>"+
|
||||
"<label for='exampleInputEmail1'>Link title</label>"+
|
||||
"<input id='edittit' type='text' class='form-control' value='"+
|
||||
wavdetails.tit +
|
||||
evdetails.tit +
|
||||
"'></input> </div>"+
|
||||
" </div>"+
|
||||
" <div class='col-sm-4 col-md-4'>"+
|
||||
|
@ -277,7 +310,7 @@ document.getElementById('editlink').innerHTML = "<div class='row'>"+
|
|||
" <div class='form-group'>"+
|
||||
" <label>Select a wallet</label>"+
|
||||
"<select id='editwal' class='form-control'>"+
|
||||
" <option>" + wavdetails.walnme + "-" + wavdetails.wal + "</option>"+
|
||||
" <option>" + evdetails.walnme + "-" + evdetails.wal + "</option>"+
|
||||
" {% for w in user_wallets %}"+
|
||||
|
||||
" <option>{{w.name}}-{{w.id}}</option>"+
|
||||
|
@ -289,58 +322,48 @@ document.getElementById('editlink').innerHTML = "<div class='row'>"+
|
|||
" <div class='col-sm-3 col-md-4'>"+
|
||||
"<div class='form-group'>"+
|
||||
" <label for='exampleInputPassword1'>No of tickets:</label>"+
|
||||
" <input id='edittme' type='number' class='form-control' placeholder='0' max='86400' value='"+
|
||||
wavdetails.notickets +
|
||||
" <input id='editnooftickets' type='number' class='form-control' placeholder='0' max='86400' value='"+
|
||||
evdetails.notickets +
|
||||
"'></input>"+
|
||||
"</div> </div>"+
|
||||
" <div class='col-sm-3 col-md-4'>"+
|
||||
"<div class='form-group'>"+
|
||||
"<label for='exampleInputEmail1'>Price per ticket:</label>"+
|
||||
" <input id='editmaxamt' type='number' class='form-control' placeholder='1' value='"+
|
||||
wavdetails.prperticket +
|
||||
" <input id='editprtick' type='number' class='form-control' placeholder='1' value='"+
|
||||
evdetails.prtick +
|
||||
"'></input>"+
|
||||
" </div></div>"+
|
||||
" <div class='col-sm-3 col-md-4'>"+
|
||||
" <div class='form-group'>"+
|
||||
" <div class='input-group date'>"+
|
||||
" <label for='exampleInputEmail1'>Close date:</label>"+
|
||||
" <input id='editminamt' type='number' class='form-control' placeholder='1' value='"+
|
||||
wavdetails.minamt +
|
||||
" <input id='datepicker2' type='text' class='form-control' placeholder='1' value='"+
|
||||
evdetails.cldate +
|
||||
"'></input>"+
|
||||
" </div></div>"+
|
||||
" <div class='col-sm-3 col-md-4'>"+
|
||||
"<div class='form-group'>"+
|
||||
" <label for='exampleInputPassword1'>Amount of uses:</label>"+
|
||||
" <input id='editamt' type='number' class='form-control' placeholder='1' value='"+
|
||||
wavdetails.inc +
|
||||
"'></input>"+
|
||||
" </div> </div>"+
|
||||
" <div class='col-sm-3 col-md-4'>"+
|
||||
" <div class='checkbox'>"+
|
||||
"<label data-toggle='tooltip' title='Some tooltip text!'><input id='edituniq' type='checkbox' "+
|
||||
checkbox +
|
||||
">"+
|
||||
"Unique links</label>"+
|
||||
"</div></div><!-- /.box-body -->"+
|
||||
"</div><!-- /.box-body -->"+
|
||||
" </div><br/>"+
|
||||
" <div class='box-footer'>"+
|
||||
" <button onclick='editlinkcont()' type='button' class='btn btn-info'>Edit link(s)</button><p style='color:red;' id='error2'></p>"+
|
||||
" <button onclick='editlinkcont()' type='button' style='margin: 24px;' class='btn btn-info'>Edit link(s)</button><p style='color:red;' id='error2'></p>"+
|
||||
" </div></form></div><!-- /.box --></div></div>"
|
||||
|
||||
|
||||
}
|
||||
|
||||
//Date picker
|
||||
$('#datepicker2').datepicker({
|
||||
autoclose: true
|
||||
})
|
||||
|
||||
function editlinkcont(){
|
||||
|
||||
unid = document.getElementById('unid').innerHTML
|
||||
wal = document.getElementById('editwal').value
|
||||
tit = document.getElementById('edittit').value
|
||||
amt = document.getElementById('editamt').value
|
||||
maxamt = document.getElementById('editmaxamt').value
|
||||
minamt = document.getElementById('editminamt').value
|
||||
tme = document.getElementById('edittme').value
|
||||
uniq = document.getElementById('edituniq').checked
|
||||
|
||||
|
||||
nooftickets = document.getElementById('editnooftickets').value
|
||||
prtick = document.getElementById('editprtick').value
|
||||
cldate = document.getElementById('datepicker2').value
|
||||
uni = unid.split("-")[1]
|
||||
|
||||
if (tit == "") {
|
||||
document.getElementById("error2").innerHTML = "Only use letters in title"
|
||||
|
@ -351,38 +374,29 @@ function editlinkcont(){
|
|||
return amt
|
||||
}
|
||||
|
||||
if (isNaN(maxamt) || maxamt < 10 || maxamt > 1000000) {
|
||||
document.getElementById("error2").innerHTML = "Max 10 - 1000000 and must be higher than min"
|
||||
if (isNaN(nooftickets) || nooftickets < 1 || nooftickets > 1000000) {
|
||||
document.getElementById("error2").innerHTML = "No. of tickets must be between 1 - 1000000"
|
||||
return amt
|
||||
}
|
||||
if (isNaN(minamt) || minamt < 1 || minamt > 1000000 || minamt > maxamt) {
|
||||
document.getElementById("error2").innerHTML = "Min 1 - 1000000 and must be lower than max"
|
||||
return amt
|
||||
}
|
||||
|
||||
if (isNaN(amt) || amt < 1 || amt > 1000) {
|
||||
document.getElementById("error2").innerHTML = "Amount of uses must be between 1 - 1000"
|
||||
return amt
|
||||
}
|
||||
|
||||
if (isNaN(tme) || tme < 1 || tme > 86400) {
|
||||
document.getElementById("error2").innerHTML = "Max waiting time 1 day (86400 secs)"
|
||||
if (isNaN(prtick) || prtick < 10 ) {
|
||||
document.getElementById("error2").innerHTML = "Ticket pricket must be higher than 10"
|
||||
return amt
|
||||
}
|
||||
|
||||
|
||||
postAjax(
|
||||
"{{ url_for('withdraw.create') }}",
|
||||
JSON.stringify({"id": unid, "tit": tit, "amt": amt, "maxamt": maxamt, "minamt": minamt, "tme": tme, "wal": wal, "usr": user, "uniq": uniq}),
|
||||
"{{ url_for('events.create') }}",
|
||||
JSON.stringify({"tit": tit, "usr": user, "wal": wal, "notickets": nooftickets,"cldate": cldate, "prtickets": prtick, "id": unid}),
|
||||
|
||||
"filla",
|
||||
|
||||
function(data) { location.replace("{{ url_for('withdraw.index') }}?usr=" + user)
|
||||
function(data) { location.replace("{{ url_for('events.index') }}?usr=" + user)
|
||||
})
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -10,8 +10,98 @@ from lnbits.extensions.events import events_ext
|
|||
@events_ext.route("/")
|
||||
def index():
|
||||
"""Main events link page."""
|
||||
usr = request.args.get("usr")
|
||||
|
||||
if usr:
|
||||
if not len(usr) > 20:
|
||||
return redirect(url_for("home"))
|
||||
|
||||
# Get all the data
|
||||
with open_db() as db:
|
||||
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (usr,))
|
||||
user_ext = db.fetchall("SELECT * FROM extensions WHERE user = ?", (usr,))
|
||||
user_ext = [v[0] for v in user_ext]
|
||||
|
||||
with open_ext_db("events") as events_ext_db:
|
||||
user_ev = events_ext_db.fetchall("SELECT * FROM events WHERE usr = ?", (usr,))
|
||||
|
||||
# If del is selected by user from events page, the event link is to be deleted
|
||||
evdel = request.args.get("del")
|
||||
if evdel:
|
||||
events_ext_db.execute("DELETE FROM events WHERE uni = ?", (evdel,))
|
||||
user_ev = events_ext_db.fetchall("SELECT * FROM events WHERE usr = ?", (usr,))
|
||||
print(user_ext)
|
||||
|
||||
return render_template(
|
||||
"events/index.html"
|
||||
"events/index.html", user_wallets=user_wallets, user=usr, user_ext=user_ext, user_ev=user_ev
|
||||
)
|
||||
|
||||
@events_ext.route("/create", methods=["GET", "POST"])
|
||||
def create():
|
||||
"""."""
|
||||
|
||||
data = request.json
|
||||
tit = data["tit"]
|
||||
wal = data["wal"]
|
||||
cldate = data["cldate"]
|
||||
notickets = data["notickets"]
|
||||
prtick = data["prtickets"]
|
||||
usr = data["usr"]
|
||||
wall = wal.split("-")
|
||||
|
||||
# Form validation
|
||||
if (
|
||||
not tit.replace(" ", "").isalnum()
|
||||
or wal == ""
|
||||
or int(notickets) < 0
|
||||
or int(prtick) < 0
|
||||
):
|
||||
return jsonify({"ERROR": "FORM ERROR"}), 401
|
||||
|
||||
# If id that means its a link being edited, delete the record first
|
||||
if "id" in data:
|
||||
unid = data["id"].split("-")
|
||||
uni = unid[1]
|
||||
with open_ext_db("events") as events_ext_db:
|
||||
events_ext_db.execute("DELETE FROM events WHERE uni = ?", (unid[1],))
|
||||
else:
|
||||
uni = uuid.uuid4().hex
|
||||
|
||||
with open_db() as dbb:
|
||||
user_wallets = dbb.fetchall("SELECT * FROM wallets WHERE user = ? AND id = ?", (usr, wall[1],))
|
||||
if not user_wallets:
|
||||
return jsonify({"ERROR": "NO WALLET USER"}), 401
|
||||
|
||||
with open_db() as db:
|
||||
user_ext = db.fetchall("SELECT * FROM extensions WHERE user = ?", (usr,))
|
||||
user_ext = [v[0] for v in user_ext]
|
||||
|
||||
# Add to DB
|
||||
with open_ext_db("events") as events_ext_db:
|
||||
events_ext_db.execute(
|
||||
"""
|
||||
INSERT OR IGNORE INTO events
|
||||
(usr, wal, walnme, walinvkey, uni, tit, cldate, notickets, prtick)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
usr,
|
||||
wall[1],
|
||||
user_wallets[0][1],
|
||||
user_wallets[0][4],
|
||||
uni,
|
||||
tit,
|
||||
cldate,
|
||||
notickets,
|
||||
prtick,
|
||||
),
|
||||
)
|
||||
|
||||
user_ev = events_ext_db.fetchall("SELECT * FROM events WHERE usr = ?", (usr,))
|
||||
|
||||
if not user_ev:
|
||||
return jsonify({"ERROR": "NO WALLET USER"}), 401
|
||||
|
||||
return render_template(
|
||||
"events/index.html", user_wallets=user_wallets, user=usr, user_ext=user_ext, user_ev=user_ev
|
||||
)
|
||||
|
|
|
@ -5,5 +5,7 @@ This is an example extension to help you organise and build you own.
|
|||
Try to include an image
|
||||
<img src="https://i.imgur.com/9i4xcQB.png">
|
||||
|
||||
<h2>If your extension has API endpoints, include any useful ones here</h2>
|
||||
|
||||
<h2>If your extension has API endpoints, include useful ones here</h2>
|
||||
|
||||
<code>curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "Grpc-Metadata-macaroon: YOUR_WALLET-ADMIN/INVOICE-KEY"</code>
|
||||
|
|
8
lnbits/extensions/example/__init__.py
Normal file
8
lnbits/extensions/example/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from flask import Blueprint
|
||||
|
||||
|
||||
example_ext = Blueprint("example", __name__, static_folder="static", template_folder="templates")
|
||||
|
||||
|
||||
from .views_api import * # noqa
|
||||
from .views import * # noqa
|
5
lnbits/extensions/example/example.config.json
Normal file
5
lnbits/extensions/example/example.config.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "SHORT-NAME-FOR-EXTENSIONS-PAGE",
|
||||
"short_description": "BLah blah blah.",
|
||||
"ion_icon": "calendar"
|
||||
}
|
7
lnbits/extensions/example/schema.sql
Normal file
7
lnbits/extensions/example/schema.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
/* create your extensions table and the variables needed here */
|
||||
CREATE TABLE IF NOT EXISTS example (
|
||||
key INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
usr TEXT,
|
||||
wal TEXT,
|
||||
walnme TEXT
|
||||
);
|
102
lnbits/extensions/example/templates/example/index.html
Normal file
102
lnbits/extensions/example/templates/example/index.html
Normal file
|
@ -0,0 +1,102 @@
|
|||
<!-- @format -->
|
||||
|
||||
{% extends "base.html" %} {% block messages %}
|
||||
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-bell-o"></i>
|
||||
<span class="label label-danger">!</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="header"><b>Instant wallet, bookmark to save</b></li>
|
||||
<li></li>
|
||||
</ul>
|
||||
{% endblock %} {% block menuitems %}
|
||||
<li class="treeview">
|
||||
<a href="#">
|
||||
<i class="fa fa-bitcoin"></i> <span>Wallets</span>
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
{% for w in user_wallets %}
|
||||
<li>
|
||||
<a href="{{ url_for('wallet') }}?wal={{ w.id }}&usr={{ w.user }}"
|
||||
><i class="fa fa-bolt"></i> {{ w.name }}</a
|
||||
>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li><a onclick="sidebarmake()">Add a wallet +</a></li>
|
||||
<div id="sidebarmake"></div>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="active treeview">
|
||||
<a href="#">
|
||||
<i class="fa fa-th"></i> <span>Extensions</span>
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
{% for extension in EXTENSIONS %}
|
||||
{% if extension.code in user_ext %}
|
||||
<li>
|
||||
<a href="{{ url_for(extension.code + '.index') }}?usr={{ user }}"><i class="fa fa-plus"></i> {{ extension.name }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<li>
|
||||
<a href="{{ url_for('extensions') }}?usr={{ user }}">Manager </a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
{% endblock %} {% block body %}
|
||||
<!-- Right side column. Contains the navbar and content of the page -->
|
||||
<div class="content-wrapper">
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Withdraw link maker
|
||||
<small>powered by LNURL</small>
|
||||
|
||||
</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li>
|
||||
<a href="{{ url_for('wallet') }}?usr={{ user }}"><i class="fa fa-dashboard"></i> Home</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('extensions') }}?usr={{ user }}"><li class="fa fa-dashboard">Extensions</li></a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="active" class="fa fa-dashboard">example</i>
|
||||
</li>
|
||||
</ol>
|
||||
<br /><br />
|
||||
|
||||
|
||||
|
||||
<!-- DOWNLOAD AND SEARCH ADMINLITE2 FOR HTML-->
|
||||
|
||||
</section>
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<!-- Small boxes (Stat box) -->
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<!-- general form elements -->
|
||||
<div class="box box-primary">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title"> EXAMPLE BOX HEADING</h3>
|
||||
</div><!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<p>*Some content in here</p>
|
||||
</div>
|
||||
</div><!-- /.box -->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
|
||||
//ADD YOUR JAVASCIPT HERE//
|
||||
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
14
lnbits/extensions/example/views.py
Normal file
14
lnbits/extensions/example/views.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
#add your dependencies here
|
||||
|
||||
from flask import jsonify, render_template, request, redirect, url_for
|
||||
from lnbits.db import open_db, open_ext_db
|
||||
from lnbits.extensions.example import example_ext
|
||||
|
||||
#add your endpoints here
|
||||
|
||||
@example_ext.route("/")
|
||||
def index():
|
||||
"""Try to add descriptions for others."""
|
||||
return render_template(
|
||||
"example/index.html"
|
||||
)
|
18
lnbits/extensions/example/views_api.py
Normal file
18
lnbits/extensions/example/views_api.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
#views_api.py is for you API endpoints that could be hit by another service
|
||||
|
||||
#add your dependencies here
|
||||
|
||||
import json
|
||||
import requests
|
||||
from flask import jsonify, render_template, request, redirect, url_for
|
||||
from lnbits.db import open_db, open_ext_db
|
||||
from lnbits.extensions.example import example_ext
|
||||
|
||||
#add your endpoints here
|
||||
|
||||
@example_ext.route("/api/v1/example", methods=["GET","POST"])
|
||||
def api_example():
|
||||
"""Try to add descriptions for others."""
|
||||
#YOUR-CODE
|
||||
|
||||
return jsonify({"status": "TRUE"}), 200
|
|
@ -6,3 +6,9 @@ https://github.com/btcontract/lnurl-rfc/blob/master/spec.md#3-lnurl-withdraw
|
|||
With this extension to can create/edit LNURL withdraws, set a min/max amount, set time (useful for subscription services)
|
||||
|
||||
![lnurl](https://i.imgur.com/qHi7ExL.png)
|
||||
|
||||
|
||||
## API endpoint - /withdraw/api/v1/lnurlmaker
|
||||
Easily fetch one-off LNURLw
|
||||
|
||||
curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/withdraw/api/v1/lnurlmaker -d '{"amount":"100","memo":"ATM"}' -H "Grpc-Metadata-macaroon: YOUR-WALLET-ADMIN-KEY"
|
||||
|
|
|
@ -6,7 +6,8 @@ from flask import jsonify, request, url_for
|
|||
from lnurl import LnurlWithdrawResponse, encode as lnurl_encode
|
||||
from datetime import datetime
|
||||
|
||||
from lnbits.db import open_ext_db
|
||||
|
||||
from lnbits.db import open_ext_db, open_db
|
||||
from lnbits.extensions.withdraw import withdraw_ext
|
||||
|
||||
|
||||
|
@ -117,3 +118,64 @@ def api_lnurlwithdraw(rand):
|
|||
user_fau = withdraw_ext_db.fetchall("SELECT * FROM withdraws WHERE withdrawals = ?", (k1,))
|
||||
|
||||
return jsonify({"status": "OK"}), 200
|
||||
|
||||
@withdraw_ext.route("/api/v1/lnurlmaker", methods=["GET","POST"])
|
||||
def api_lnurlmaker():
|
||||
|
||||
if request.headers["Content-Type"] != "application/json":
|
||||
return jsonify({"ERROR": "MUST BE JSON"}), 400
|
||||
|
||||
with open_db() as db:
|
||||
wallet = db.fetchall(
|
||||
"SELECT * FROM wallets WHERE adminkey = ?",
|
||||
(request.headers["Grpc-Metadata-macaroon"],),
|
||||
)
|
||||
if not wallet:
|
||||
return jsonify({"ERROR": "NO KEY"}), 200
|
||||
|
||||
balance = db.fetchone("SELECT balance/1000 FROM balances WHERE wallet = ?", (wallet[0][0],))[0]
|
||||
print(balance)
|
||||
|
||||
postedjson = request.json
|
||||
print(postedjson["amount"])
|
||||
|
||||
if balance < int(postedjson["amount"]):
|
||||
return jsonify({"ERROR": "NOT ENOUGH FUNDS"}), 200
|
||||
|
||||
uni = uuid.uuid4().hex
|
||||
rand = uuid.uuid4().hex[0:5]
|
||||
|
||||
with open_ext_db("withdraw") as withdraw_ext_db:
|
||||
withdraw_ext_db.execute(
|
||||
"""
|
||||
INSERT OR IGNORE INTO withdraws
|
||||
(usr, wal, walnme, adm, uni, tit, maxamt, minamt, spent, inc, tme, uniq, withdrawals, tmestmp, rand)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
wallet[0][2],
|
||||
wallet[0][0],
|
||||
wallet[0][1],
|
||||
wallet[0][3],
|
||||
uni,
|
||||
postedjson["memo"],
|
||||
postedjson["amount"],
|
||||
postedjson["amount"],
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
rand,
|
||||
),
|
||||
)
|
||||
|
||||
user_fau = withdraw_ext_db.fetchone("SELECT * FROM withdraws WHERE uni = ?", (uni,))
|
||||
|
||||
if not user_fau:
|
||||
return jsonify({"ERROR": "WITHDRAW NOT MADE"}), 401
|
||||
|
||||
url = url_for("withdraw.api_lnurlfetch", _external=True, urlstr=request.host, parstr=uni, rand=rand)
|
||||
|
||||
return jsonify({"status": "TRUE", "lnurl": lnurl_encode(url.replace("http://", "https://"))}), 200
|
||||
|
|
7
lnbits/static/bootstrap/css/datepicker.min.css
vendored
Normal file
7
lnbits/static/bootstrap/css/datepicker.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
8
lnbits/static/bootstrap/js/datepicker.min.js
vendored
Normal file
8
lnbits/static/bootstrap/js/datepicker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -9,6 +9,13 @@
|
|||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
|
||||
name="viewport"
|
||||
/>
|
||||
<!-- Date picker -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
media="screen"
|
||||
href="{{ url_for('static', filename='bootstrap/css/bootstrap-datepicker.min.css') }}"
|
||||
/>
|
||||
|
||||
<!-- Bootstrap 3.3.2 -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
|
@ -115,6 +122,12 @@
|
|||
<script>
|
||||
$.widget.bridge('uibutton', $.ui.button)
|
||||
</script>
|
||||
<!-- Datepicker 3.3.2 JS -->
|
||||
<script
|
||||
src="{{ url_for('static', filename='bootstrap/js/bootstrap-datepicker.min.js') }}"
|
||||
type="text/javascript"
|
||||
></script>
|
||||
|
||||
<!-- Bootstrap 3.3.2 JS -->
|
||||
<script
|
||||
src="{{ url_for('static', filename='bootstrap/js/bootstrap.min.js') }}"
|
||||
|
|
Loading…
Reference in New Issue
Block a user