1
- use rusqlite:: { params, Connection , Error :: QueryReturnedNoRows } ;
1
+ use rusqlite:: { params, Connection , Statement , Error :: QueryReturnedNoRows } ;
2
2
use std:: error:: Error ;
3
3
use std:: fmt;
4
4
use serde:: { Deserialize , Serialize } ;
5
5
use serde_json:: Value ;
6
6
use std:: os:: unix:: fs:: PermissionsExt ;
7
7
use chrono:: DateTime ;
8
+ use std:: collections:: HashMap ;
8
9
9
10
#[ derive( Serialize , Deserialize , Debug ) ]
10
11
pub struct NodeInfoRecord {
@@ -106,6 +107,17 @@ pub struct SettingsRecord {
106
107
pub custom_pew_file : Option < String > ,
107
108
}
108
109
110
+ #[ derive( Debug , Default ) ]
111
+ pub struct BoostFilters {
112
+ pub podcast : Option < String > ,
113
+ }
114
+
115
+ impl BoostFilters {
116
+ pub fn new ( ) -> Self {
117
+ Default :: default ( )
118
+ }
119
+ }
120
+
109
121
#[ derive( Debug ) ]
110
122
struct HydraError ( String ) ;
111
123
impl fmt:: Display for HydraError {
@@ -165,6 +177,20 @@ fn table_exists(conn: &Connection, table_name: &str) -> Result<bool, Box<dyn Err
165
177
Ok ( rows. next ( ) . is_some ( ) )
166
178
}
167
179
180
+ //Bind a query parameter by param name and desired value
181
+ fn bind_query_param ( stmt : & mut Statement , name : & str , value : & str ) -> Result < ( ) , Box < dyn Error > > {
182
+ let idx = match stmt. parameter_index ( name) ? {
183
+ Some ( num) => num,
184
+ None => {
185
+ return Err ( format ! ( "{} param not found" , name) . into ( ) ) ;
186
+ }
187
+ } ;
188
+
189
+ stmt. raw_bind_parameter ( idx, value) ?;
190
+
191
+ Ok ( ( ) )
192
+ }
193
+
168
194
//Create or update a new database file if needed
169
195
pub fn create_database ( filepath : & String ) -> Result < bool , Box < dyn Error > > {
170
196
let conn = connect_to_database ( true , filepath) ?;
@@ -569,36 +595,64 @@ pub fn mark_boost_as_replied(filepath: &String, index: u64) -> Result<bool, Box<
569
595
}
570
596
571
597
//Get all of the invoices from the database
572
- pub fn get_invoices_from_db ( filepath : & String , invtype : & str , index : u64 , max : u64 , direction : bool , escape_html : bool ) -> Result < Vec < BoostRecord > , Box < dyn Error > > {
598
+ pub fn get_invoices_from_db ( filepath : & String , invtype : & str , index : u64 , max : u64 , direction : bool , escape_html : bool , filters : BoostFilters ) -> Result < Vec < BoostRecord > , Box < dyn Error > > {
573
599
let conn = connect_to_database ( false , filepath) ?;
574
600
let mut boosts: Vec < BoostRecord > = Vec :: new ( ) ;
575
601
576
- let mut ltgt = ">=" ;
577
- if direction {
578
- ltgt = "<=" ;
579
- }
602
+ let mut conditions = String :: new ( ) ;
603
+ let mut bindings: HashMap < & str , & str > = HashMap :: new ( ) ;
580
604
581
- let action = match invtype {
582
- "boost" => " AND action IN (2, 4)" ,
583
- "stream" => " AND action NOT IN (2, 4)" ,
584
- _ => "" ,
605
+ let ltgt = if direction {
606
+ "<="
607
+ } else {
608
+ ">="
585
609
} ;
586
610
611
+ conditions. push_str ( & format ! ( "idx {} :idx" , ltgt) ) ;
612
+
613
+ let strindex = index. to_string ( ) ;
614
+ bindings. insert ( ":idx" , & strindex) ;
615
+
616
+ if invtype == "boost" {
617
+ conditions. push_str ( " AND action IN (2, 4)" ) ;
618
+ }
619
+ else if invtype == "stream" {
620
+ conditions. push_str ( " AND action NOT IN (2, 4)" ) ;
621
+ }
622
+
623
+ if let Some ( podcast) = & filters. podcast {
624
+ conditions. push_str ( " AND podcast = :podcast" ) ;
625
+ bindings. insert ( ":podcast" , podcast) ;
626
+ }
627
+
628
+ let strmax = max. to_string ( ) ;
629
+ bindings. insert ( ":max" , & strmax) ;
630
+
587
631
//Query for boosts and automated boosts
588
632
let sqltxt = format ! ( "
589
- SELECT idx, time, value_msat, value_msat_total, action, sender, app, message, podcast, episode, tlv, remote_podcast, remote_episode, reply_sent, custom_key, custom_value
590
- FROM boosts
633
+ SELECT
634
+ idx, time, value_msat, value_msat_total, action, sender, app, message, podcast, episode, tlv, remote_podcast, remote_episode, reply_sent, custom_key, custom_value
635
+ FROM
636
+ boosts
591
637
WHERE
592
- idx {} :index
593
638
{}
594
- ORDER BY idx DESC
595
- LIMIT :max
596
- " , ltgt, action) ;
639
+ ORDER BY
640
+ idx DESC
641
+ LIMIT
642
+ :max
643
+ " , conditions) ;
597
644
598
645
//Prepare and execute the query
599
646
let mut stmt = conn. prepare ( sqltxt. as_str ( ) ) ?;
600
- let rows = stmt. query_map ( & [ ( ":index" , index. to_string ( ) . as_str ( ) ) , ( ":max" , max. to_string ( ) . as_str ( ) ) ] , |row| {
601
- Ok ( BoostRecord {
647
+
648
+ for ( name, value) in & bindings {
649
+ bind_query_param ( & mut stmt, name, value) ?;
650
+ }
651
+
652
+ let mut rows = stmt. raw_query ( ) ;
653
+
654
+ while let Some ( row) = rows. next ( ) ? {
655
+ let boost = BoostRecord {
602
656
index : row. get ( 0 ) ?,
603
657
time : row. get ( 1 ) ?,
604
658
value_msat : row. get ( 2 ) ?,
@@ -616,12 +670,7 @@ pub fn get_invoices_from_db(filepath: &String, invtype: &str, index: u64, max: u
616
670
custom_key : row. get ( 14 ) . ok ( ) ,
617
671
custom_value : row. get ( 15 ) . ok ( ) ,
618
672
payment_info : None ,
619
- } )
620
- } ) . unwrap ( ) ;
621
-
622
- //Parse the results
623
- for row in rows {
624
- let boost: BoostRecord = row. unwrap ( ) ;
673
+ } ;
625
674
626
675
//Some things like text output don't need to be html entity escaped
627
676
//so only do it if asked for
@@ -648,7 +697,8 @@ pub fn get_invoices_from_db(filepath: &String, invtype: &str, index: u64, max: u
648
697
}
649
698
650
699
pub fn get_single_invoice_from_db ( filepath : & String , invtype : & str , index : u64 , escape_html : bool ) -> Result < Option < BoostRecord > , Box < dyn Error > > {
651
- let invoices = get_invoices_from_db ( filepath, invtype, index, 1 , true , escape_html) ?;
700
+ let filters = BoostFilters :: new ( ) ;
701
+ let invoices = get_invoices_from_db ( filepath, invtype, index, 1 , true , escape_html, filters) ?;
652
702
653
703
if !invoices. is_empty ( ) && invoices[ 0 ] . index == index {
654
704
Ok ( Some ( invoices[ 0 ] . clone ( ) ) )
@@ -658,13 +708,13 @@ pub fn get_single_invoice_from_db(filepath: &String, invtype: &str, index: u64,
658
708
}
659
709
}
660
710
661
- pub fn get_boosts_from_db ( filepath : & String , index : u64 , max : u64 , direction : bool , escape_html : bool ) -> Result < Vec < BoostRecord > , Box < dyn Error > > {
662
- get_invoices_from_db ( filepath, "boost" , index, max, direction, escape_html)
711
+ pub fn get_boosts_from_db ( filepath : & String , index : u64 , max : u64 , direction : bool , escape_html : bool , filters : BoostFilters ) -> Result < Vec < BoostRecord > , Box < dyn Error > > {
712
+ get_invoices_from_db ( filepath, "boost" , index, max, direction, escape_html, filters )
663
713
}
664
714
665
715
//Get all of the non-boosts from the database
666
- pub fn get_streams_from_db ( filepath : & String , index : u64 , max : u64 , direction : bool , escape_html : bool ) -> Result < Vec < BoostRecord > , Box < dyn Error > > {
667
- get_invoices_from_db ( filepath, "stream" , index, max, direction, escape_html)
716
+ pub fn get_streams_from_db ( filepath : & String , index : u64 , max : u64 , direction : bool , escape_html : bool , filters : BoostFilters ) -> Result < Vec < BoostRecord > , Box < dyn Error > > {
717
+ get_invoices_from_db ( filepath, "stream" , index, max, direction, escape_html, filters )
668
718
}
669
719
670
720
//Get the last boost index number from the database
@@ -721,6 +771,25 @@ pub fn get_last_boost_index_from_db(filepath: &String) -> Result<u64, Box<dyn Er
721
771
Ok ( 0 )
722
772
}
723
773
774
+ //Get podcasts that received boosts to this node
775
+ pub fn get_podcasts_from_db ( filepath : & String ) -> Result < Vec < String > , Box < dyn Error > > {
776
+ let conn = connect_to_database ( false , filepath) ?;
777
+
778
+ let query = "SELECT DISTINCT podcast FROM boosts WHERE podcast <> '' ORDER BY podcast" . to_string ( ) ;
779
+
780
+ let mut stmt = conn. prepare ( & query) ?;
781
+ let mut rows = stmt. raw_query ( ) ;
782
+
783
+ //Parse the results
784
+ let mut podcasts = Vec :: new ( ) ;
785
+
786
+ while let Some ( row) = rows. next ( ) ? {
787
+ podcasts. push ( row. get ( 0 ) ?) ;
788
+ }
789
+
790
+ Ok ( podcasts)
791
+ }
792
+
724
793
//Set/Get the wallet balance from the database in sats
725
794
pub fn add_wallet_balance_to_db ( filepath : & String , balance : i64 ) -> Result < bool , Box < dyn Error > > {
726
795
let conn = connect_to_database ( false , filepath) ?;
@@ -758,16 +827,33 @@ pub fn get_wallet_balance_from_db(filepath: &String) -> Result<i64, Box<dyn Erro
758
827
}
759
828
760
829
//Get all of the sent boosts from the database
761
- pub fn get_payments_from_db ( filepath : & String , index : u64 , max : u64 , direction : bool , escape_html : bool ) -> Result < Vec < BoostRecord > , Box < dyn Error > > {
830
+ pub fn get_payments_from_db ( filepath : & String , index : u64 , max : u64 , direction : bool , escape_html : bool , filters : BoostFilters ) -> Result < Vec < BoostRecord > , Box < dyn Error > > {
762
831
let conn = connect_to_database ( false , filepath) ?;
763
832
let mut boosts: Vec < BoostRecord > = Vec :: new ( ) ;
764
833
765
- let mut ltgt = ">=" ;
766
- if direction {
767
- ltgt = "<=" ;
834
+ let mut conditions = String :: new ( ) ;
835
+ let mut bindings: HashMap < & str , & str > = HashMap :: new ( ) ;
836
+
837
+ let ltgt = if direction {
838
+ "<="
839
+ } else {
840
+ ">="
841
+ } ;
842
+
843
+ conditions. push_str ( & format ! ( "idx {} :idx" , ltgt) ) ;
844
+
845
+ let strindex = index. to_string ( ) ;
846
+ bindings. insert ( ":idx" , & strindex) ;
847
+
848
+ if let Some ( podcast) = & filters. podcast {
849
+ conditions. push_str ( " AND podcast = :podcast" ) ;
850
+ bindings. insert ( ":podcast" , podcast) ;
768
851
}
769
852
770
- //Build the query
853
+ let strmax = max. to_string ( ) ;
854
+ bindings. insert ( ":max" , & strmax) ;
855
+
856
+ //Query for boosts and automated boosts
771
857
let sqltxt = format ! (
772
858
"SELECT
773
859
idx,
@@ -792,19 +878,27 @@ pub fn get_payments_from_db(filepath: &String, index: u64, max: u64, direction:
792
878
FROM
793
879
sent_boosts
794
880
WHERE
795
- idx {} :index
881
+ {}
796
882
ORDER BY
797
883
idx DESC
798
884
LIMIT
799
885
:max
800
886
" ,
801
- ltgt
887
+ conditions
802
888
) ;
803
889
804
890
//Prepare and execute the query
805
891
let mut stmt = conn. prepare ( sqltxt. as_str ( ) ) ?;
806
- let rows = stmt. query_map ( & [ ( ":index" , index. to_string ( ) . as_str ( ) ) , ( ":max" , max. to_string ( ) . as_str ( ) ) ] , |row| {
807
- Ok ( BoostRecord {
892
+
893
+ for ( name, value) in & bindings {
894
+ bind_query_param ( & mut stmt, name, value) ?;
895
+ }
896
+
897
+ let mut rows = stmt. raw_query ( ) ;
898
+
899
+ //Parse the results
900
+ while let Some ( row) = rows. next ( ) ? {
901
+ let boost = BoostRecord {
808
902
index : row. get ( 0 ) ?,
809
903
time : row. get ( 1 ) ?,
810
904
value_msat : row. get ( 2 ) ?,
@@ -829,12 +923,7 @@ pub fn get_payments_from_db(filepath: &String, index: u64, max: u64, direction:
829
923
fee_msat : row. get ( 17 ) ?,
830
924
reply_to_idx : row. get ( 18 ) ?,
831
925
} ) ,
832
- } )
833
- } ) . unwrap ( ) ;
834
-
835
- //Parse the results
836
- for row in rows {
837
- let boost: BoostRecord = row. unwrap ( ) ;
926
+ } ;
838
927
839
928
//Some things like text output don't need to be html entity escaped
840
929
//so only do it if asked for
@@ -862,7 +951,6 @@ pub fn get_payments_from_db(filepath: &String, index: u64, max: u64, direction:
862
951
} else {
863
952
boosts. push ( boost) ;
864
953
}
865
-
866
954
}
867
955
868
956
Ok ( boosts)
@@ -949,6 +1037,25 @@ pub fn add_payment_to_db(filepath: &String, boost: &BoostRecord) -> Result<bool,
949
1037
Ok ( true )
950
1038
}
951
1039
1040
+ //Get podcasts that were send boosts from this node
1041
+ pub fn get_sent_podcasts_from_db ( filepath : & String ) -> Result < Vec < String > , Box < dyn Error > > {
1042
+ let conn = connect_to_database ( false , filepath) ?;
1043
+
1044
+ let query = "SELECT DISTINCT podcast FROM sent_boosts WHERE podcast <> '' ORDER BY podcast" . to_string ( ) ;
1045
+
1046
+ let mut stmt = conn. prepare ( & query) ?;
1047
+ let mut rows = stmt. raw_query ( ) ;
1048
+
1049
+ //Parse the results
1050
+ let mut podcasts = Vec :: new ( ) ;
1051
+
1052
+ while let Some ( row) = rows. next ( ) ? {
1053
+ podcasts. push ( row. get ( 0 ) ?) ;
1054
+ }
1055
+
1056
+ Ok ( podcasts)
1057
+ }
1058
+
952
1059
pub fn get_webhooks_from_db ( filepath : & String , enabled : Option < bool > ) -> Result < Vec < WebhookRecord > , Box < dyn Error > > {
953
1060
let conn = connect_to_database ( false , filepath) ?;
954
1061
let mut webhooks: Vec < WebhookRecord > = Vec :: new ( ) ;
0 commit comments