diff --git a/lnbits/core/templates/audit/index.html b/lnbits/core/templates/audit/index.html deleted file mode 100644 index 1626ca6a..00000000 --- a/lnbits/core/templates/audit/index.html +++ /dev/null @@ -1,201 +0,0 @@ -{% if not ajax %} {% extends "base.html" %} {% endif %} - -{% from "macros.jinja" import window_vars with context %} - -{% block scripts %} {{ window_vars(user) }} {% endblock %} {% block page %} -
-
- -
-
-
- -
-
- - - -
-
-
-
-
-
- -
-
- - -
- -
-
-
-
- - -
- -
-
-
-
- - -
- -
-
-
-
- - -
- -
-
-
-
- -
-
- - - - - - -
-
- - - - - - -
- - -
-
-
- -{% endblock %} diff --git a/lnbits/core/views/generic.py b/lnbits/core/views/generic.py index b3701ed4..15e6965c 100644 --- a/lnbits/core/views/generic.py +++ b/lnbits/core/views/generic.py @@ -462,21 +462,6 @@ async def users_index(request: Request, user: User = Depends(check_admin)): ) -@generic_router.get("/audit", response_class=HTMLResponse) -async def audit_index(request: Request, user: User = Depends(check_admin)): - if not settings.lnbits_audit_enabled: - raise HTTPException(HTTPStatus.NOT_FOUND, "Audit not enabled") - - return template_renderer().TemplateResponse( - "audit/index.html", - { - "request": request, - "user": user.json(), - "ajax": _is_ajax_request(request), - }, - ) - - @generic_router.get("/payments", response_class=HTMLResponse) async def empty_index(request: Request, user: User = Depends(check_user_exists)): return template_renderer().TemplateResponse( @@ -488,6 +473,7 @@ async def empty_index(request: Request, user: User = Depends(check_user_exists)) ) +@generic_router.get("/audit", response_class=HTMLResponse) @generic_router.get("/node", response_class=HTMLResponse) async def empty_admin_index(request: Request, admin: User = Depends(check_admin)): return template_renderer().TemplateResponse( @@ -500,7 +486,7 @@ async def empty_admin_index(request: Request, admin: User = Depends(check_admin) @generic_router.get("/node/public", response_class=HTMLResponse) -async def node_public(request: Request): +async def empty_public(request: Request): return template_renderer().TemplateResponse(request, "empty_public.html") diff --git a/lnbits/core/views/node_api.py b/lnbits/core/views/node_api.py index 873c830e..d299365c 100644 --- a/lnbits/core/views/node_api.py +++ b/lnbits/core/views/node_api.py @@ -3,7 +3,6 @@ from http import HTTPStatus import httpx from fastapi import APIRouter, Body, Depends, HTTPException from pydantic import BaseModel -from starlette.status import HTTP_503_SERVICE_UNAVAILABLE from lnbits.decorators import check_admin, check_super_user, parse_filters from lnbits.settings import settings @@ -154,7 +153,7 @@ async def api_get_payments( ) -> Page[NodePayment] | None: if not settings.lnbits_node_ui_transactions: raise HTTPException( - HTTP_503_SERVICE_UNAVAILABLE, + HTTPStatus.SERVICE_UNAVAILABLE, detail="You can enable node transactions in the Admin UI", ) return await node.get_payments(filters) @@ -167,7 +166,7 @@ async def api_get_invoices( ) -> Page[NodeInvoice] | None: if not settings.lnbits_node_ui_transactions: raise HTTPException( - HTTP_503_SERVICE_UNAVAILABLE, + HTTPStatus.SERVICE_UNAVAILABLE, detail="You can enable node transactions in the Admin UI", ) return await node.get_invoices(filters) diff --git a/lnbits/static/bundle-components.min.js b/lnbits/static/bundle-components.min.js index 637da55a..117e3dd5 100644 --- a/lnbits/static/bundle-components.min.js +++ b/lnbits/static/bundle-components.min.js @@ -1 +1 @@ -window.PagePayments={template:"#page-payments",mixins:[window.windowMixin],data:()=>({payments:[],dailyChartData:[],searchDate:{from:null,to:null},searchData:{wallet_id:null,payment_hash:null,memo:null,internal_memo:null},statusFilters:{success:!0,pending:!0,failed:!0,incoming:!0,outgoing:!0},chartData:{showPaymentStatus:!0,showPaymentTags:!0,showBalance:!0,showWalletsSize:!1,showBalanceInOut:!1,showPaymentCountInOut:!1},searchOptions:{status:[]},paymentsTable:{columns:[{name:"status",align:"left",label:"Status",field:"status",sortable:!1},{name:"created_at",align:"left",label:"Created At",field:"created_at",sortable:!0},{name:"amount",align:"right",label:"Amount",field:"amount",sortable:!0},{name:"amountFiat",align:"right",label:"Fiat",field:"amountFiat",sortable:!1},{name:"fee_sats",align:"left",label:"Fee",field:"fee_sats",sortable:!0},{name:"tag",align:"left",label:"Tag",field:"tag",sortable:!1},{name:"memo",align:"left",label:"Memo",field:"memo",sortable:!1,max_length:20},{name:"internal_memo",align:"left",label:"Internal Memo",field:"internal_memo",sortable:!1,max_length:20},{name:"wallet_id",align:"left",label:"Wallet (ID)",field:"wallet_id",sortable:!1},{name:"payment_hash",align:"left",label:"Payment Hash",field:"payment_hash",sortable:!1}],pagination:{sortBy:"created_at",rowsPerPage:25,page:1,descending:!0,rowsNumber:10},search:null,hideEmpty:!0,loading:!1},chartsReady:!1,showDetails:!1,paymentDetails:null,lnbitsBalance:0}),async mounted(){this.chartsReady=!0,await this.$nextTick(),this.initCharts(),await this.fetchPayments()},computed:{},methods:{async fetchPayments(t){const e=Object.entries(this.searchData).reduce(((t,[e,a])=>a?(t[e]=a,t):t),{});delete e["time[ge]"],delete e["time[le]"],this.searchDate.from&&(e["time[ge]"]=this.searchDate.from+"T00:00:00"),this.searchDate.to&&(e["time[le]"]=this.searchDate.to+"T23:59:59"),this.paymentsTable.filter=e;try{const e=LNbits.utils.prepareFilterQuery(this.paymentsTable,t),{data:a}=await LNbits.api.request("GET",`/api/v1/payments/all/paginated?${e}`);this.paymentsTable.pagination.rowsNumber=a.total,this.payments=a.data.map((t=>(t.extra&&t.extra.tag&&(t.tag=t.extra.tag),t.timeFrom=moment.utc(t.created_at).local().fromNow(),t.outgoing=t.amount<0,t.amount=new Intl.NumberFormat(window.LOCALE).format(t.amount/1e3)+" sats",t.extra?.wallet_fiat_amount&&(t.amountFiat=this.formatCurrency(t.extra.wallet_fiat_amount,t.extra.wallet_fiat_currency)),t.extra?.internal_memo&&(t.internal_memo=t.extra.internal_memo),t.fee_sats=new Intl.NumberFormat(window.LOCALE).format(t.fee/1e3)+" sats",t)))}catch(t){console.error(t),LNbits.utils.notifyApiError(t)}finally{this.updateCharts(t)}},async searchPaymentsBy(t,e){t&&(this.searchData[t]=e),await this.fetchPayments()},clearDateSeach(){this.searchDate={from:null,to:null},delete this.paymentsTable.filter["time[ge]"],delete this.paymentsTable.filter["time[le]"],this.fetchPayments()},searchByDate(){"string"==typeof this.searchDate&&(this.searchDate={from:this.searchDate,to:this.searchDate}),this.searchDate.from&&(this.paymentsTable.filter["time[ge]"]=this.searchDate.from+"T00:00:00"),this.searchDate.to&&(this.paymentsTable.filter["time[le]"]=this.searchDate.to+"T23:59:59"),this.fetchPayments()},handleFilterChanged(){const{success:t,pending:e,failed:a,incoming:s,outgoing:n}=this.statusFilters;delete this.searchData["status[ne]"],delete this.searchData["status[eq]"],t&&e&&a||(t&&e?this.searchData["status[ne]"]="failed":t&&a?this.searchData["status[ne]"]="pending":a&&e?this.searchData["status[ne]"]="success":t?this.searchData["status[eq]"]="success":e?this.searchData["status[eq]"]="pending":a&&(this.searchData["status[eq]"]="failed")),delete this.searchData["amount[ge]"],delete this.searchData["amount[le]"],s&&n||(s?this.searchData["amount[ge]"]="0":n&&(this.searchData["amount[le]"]="0")),this.fetchPayments()},showDetailsToggle(t){return this.paymentDetails=t,this.showDetails=!this.showDetails},formatDate:t=>LNbits.utils.formatDateString(t),formatCurrency(t,e){try{return LNbits.utils.formatCurrency(t,e)}catch(e){return console.error(e),`${t} ???`}},shortify:(t,e=10)=>(valueLength=(t||"").length,valueLength<=e?t:`${t.substring(0,5)}...${t.substring(valueLength-5,valueLength)}`),async updateCharts(t){let e=LNbits.utils.prepareFilterQuery(this.paymentsTable,t);try{const{data:t}=await LNbits.api.request("GET",`/api/v1/payments/stats/count?${e}&count_by=status`);t.sort(((t,e)=>t.field-e.field)).reverse(),this.searchOptions.status=t.map((t=>t.field)),this.paymentsStatusChart.data.datasets[0].data=t.map((t=>t.total)),this.paymentsStatusChart.data.labels=[...this.searchOptions.status],this.paymentsStatusChart.update()}catch(t){console.warn(t),LNbits.utils.notifyApiError(t)}try{const{data:t}=await LNbits.api.request("GET",`/api/v1/payments/stats/wallets?${e}`),a=t.map((t=>t.balance/t.payments_count)),s=Math.min(...a),n=Math.max(...a),i=t=>Math.floor(3+22*(t-s)/(n-s)),o=this.randomColors(20),l=t.map(((t,e)=>({data:[{x:t.payments_count,y:t.balance,r:i(Math.max(t.balance/t.payments_count,5))}],label:t.wallet_name,wallet_id:t.wallet_id,backgroundColor:o[e%100],hoverOffset:4})));this.paymentsWalletsChart.data.datasets=l,this.paymentsWalletsChart.update()}catch(t){console.warn(t),LNbits.utils.notifyApiError(t)}try{const{data:t}=await LNbits.api.request("GET",`/api/v1/payments/stats/count?${e}&count_by=tag`);this.searchOptions.tag=t.map((t=>t.field)),this.searchOptions.status.sort(),this.paymentsTagsChart.data.datasets[0].data=t.map((t=>t.total)),this.paymentsTagsChart.data.labels=t.map((t=>t.field||"core")),this.paymentsTagsChart.update()}catch(t){console.warn(t),LNbits.utils.notifyApiError(t)}try{const e=Object.entries(this.searchData).reduce(((t,[e,a])=>a?(t[e]=a,t):t),{}),a={...this.paymentsTable,filter:e},s=LNbits.utils.prepareFilterQuery(a,t);let{data:n}=await LNbits.api.request("GET",`/api/v1/payments/stats/daily?${s}`);const i=this.searchDate.from+"T00:00:00",o=this.searchDate.to+"T23:59:59";this.lnbitsBalance=n.length?n[n.length-1].balance:0,n=n.filter((t=>this.searchDate.from&&this.searchDate.to?t.date>=i&&t.date<=o:this.searchDate.from?t.date>=i:!this.searchDate.to||t.date<=o)),this.paymentsDailyChart.data.datasets=[{label:"Balance",data:n.map((t=>t.balance)),pointStyle:!1,borderWidth:2,tension:.7,fill:1},{label:"Fees",data:n.map((t=>t.fee)),pointStyle:!1,borderWidth:1,tension:.4,fill:1}],this.paymentsDailyChart.data.labels=n.map((t=>t.date.substring(0,10))),this.paymentsDailyChart.update(),this.paymentsBalanceInOutChart.data.datasets=[{label:"Incoming Payments Balance",data:n.map((t=>t.balance_in))},{label:"Outgoing Payments Balance",data:n.map((t=>t.balance_out))}],this.paymentsBalanceInOutChart.data.labels=n.map((t=>t.date.substring(0,10))),this.paymentsBalanceInOutChart.update(),this.paymentsCountInOutChart.data.datasets=[{label:"Incoming Payments Count",data:n.map((t=>t.count_in))},{label:"Outgoing Payments Count",data:n.map((t=>-t.count_out))}],this.paymentsCountInOutChart.data.labels=n.map((t=>t.date.substring(0,10))),this.paymentsCountInOutChart.update()}catch(t){console.warn(t),LNbits.utils.notifyApiError(t)}},async initCharts(){const t=this.$q.localStorage.getItem("lnbits.payments.chartData")||{};this.chartData={...this.chartData,...t},this.chartsReady?(this.paymentsStatusChart=new Chart(this.$refs.paymentsStatusChart.getContext("2d"),{type:"doughnut",options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:!1}},onClick:(t,e,a)=>{if(e[0]){const t=e[0].index;this.searchPaymentsBy("status",a.data.labels[t])}}},data:{datasets:[{label:"",data:[],backgroundColor:["rgb(0, 205, 86)","rgb(64, 72, 78)","rgb(255, 99, 132)"],hoverOffset:4}]}}),this.paymentsWalletsChart=new Chart(this.$refs.paymentsWalletsChart.getContext("2d"),{type:"bubble",options:{responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1},title:{display:!1}},onClick:(t,e,a)=>{if(e[0]){const t=e[0].datasetIndex;this.searchPaymentsBy("wallet_id",a.data.datasets[t].wallet_id)}}},data:{datasets:[{label:"",data:[],backgroundColor:this.randomColors(20),hoverOffset:4}]}}),this.paymentsTagsChart=new Chart(this.$refs.paymentsTagsChart.getContext("2d"),{type:"pie",options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:!1},legend:{display:!1,title:{display:!1,text:"Tags"}}},onClick:(t,e,a)=>{if(e[0]){const t=e[0].index;this.searchPaymentsBy("tag",a.data.labels[t])}}},data:{datasets:[{label:"",data:[],backgroundColor:this.randomColors(10),hoverOffset:4}]}}),this.paymentsDailyChart=new Chart(this.$refs.paymentsDailyChart.getContext("2d"),{type:"line",options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:!1},legend:{display:!0,title:{display:!1,text:"Tags"}}}},data:{datasets:[{label:"",data:[],backgroundColor:this.randomColors(10),hoverOffset:4}]}}),this.paymentsBalanceInOutChart=new Chart(this.$refs.paymentsBalanceInOutChart.getContext("2d"),{type:"bar",options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:!1},legend:{display:!0,title:{display:!1,text:"Tags"}}},scales:{x:{stacked:!0},y:{stacked:!0}}},data:{datasets:[{label:"",data:[],backgroundColor:this.randomColors(50),hoverOffset:4}]}}),this.paymentsCountInOutChart=new Chart(this.$refs.paymentsCountInOutChart.getContext("2d"),{type:"bar",options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:!1},legend:{display:!0,title:{display:!1,text:""}}},scales:{x:{stacked:!0},y:{stacked:!0}}},data:{datasets:[{label:"",data:[],backgroundColor:this.randomColors(80),hoverOffset:4}]}})):console.warn("Charts are not ready yet. Initialization delayed.")},saveChartsPreferences(){this.$q.localStorage.set("lnbits.payments.chartData",this.chartData)},randomColors(t=1){const e=[];for(let a=1;a<=10;a++)for(let s=1;s<=10;s++)e.push(`rgb(${s*t*33%200}, ${71*(a+s+t)%255}, ${(a+30*t)%255})`);return e}}},window.PageNode={mixins:[window.windowMixin],template:"#page-node",config:{globalProperties:{LNbits:LNbits,msg:"hello"}},data(){return{isSuperUser:!1,wallet:{},tab:"dashboard",payments:1e3,info:{},channel_stats:{},channels:{data:[],filter:""},activeBalance:{},ranks:{},peers:{data:[],filter:""},connectPeerDialog:{show:!1,data:{}},setFeeDialog:{show:!1,data:{fee_ppm:0,fee_base_msat:0}},openChannelDialog:{show:!1,data:{}},closeChannelDialog:{show:!1,data:{}},nodeInfoDialog:{show:!1,data:{}},transactionDetailsDialog:{show:!1,data:{}},states:[{label:"Active",value:"active",color:"green"},{label:"Pending",value:"pending",color:"orange"},{label:"Inactive",value:"inactive",color:"grey"},{label:"Closed",value:"closed",color:"red"}],stateFilters:[{label:"Active",value:"active"},{label:"Pending",value:"pending"}],paymentsTable:{data:[],columns:[{name:"pending",label:""},{name:"date",align:"left",label:this.$t("date"),field:"date",sortable:!0},{name:"sat",align:"right",label:this.$t("amount")+" ("+LNBITS_DENOMINATION+")",field:t=>this.formatMsat(t.amount),sortable:!0},{name:"fee",align:"right",label:this.$t("fee")+" (m"+LNBITS_DENOMINATION+")",field:"fee"},{name:"destination",align:"right",label:"Destination",field:"destination"},{name:"memo",align:"left",label:this.$t("memo"),field:"memo"}],pagination:{rowsPerPage:10,page:1,rowsNumber:10},filter:null},invoiceTable:{data:[],columns:[{name:"pending",label:""},{name:"paid_at",field:"paid_at",align:"left",label:"Paid at",sortable:!0},{name:"expiry",label:this.$t("expiry"),field:"expiry",align:"left",sortable:!0},{name:"amount",label:this.$t("amount")+" ("+LNBITS_DENOMINATION+")",field:t=>this.formatMsat(t.amount),sortable:!0},{name:"memo",align:"left",label:this.$t("memo"),field:"memo"}],pagination:{rowsPerPage:10,page:1,rowsNumber:10},filter:null}}},created(){this.getInfo(),this.get1MLStats()},watch:{tab(t){"transactions"!==t||this.paymentsTable.data.length?"channels"!==t||this.channels.data.length||(this.getChannels(),this.getPeers()):(this.getPayments(),this.getInvoices())}},computed:{checkChanges(){return!_.isEqual(this.settings,this.formData)},filteredChannels(){return this.stateFilters?this.channels.data.filter((t=>this.stateFilters.find((({value:e})=>e==t.state)))):this.channels.data},totalBalance(){return this.filteredChannels.reduce(((t,e)=>(t.local_msat+=e.balance.local_msat,t.remote_msat+=e.balance.remote_msat,t.total_msat+=e.balance.total_msat,t)),{local_msat:0,remote_msat:0,total_msat:0})}},methods:{formatMsat:t=>LNbits.utils.formatMsat(t),api(t,e,a){const s=new URLSearchParams(a?.query);return LNbits.api.request(t,`/node/api/v1${e}?${s}`,{},a?.data).catch((t=>{LNbits.utils.notifyApiError(t)}))},getChannel(t){return this.api("GET",`/channels/${t}`).then((t=>{this.setFeeDialog.data.fee_ppm=t.data.fee_ppm,this.setFeeDialog.data.fee_base_msat=t.data.fee_base_msat}))},getChannels(){return this.api("GET","/channels").then((t=>{this.channels.data=t.data}))},getInfo(){return this.api("GET","/info").then((t=>{this.info=t.data,this.channel_stats=t.data.channel_stats})).catch((()=>{this.info={},this.channel_stats={}}))},get1MLStats(){return this.api("GET","/rank").then((t=>{this.ranks=t.data})).catch((()=>{this.ranks={}}))},getPayments(t){t&&(this.paymentsTable.pagination=t.pagination);let e=this.paymentsTable.pagination;const a={limit:e.rowsPerPage,offset:(e.page-1)*e.rowsPerPage??0};return this.api("GET","/payments",{query:a}).then((t=>{this.paymentsTable.data=t.data.data,this.paymentsTable.pagination.rowsNumber=t.data.total}))},getInvoices(t){t&&(this.invoiceTable.pagination=t.pagination);let e=this.invoiceTable.pagination;const a={limit:e.rowsPerPage,offset:(e.page-1)*e.rowsPerPage??0};return this.api("GET","/invoices",{query:a}).then((t=>{this.invoiceTable.data=t.data.data,this.invoiceTable.pagination.rowsNumber=t.data.total}))},getPeers(){return this.api("GET","/peers").then((t=>{this.peers.data=t.data}))},connectPeer(){this.api("POST","/peers",{data:this.connectPeerDialog.data}).then((()=>{this.connectPeerDialog.show=!1,this.getPeers()}))},disconnectPeer(t){LNbits.utils.confirmDialog("Do you really wanna disconnect this peer?").onOk((()=>{this.api("DELETE",`/peers/${t}`).then((t=>{Quasar.Notify.create({message:"Disconnected",icon:null}),this.needsRestart=!0,this.getPeers()}))}))},setChannelFee(t){this.api("PUT",`/channels/${t}`,{data:this.setFeeDialog.data}).then((t=>{this.setFeeDialog.show=!1,this.getChannels()})).catch(LNbits.utils.notifyApiError)},openChannel(){this.api("POST","/channels",{data:this.openChannelDialog.data}).then((t=>{this.openChannelDialog.show=!1,this.getChannels()})).catch((t=>{console.log(t)}))},showCloseChannelDialog(t){this.closeChannelDialog.show=!0,this.closeChannelDialog.data={force:!1,short_id:t.short_id,...t.point}},closeChannel(){this.api("DELETE","/channels",{query:this.closeChannelDialog.data}).then((t=>{this.closeChannelDialog.show=!1,this.getChannels()}))},showSetFeeDialog(t){this.setFeeDialog.show=!0,this.setFeeDialog.channel_id=t,this.getChannel(t)},showOpenChannelDialog(t){this.openChannelDialog.show=!0,this.openChannelDialog.data={peer_id:t,funding_amount:0}},showNodeInfoDialog(t){this.nodeInfoDialog.show=!0,this.nodeInfoDialog.data=t},showTransactionDetailsDialog(t){this.transactionDetailsDialog.show=!0,this.transactionDetailsDialog.data=t},shortenNodeId:t=>t?t.substring(0,5)+"..."+t.substring(t.length-5):"..."}},window.PageNodePublic={template:"#page-node-public",mixins:[window.windowMixin],data:()=>({enabled:!1,isSuperUser:!1,wallet:{},tab:"dashboard",payments:1e3,info:{},channel_stats:{},channels:[],activeBalance:{},ranks:{},peers:[],connectPeerDialog:{show:!1,data:{}},openChannelDialog:{show:!1,data:{}},closeChannelDialog:{show:!1,data:{}},nodeInfoDialog:{show:!1,data:{}},states:[{label:"Active",value:"active",color:"green"},{label:"Pending",value:"pending",color:"orange"},{label:"Inactive",value:"inactive",color:"grey"},{label:"Closed",value:"closed",color:"red"}]}),created(){this.getInfo(),this.get1MLStats()},methods:{formatMsat:t=>LNbits.utils.formatMsat(t),api:(t,e,a)=>LNbits.api.request(t,"/node/public/api/v1"+e,{},a),getInfo(){this.api("GET","/info",{}).then((t=>{this.info=t.data,this.channel_stats=t.data.channel_stats,this.enabled=!0})).catch((()=>{this.info={},this.channel_stats={}}))},get1MLStats(){this.api("GET","/rank",{}).then((t=>{this.ranks=t.data})).catch((()=>{this.ranks={}}))}}},window.app.component("lnbits-qrcode",{mixins:[window.windowMixin],template:"#lnbits-qrcode",components:{QrcodeVue:QrcodeVue},props:{value:{type:String,required:!0},nfc:{type:Boolean,default:!1},showButtons:{type:Boolean,default:!0},href:{type:String,default:""},margin:{type:Number,default:3},maxWidth:{type:Number,default:450},logo:{type:String,default:LNBITS_QR_LOGO}},data:()=>({nfcTagWriting:!1,nfcSupported:"undefined"!=typeof NDEFReader}),methods:{clickQrCode(t){if(""===this.href)return this.copyText(this.value),t.preventDefault(),t.stopPropagation(),!1},async writeNfcTag(){try{if(!this.nfcSupported)throw{toString:function(){return"NFC not supported on this device or browser."}};const t=new NDEFReader;this.nfcTagWriting=!0,this.$q.notify({message:"Tap your NFC tag to write the LNURL-withdraw link to it."}),await t.write({records:[{recordType:"url",data:this.value,lang:"en"}]}),this.nfcTagWriting=!1,this.$q.notify({type:"positive",message:"NFC tag written successfully."})}catch(t){this.nfcTagWriting=!1,this.$q.notify({type:"negative",message:t?t.toString():"An unexpected error has occurred."})}},downloadSVG(){const t=this.$refs.qrCode.$el;if(!t)return void console.error("SVG element not found");let e=(new XMLSerializer).serializeToString(t);e.match(/^]+xmlns="http:\/\/www\.w3\.org\/2000\/svg"/)||(e=e.replace(/^({tab:"bech32",lnurl:""}),methods:{setLnurl(){if("bech32"==this.tab){const t=(new TextEncoder).encode(this.url),e=NostrTools.nip19.encodeBytes("lnurl",t);this.lnurl=`lightning:${e.toUpperCase()}`}else"lud17"==this.tab&&(this.url.startsWith("http://")?this.lnurl=this.url.replace("http://",this.prefix+"://"):this.lnurl=this.url.replace("https://",this.prefix+"://"));this.$emit("update:lnurl",this.lnurl)}},watch:{url(){this.setLnurl()},tab(){this.setLnurl()}},created(){this.setLnurl()}}),window.app.component("lnbits-funding-sources",{template:"#lnbits-funding-sources",mixins:[window.windowMixin],props:["form-data","allowed-funding-sources"],methods:{getFundingSourceLabel(t){const e=this.rawFundingSources.find((e=>e[0]===t));return e?e[1]:t},showQRValue(t){this.qrValue=t,this.showQRDialog=!0}},computed:{fundingSources(){let t=[];for(const[e,a,s]of this.rawFundingSources){const a={};if(null!==s)for(let[t,e]of Object.entries(s))a[t]="string"==typeof e?{label:e,value:null}:e||{};t.push([e,a])}return new Map(t)},sortedAllowedFundingSources(){return this.allowedFundingSources.sort()}},data:()=>({hideInput:!0,showQRDialog:!1,qrValue:"",rawFundingSources:[["VoidWallet","Void Wallet",null],["FakeWallet","Fake Wallet",{fake_wallet_secret:"Secret",lnbits_denomination:'"sats" or 3 Letter Custom Denomination'}],["CLNRestWallet","Core Lightning Rest (plugin)",{clnrest_url:"Endpoint",clnrest_ca:"ca.pem",clnrest_cert:"server.pem",clnrest_readonly_rune:"Rune used for readonly requests",clnrest_invoice_rune:"Rune used for creating invoices",clnrest_pay_rune:"Rune used for paying invoices using pay",clnrest_renepay_rune:"Rune used for paying invoices using renepay",clnrest_last_pay_index:"Ignores any invoices paid prior to or including this index. 0 is equivalent to not specifying and negative value is invalid.",clnrest_nodeid:"Node id"}],["CoreLightningWallet","Core Lightning",{corelightning_rpc:"Endpoint",corelightning_pay_command:"Custom Pay Command"}],["CoreLightningRestWallet","Core Lightning Rest (legacy)",{corelightning_rest_url:"Endpoint",corelightning_rest_cert:"Certificate",corelightning_rest_macaroon:"Macaroon"}],["LndRestWallet","Lightning Network Daemon (LND Rest)",{lnd_rest_endpoint:"Endpoint",lnd_rest_cert:"Certificate",lnd_rest_macaroon:"Macaroon",lnd_rest_macaroon_encrypted:"Encrypted Macaroon",lnd_rest_route_hints:"Enable Route Hints",lnd_rest_allow_self_payment:"Allow Self Payment"}],["LndWallet","Lightning Network Daemon (LND)",{lnd_grpc_endpoint:"Endpoint",lnd_grpc_cert:"Certificate",lnd_grpc_port:"Port",lnd_grpc_macaroon:"GRPC Macaroon",lnd_grpc_invoice_macaroon:"GRPC Invoice Macaroon",lnd_grpc_admin_macaroon:"GRPC Admin Macaroon",lnd_grpc_macaroon_encrypted:"Encrypted Macaroon"}],["LnTipsWallet","LN.Tips",{lntips_api_endpoint:"Endpoint",lntips_api_key:"API Key"}],["LNPayWallet","LN Pay",{lnpay_api_endpoint:"Endpoint",lnpay_api_key:"API Key",lnpay_wallet_key:"Wallet Key"}],["EclairWallet","Eclair (ACINQ)",{eclair_url:"URL",eclair_pass:"Password"}],["LNbitsWallet","LNbits",{lnbits_endpoint:"Endpoint",lnbits_key:"Admin Key"}],["BlinkWallet","Blink",{blink_api_endpoint:"Endpoint",blink_ws_endpoint:"WebSocket",blink_token:"Key"}],["AlbyWallet","Alby",{alby_api_endpoint:"Endpoint",alby_access_token:"Key"}],["BoltzWallet","Boltz",{boltz_client_endpoint:"Endpoint",boltz_client_macaroon:"Admin Macaroon path or hex",boltz_client_cert:"Certificate path or hex",boltz_client_wallet:"Wallet Name",boltz_client_password:"Wallet Password (can be empty)",boltz_mnemonic:{label:"Liquid mnemonic (copy into greenwallet)",readonly:!0,copy:!0,qrcode:!0}}],["ZBDWallet","ZBD",{zbd_api_endpoint:"Endpoint",zbd_api_key:"Key"}],["PhoenixdWallet","Phoenixd",{phoenixd_api_endpoint:"Endpoint",phoenixd_api_password:"Key"}],["OpenNodeWallet","OpenNode",{opennode_api_endpoint:"Endpoint",opennode_key:"Key"}],["ClicheWallet","Cliche (NBD)",{cliche_endpoint:"Endpoint"}],["SparkWallet","Spark",{spark_url:"Endpoint",spark_token:"Token"}],["NWCWallet","Nostr Wallet Connect",{nwc_pairing_url:"Pairing URL"}],["BreezSdkWallet","Breez SDK",{breez_api_key:"Breez API Key",breez_greenlight_seed:"Greenlight Seed",breez_greenlight_device_key:"Greenlight Device Key",breez_greenlight_device_cert:"Greenlight Device Cert",breez_greenlight_invite_code:"Greenlight Invite Code"}],["StrikeWallet","Strike (alpha)",{strike_api_endpoint:"API Endpoint",strike_api_key:"API Key"}],["BreezLiquidSdkWallet","Breez Liquid SDK",{breez_liquid_api_key:"Breez API Key (can be empty)",breez_liquid_seed:"Liquid seed phrase",breez_liquid_fee_offset_sat:"Offset amount in sats to increase fee limit"}]]})}),window.app.component("lnbits-extension-settings-form",{name:"lnbits-extension-settings-form",template:"#lnbits-extension-settings-form",props:["options","adminkey","endpoint"],methods:{async updateSettings(){if(!this.settings)return Quasar.Notify.create({message:"No settings to update",type:"negative"});try{const{data:t}=await LNbits.api.request("PUT",this.endpoint,this.adminkey,this.settings);this.settings=t}catch(t){LNbits.utils.notifyApiError(t)}},async getSettings(){try{const{data:t}=await LNbits.api.request("GET",this.endpoint,this.adminkey);this.settings=t}catch(t){LNbits.utils.notifyApiError(t)}},async resetSettings(){LNbits.utils.confirmDialog("Are you sure you want to reset the settings?").onOk((async()=>{try{await LNbits.api.request("DELETE",this.endpoint,this.adminkey),await this.getSettings()}catch(t){LNbits.utils.notifyApiError(t)}}))}},async created(){await this.getSettings()},data:()=>({settings:void 0})}),window.app.component("lnbits-extension-settings-btn-dialog",{template:"#lnbits-extension-settings-btn-dialog",name:"lnbits-extension-settings-btn-dialog",props:["options","adminkey","endpoint"],data:()=>({show:!1})}),window.app.component("lnbits-data-fields",{name:"lnbits-data-fields",template:"#lnbits-data-fields",props:["fields","hide-advanced"],data:()=>({fieldTypes:[{label:"Text",value:"str"},{label:"Integer",value:"int"},{label:"Float",value:"float"},{label:"Boolean",value:"bool"},{label:"Date Time",value:"datetime"},{label:"JSON",value:"json"},{label:"Wallet Select",value:"wallet"},{label:"Currency Select",value:"currency"}],fieldsTable:{columns:[{name:"name",align:"left",label:"Field Name",field:"name",sortable:!0},{name:"type",align:"left",label:"Type",field:"type",sortable:!1},{name:"label",align:"left",label:"UI Label",field:"label",sortable:!0},{name:"hint",align:"left",label:"UI Hint",field:"hint",sortable:!1},{name:"optional",align:"left",label:"Optional",field:"optional",sortable:!1}],pagination:{sortBy:"name",rowsPerPage:100,page:1,rowsNumber:100},search:null,hideEmpty:!0}}),methods:{addField:function(){this.fields.push({name:"field_name_"+(this.fields.length+1),type:"text",label:"",hint:"",optional:!0,sortable:!0,searchable:!0,editable:!0,fields:[]})},removeField:function(t){const e=this.fields.indexOf(t);e>-1&&this.fields.splice(e,1)}},async created(){this.hideAdvanced||this.fieldsTable.columns.push({name:"editable",align:"left",label:"UI Editable",field:"editable",sortable:!1},{name:"sortable",align:"left",label:"Sortable",field:"sortable",sortable:!1},{name:"searchable",align:"left",label:"Searchable",field:"searchable",sortable:!1})}}),window.app.component("payment-list",{name:"payment-list",template:"#payment-list",props:["update","lazy","wallet"],mixins:[window.windowMixin],data(){return{denomination:LNBITS_DENOMINATION,payments:[],paymentsTable:{columns:[{name:"time",align:"left",label:this.$t("memo")+"/"+this.$t("date"),field:"date",sortable:!0},{name:"amount",align:"right",label:this.$t("amount")+" ("+LNBITS_DENOMINATION+")",field:"sat",sortable:!0}],pagination:{rowsPerPage:10,page:1,sortBy:"time",descending:!0,rowsNumber:10},search:"",filter:{"status[ne]":"failed"},loading:!1},searchDate:{from:null,to:null},searchStatus:{success:!0,pending:!0,failed:!1,incoming:!0,outgoing:!0},exportTagName:"",exportPaymentTagList:[],paymentsCSV:{columns:[{name:"pending",align:"left",label:"Pending",field:"pending"},{name:"memo",align:"left",label:this.$t("memo"),field:"memo"},{name:"time",align:"left",label:this.$t("date"),field:"date",sortable:!0},{name:"amount",align:"right",label:this.$t("amount")+" ("+LNBITS_DENOMINATION+")",field:"sat",sortable:!0},{name:"fee",align:"right",label:this.$t("fee")+" (m"+LNBITS_DENOMINATION+")",field:"fee"},{name:"tag",align:"right",label:this.$t("tag"),field:"tag"},{name:"payment_hash",align:"right",label:this.$t("payment_hash"),field:"payment_hash"},{name:"payment_proof",align:"right",label:this.$t("payment_proof"),field:"payment_proof"},{name:"webhook",align:"right",label:this.$t("webhook"),field:"webhook"},{name:"fiat_currency",align:"right",label:"Fiat Currency",field:t=>t.extra.wallet_fiat_currency},{name:"fiat_amount",align:"right",label:"Fiat Amount",field:t=>t.extra.wallet_fiat_amount}],preimage:null,loading:!1},hodlInvoice:{show:!1,payment:null,preimage:null}}},computed:{currentWallet(){return this.wallet||this.g.wallet},filteredPayments(){const t=this.paymentsTable.search;return t&&""!==t?LNbits.utils.search(this.payments,t):this.payments},paymentsOmitter(){return this.$q.screen.lt.md&&this.mobileSimple?this.payments.length>0?[this.payments[0]]:[]:this.payments},pendingPaymentsExist(){return-1!==this.payments.findIndex((t=>t.pending))}},methods:{searchByDate(){"string"==typeof this.searchDate&&(this.searchDate={from:this.searchDate,to:this.searchDate}),this.searchDate.from&&(this.paymentsTable.filter["time[ge]"]=this.searchDate.from+"T00:00:00"),this.searchDate.to&&(this.paymentsTable.filter["time[le]"]=this.searchDate.to+"T23:59:59"),this.fetchPayments()},clearDateSeach(){this.searchDate={from:null,to:null},delete this.paymentsTable.filter["time[ge]"],delete this.paymentsTable.filter["time[le]"],this.fetchPayments()},fetchPayments(t){this.$emit("filter-changed",{...this.paymentsTable.filter});const e=LNbits.utils.prepareFilterQuery(this.paymentsTable,t);return LNbits.api.getPayments(this.currentWallet,e).then((t=>{this.paymentsTable.loading=!1,this.paymentsTable.pagination.rowsNumber=t.data.total,this.payments=t.data.data.map((t=>LNbits.map.payment(t)))})).catch((t=>{this.paymentsTable.loading=!1,g.user.admin?this.fetchPaymentsAsAdmin(this.currentWallet.id,e):LNbits.utils.notifyApiError(t)}))},fetchPaymentsAsAdmin(t,e){return e=(e||"")+"&wallet_id="+t,LNbits.api.request("GET","/api/v1/payments/all/paginated?"+e).then((t=>{this.paymentsTable.loading=!1,this.paymentsTable.pagination.rowsNumber=t.data.total,this.payments=t.data.data.map((t=>LNbits.map.payment(t)))})).catch((t=>{this.paymentsTable.loading=!1,LNbits.utils.notifyApiError(t)}))},checkPayment(t){LNbits.api.getPayment(this.g.wallet,t).then((t=>{this.update=!this.update,"success"==t.data.status&&Quasar.Notify.create({type:"positive",message:this.$t("payment_successful")}),"pending"==t.data.status&&Quasar.Notify.create({type:"info",message:this.$t("payment_pending")})})).catch(LNbits.utils.notifyApiError)},showHoldInvoiceDialog(t){this.hodlInvoice.show=!0,this.hodlInvoice.preimage="",this.hodlInvoice.payment=t},cancelHoldInvoice(t){LNbits.api.cancelInvoice(this.g.wallet,t).then((()=>{this.update=!this.update,Quasar.Notify.create({type:"positive",message:this.$t("invoice_cancelled")})})).catch(LNbits.utils.notifyApiError)},settleHoldInvoice(t){LNbits.api.settleInvoice(this.g.wallet,t).then((()=>{this.update=!this.update,Quasar.Notify.create({type:"positive",message:this.$t("invoice_settled")})})).catch(LNbits.utils.notifyApiError)},paymentTableRowKey:t=>t.payment_hash+t.amount,exportCSV(t=!1){const e=this.paymentsTable.pagination,a={sortby:e.sortBy??"time",direction:e.descending?"desc":"asc"},s=new URLSearchParams(a);LNbits.api.getPayments(this.g.wallet,s).then((e=>{let a=e.data.data.map(LNbits.map.payment),s=this.paymentsCSV.columns;if(t){this.exportPaymentTagList.length&&(a=a.filter((t=>this.exportPaymentTagList.includes(t.tag))));const t=Object.keys(a.reduce(((t,e)=>({...t,...e.details})),{})).map((t=>({name:t,align:"right",label:t.charAt(0).toUpperCase()+t.slice(1).replace(/([A-Z])/g," $1"),field:e=>e.details[t],format:t=>"object"==typeof t?JSON.stringify(t):t})));s=this.paymentsCSV.columns.concat(t)}LNbits.utils.exportCSV(s,a,this.g.wallet.name+"-payments")}))},addFilterTag(){if(!this.exportTagName)return;const t=this.exportTagName.trim();this.exportPaymentTagList=this.exportPaymentTagList.filter((e=>e!==t)),this.exportPaymentTagList.push(t),this.exportTagName=""},removeExportTag(t){this.exportPaymentTagList=this.exportPaymentTagList.filter((e=>e!==t))},formatCurrency(t,e){try{return LNbits.utils.formatCurrency(t,e)}catch(e){return console.error(e),`${t} ???`}},handleFilterChanged(){const{success:t,pending:e,failed:a,incoming:s,outgoing:n}=this.searchStatus;delete this.paymentsTable.filter["status[ne]"],delete this.paymentsTable.filter["status[eq]"],t&&e&&a||(t&&e?this.paymentsTable.filter["status[ne]"]="failed":t&&a?this.paymentsTable.filter["status[ne]"]="pending":a&&e?this.paymentsTable.filter["status[ne]"]="success":t?this.paymentsTable.filter["status[eq]"]="success":e?this.paymentsTable.filter["status[eq]"]="pending":a&&(this.paymentsTable.filter["status[eq]"]="failed")),delete this.paymentsTable.filter["amount[ge]"],delete this.paymentsTable.filter["amount[le]"],s&&n||(s?this.paymentsTable.filter["amount[ge]"]=0:n&&(this.paymentsTable.filter["amount[le]"]=0))}},watch:{"paymentsTable.search":{handler(){const t={};this.paymentsTable.search&&(t.search=this.paymentsTable.search),this.fetchPayments()}},lazy(t){!0===t&&this.fetchPayments()},update(){this.fetchPayments()},"g.updatePayments"(){this.fetchPayments()},"g.wallet":{handler(t){this.fetchPayments()},deep:!0}},created(){void 0===this.lazy&&this.fetchPayments()}}),window.app.component(QrcodeVue),window.app.component("lnbits-extension-rating",{template:"#lnbits-extension-rating",name:"lnbits-extension-rating",props:["rating"]}),window.app.component("lnbits-fsat",{template:"{{ fsat }}",props:{amount:{type:Number,default:0}},computed:{fsat(){return LNbits.utils.formatSat(this.amount)}}}),window.app.component("lnbits-wallet-list",{mixins:[window.windowMixin],template:"#lnbits-wallet-list",props:["balance"],data:()=>({activeWallet:null,balance:0,showForm:!1,walletName:"",LNBITS_DENOMINATION:LNBITS_DENOMINATION}),methods:{createWallet(){LNbits.api.createWallet(this.g.user.wallets[0],this.walletName)}},created(){document.addEventListener("updateWalletBalance",this.updateWalletBalance)}}),window.app.component("lnbits-extension-list",{mixins:[window.windowMixin],template:"#lnbits-extension-list",data:()=>({extensions:[],searchTerm:""}),watch:{"g.user.extensions":{handler(t){this.loadExtensions()},deep:!0}},computed:{userExtensions(){return this.updateUserExtensions(this.searchTerm)}},methods:{async loadExtensions(){try{const{data:t}=await LNbits.api.request("GET","/api/v1/extension");this.extensions=t.map((t=>LNbits.map.extension(t))).sort(((t,e)=>t.name.localeCompare(e.name)))}catch(t){LNbits.utils.notifyApiError(t)}},updateUserExtensions(t){const e=window.location.pathname,a=this.g.user.extensions;return this.extensions.filter((t=>a.includes(t.code))).filter((e=>!t||`${e.code} ${e.name} ${e.short_description} ${e.url}`.toLocaleLowerCase().includes(t.toLocaleLowerCase()))).map((t=>(t.isActive=e.startsWith(t.url),t)))}},async created(){await this.loadExtensions()}}),window.app.component("lnbits-manage",{mixins:[window.windowMixin],template:"#lnbits-manage",props:["showAdmin","showNode","showExtensions","showUsers","showAudit"],methods:{isActive:t=>window.location.pathname===t},data:()=>({extensions:[]})}),window.app.component("lnbits-payment-details",{mixins:[window.windowMixin],template:"#lnbits-payment-details",props:["payment"],mixins:[window.windowMixin],data:()=>({LNBITS_DENOMINATION:LNBITS_DENOMINATION}),computed:{hasPreimage(){return this.payment.preimage&&"0000000000000000000000000000000000000000000000000000000000000000"!==this.payment.preimage},hasExpiry(){return!!this.payment.expiry},hasSuccessAction(){return this.hasPreimage&&this.payment.extra&&this.payment.extra.success_action},webhookStatusColor(){return this.payment.webhook_status>=300||this.payment.webhook_status<0?"red-10":this.payment.webhook_status?"green-10":"cyan-7"},webhookStatusText(){return this.payment.webhook_status?this.payment.webhook_status:"not sent yet"},hasTag(){return this.payment.extra&&!!this.payment.extra.tag},extras(){if(!this.payment.extra)return[];let t=_.omit(this.payment.extra,["tag","success_action"]);return Object.keys(t).map((e=>({key:e,value:t[e]})))}}}),window.app.component("lnbits-lnurlpay-success-action",{mixins:[window.windowMixin],template:"#lnbits-lnurlpay-success-action",props:["payment","success_action"],data(){return{decryptedValue:this.success_action.ciphertext}},mounted(){if("aes"!==this.success_action.tag)return null;decryptLnurlPayAES(this.success_action,this.payment.preimage).then((t=>{this.decryptedValue=t}))}}),window.app.component("lnbits-notifications-btn",{template:"#lnbits-notifications-btn",mixins:[window.windowMixin],props:["pubkey"],data:()=>({isSupported:!1,isSubscribed:!1,isPermissionGranted:!1,isPermissionDenied:!1}),methods:{urlB64ToUint8Array(t){const e=(t+"=".repeat((4-t.length%4)%4)).replace(/\-/g,"+").replace(/_/g,"/"),a=atob(e),s=new Uint8Array(a.length);for(let t=0;te!==t)),this.$q.localStorage.set("lnbits.webpush.subscribedUsers",JSON.stringify(e))},isUserSubscribed(t){return(JSON.parse(this.$q.localStorage.getItem("lnbits.webpush.subscribedUsers"))||[]).includes(t)},subscribe(){this.isSupported&&!this.isPermissionDenied&&(Notification.requestPermission().then((t=>{this.isPermissionGranted="granted"===t,this.isPermissionDenied="denied"===t})).catch(console.log),navigator.serviceWorker.ready.then((t=>{navigator.serviceWorker.getRegistration().then((t=>{t.pushManager.getSubscription().then((e=>{if(null===e||!this.isUserSubscribed(this.g.user.id)){const e={applicationServerKey:this.urlB64ToUint8Array(this.pubkey),userVisibleOnly:!0};t.pushManager.subscribe(e).then((t=>{LNbits.api.request("POST","/api/v1/webpush",null,{subscription:JSON.stringify(t)}).then((t=>{this.saveUserSubscribed(t.data.user),this.isSubscribed=!0})).catch(LNbits.utils.notifyApiError)}))}})).catch(console.log)}))})))},unsubscribe(){navigator.serviceWorker.ready.then((t=>{t.pushManager.getSubscription().then((t=>{t&&LNbits.api.request("DELETE","/api/v1/webpush?endpoint="+btoa(t.endpoint),null).then((()=>{this.removeUserSubscribed(this.g.user.id),this.isSubscribed=!1})).catch(LNbits.utils.notifyApiError)}))})).catch(console.log)},checkSupported(){let t="https:"===window.location.protocol,e="serviceWorker"in navigator,a="Notification"in window,s="PushManager"in window;return this.isSupported=t&&e&&a&&s,this.isSupported||console.log("Notifications disabled because requirements are not met:",{HTTPS:t,"Service Worker API":e,"Notification API":a,"Push API":s}),this.isSupported},async updateSubscriptionStatus(){await navigator.serviceWorker.ready.then((t=>{t.pushManager.getSubscription().then((t=>{this.isSubscribed=!!t&&this.isUserSubscribed(this.g.user.id)}))})).catch(console.log)}},created(){this.isPermissionDenied="denied"===Notification.permission,this.checkSupported()&&this.updateSubscriptionStatus()}}),window.app.component("lnbits-dynamic-fields",{template:"#lnbits-dynamic-fields",mixins:[window.windowMixin],props:["options","modelValue"],data:()=>({formData:null,rules:[t=>!!t||"Field is required"]}),methods:{applyRules(t){return t?this.rules:[]},buildData(t,e={}){return t.reduce(((t,a)=>(a.options?.length?t[a.name]=this.buildData(a.options,e[a.name]):t[a.name]=e[a.name]??a.default,t)),{})},handleValueChanged(){this.$emit("update:model-value",this.formData)}},created(){this.formData=this.buildData(this.options,this.modelValue)}}),window.app.component("lnbits-dynamic-chips",{template:"#lnbits-dynamic-chips",mixins:[window.windowMixin],props:["modelValue"],data:()=>({chip:"",chips:[]}),methods:{addChip(){this.chip&&(this.chips.push(this.chip),this.chip="",this.$emit("update:model-value",this.chips.join(",")))},removeChip(t){this.chips.splice(t,1),this.$emit("update:model-value",this.chips.join(","))}},created(){"string"==typeof this.modelValue?this.chips=this.modelValue.split(","):this.chips=[...this.modelValue]}}),window.app.component("lnbits-update-balance",{template:"#lnbits-update-balance",mixins:[window.windowMixin],props:["wallet_id","small_btn"],computed:{denomination:()=>LNBITS_DENOMINATION,admin:()=>user.super_user},data:()=>({credit:0}),methods:{updateBalance(t){LNbits.api.updateBalance(t.value,this.wallet_id).then((e=>{if(!0!==e.data.success)throw new Error(e.data);credit=parseInt(t.value),Quasar.Notify.create({type:"positive",message:this.$t("credit_ok",{amount:credit}),icon:null}),this.credit=0,t.value=0,t.set()})).catch(LNbits.utils.notifyApiError)}}}),window.app.component("user-id-only",{template:"#user-id-only",mixins:[window.windowMixin],props:{allowed_new_users:Boolean,authAction:String,authMethod:String,usr:String,wallet:String},data(){return{user:this.usr,walletName:this.wallet}},methods:{showLogin(t){this.$emit("show-login",t)},showRegister(t){this.$emit("show-register",t)},loginUsr(){this.$emit("update:usr",this.user),this.$emit("login-usr")},createWallet(){this.$emit("update:wallet",this.walletName),this.$emit("create-wallet")}},computed:{showInstantLogin(){return"username-password"!==this.authMethod||"register"!==this.authAction}},created(){}}),window.app.component("username-password",{template:"#username-password",mixins:[window.windowMixin],props:{allowed_new_users:Boolean,authMethods:Array,authAction:String,username:String,password_1:String,password_2:String,resetKey:String},data(){return{oauth:["nostr-auth-nip98","google-auth","github-auth","keycloak-auth"],username:this.userName,password:this.password_1,passwordRepeat:this.password_2,reset_key:this.resetKey,keycloakOrg:LNBITS_AUTH_KEYCLOAK_ORG||"Keycloak",keycloakIcon:LNBITS_AUTH_KEYCLOAK_ICON}},methods:{login(){this.$emit("update:userName",this.username),this.$emit("update:password_1",this.password),this.$emit("login")},register(){this.$emit("update:userName",this.username),this.$emit("update:password_1",this.password),this.$emit("update:password_2",this.passwordRepeat),this.$emit("register")},reset(){this.$emit("update:resetKey",this.reset_key),this.$emit("update:password_1",this.password),this.$emit("update:password_2",this.passwordRepeat),this.$emit("reset")},validateUsername:t=>new RegExp("^(?=[a-zA-Z0-9._]{2,20}$)(?!.*[_.]{2})[^_.].*[^_.]$").test(t),async signInWithNostr(){try{const t=await this.createNostrToken();if(!t)return;resp=await LNbits.api.loginByProvider("nostr",{Authorization:t},{}),window.location.href="/wallet"}catch(t){console.warn(t);const e=t?.response?.data?.detail||`${t}`;Quasar.Notify.create({type:"negative",message:"Failed to sign in with Nostr.",caption:e})}},async createNostrToken(){try{if(!window.nostr?.signEvent)return void Quasar.Notify.create({type:"negative",message:"No Nostr signing app detected.",caption:'Is "window.nostr" present?'});const t=`${window.location}nostr`,e="POST",a=await NostrTools.nip98.getToken(t,e,(t=>async function(t){try{const{data:e}=await LNbits.api.getServerHealth();return t.created_at=e.server_time,await window.nostr.signEvent(t)}catch(t){console.error(t),Quasar.Notify.create({type:"negative",message:"Failed to sign nostr event.",caption:`${t}`})}}(t)),!0);if(!await NostrTools.nip98.validateToken(a,t,e))throw new Error("Invalid signed token!");return a}catch(t){console.warn(t),Quasar.Notify.create({type:"negative",message:"Failed create Nostr event.",caption:`${t}`})}}},computed:{showOauth(){return this.oauth.some((t=>this.authMethods.includes(t)))}},created(){}}),window.app.component("separator-text",{template:"#separator-text",props:{text:String,uppercase:{type:Boolean,default:!1},color:{type:String,default:"grey"}}}),window.app.component("lnbits-node-ranks",{props:["ranks"],data:()=>({stats:[{label:"Capacity",key:"capacity"},{label:"Channels",key:"channelcount"},{label:"Age",key:"age"},{label:"Growth",key:"growth"},{label:"Availability",key:"availability"}]}),template:"\n \n
\n
1ml Node Rank
\n
\n
\n
{{ stat.label }}
\n
\n {{ (ranks && ranks[stat.key]) ?? '-' }}\n
\n
\n
\n
\n
\n "}),window.app.component("lnbits-channel-stats",{props:["stats"],data:()=>({states:[{label:"Active",value:"active",color:"green"},{label:"Pending",value:"pending",color:"orange"},{label:"Inactive",value:"inactive",color:"grey"},{label:"Closed",value:"closed",color:"red"}]}),template:"\n \n
\n
Channels
\n
\n
\n
\n {{ state.label }}\n
\n
\n {{ (stats?.counts && stats.counts[state.value]) ?? \"-\" }}\n
\n
\n
\n
\n
\n "}),window.app.component("lnbits-stat",{props:["title","amount","msat","btc"],computed:{value(){return this.amount??(this.btc?LNbits.utils.formatSat(this.btc):LNbits.utils.formatMsat(this.msat))}},template:"\n \n \n
\n {{ title }}\n
\n
\n {{ value }}\n sats\n BTC\n
\n
\n
\n "}),window.app.component("lnbits-node-qrcode",{props:["info"],mixins:[window.windowMixin],template:'\n \n \n
\n
\n \n
\n No addresses available\n
\n
\n
\n
\n \n Public Key Click to copy \n \n \n
\n '}),window.app.component("lnbits-channel-balance",{props:["balance","color"],methods:{formatMsat:t=>LNbits.utils.formatMsat(t)},template:'\n
\n
\n \n Local: {{ formatMsat(balance.local_msat) }}\n sats\n \n \n Remote: {{ formatMsat(balance.remote_msat) }}\n sats\n \n
\n\n \n
\n \n {{ balance.alias }}\n \n
\n \n
\n '}),window.app.component("lnbits-date",{props:["ts"],computed:{date(){return LNbits.utils.formatDate(this.ts)},dateFrom(){return moment.utc(this.date).local().fromNow()}},template:"\n
\n {{ this.date }}\n {{ this.dateFrom }}\n
\n "}),window.app.component("lnbits-node-info",{props:["info"],data:()=>({showDialog:!1}),mixins:[window.windowMixin],methods:{shortenNodeId:t=>t?t.substring(0,5)+"..."+t.substring(t.length-5):"..."},template:"\n
\n
{{ this.info.alias }}
\n
\n
{{ this.info.backend_name }}
\n \n #{{ this.info.color }}\n \n
{{ shortenNodeId(this.info.id) }}
\n \n \n
\n \n \n \n
\n "}),window.app.component("lnbits-stat",{props:["title","amount","msat","btc"],computed:{value(){return this.amount??(this.btc?LNbits.utils.formatSat(this.btc):LNbits.utils.formatMsat(this.msat))}},template:"\n \n \n
\n {{ title }}\n
\n
\n {{ value }}\n sats\n BTC\n
\n
\n
\n "});const DynamicComponent={props:{fetchUrl:{type:String,required:!0},scripts:{type:Array,default:()=>[]}},data:()=>({keys:[]}),async mounted(){await this.loadDynamicContent()},methods:{loadScript:async t=>new Promise(((e,a)=>{const s=document.querySelector(`script[src="${t}"]`);s&&s.remove();const n=document.createElement("script");n.src=t,n.async=!0,n.onload=e,n.onerror=()=>a(new Error(`Failed to load script: ${t}`)),document.head.appendChild(n)})),async loadDynamicContent(){this.$q.loading.show();try{const t=this.fetchUrl.split("#")[0],e=await fetch(t,{credentials:"include",headers:{Accept:"text/html","X-Requested-With":"XMLHttpRequest"}}),a=await e.text(),s=new DOMParser,n=s.parseFromString(a,"text/html").querySelector("#window-vars-script");n&&new Function(n.innerHTML)(),await this.loadScript("/static/js/base.js");for(const t of this.scripts)await this.loadScript(t);const i=this.$router.currentRoute.value.meta.previousRouteName;i&&window.app._context.components[i]&&delete window.app._context.components[i];const o=`${this.$route.name}PageLogic`,l=window[o];if(!l)throw new Error(`Component logic '${o}' not found. Ensure it is defined in the script.`);l.mixins=l.mixins||[],window.windowMixin&&l.mixins.push(window.windowMixin),window.app.component(this.$route.name,{...l,template:a}),delete window[o],this.$forceUpdate()}catch(t){console.error("Error loading dynamic content:",t)}finally{this.$q.loading.hide()}}},watch:{$route(t,e){routes.map((t=>t.name)).includes(t.name)?(this.$router.currentRoute.value.meta.previousRouteName=e.name,this.loadDynamicContent()):console.log(`Route '${t.name}' is not valid. Leave this one to Fastapi.`)}},template:'\n \n '},routes=[{path:"/wallet",name:"Wallet",component:DynamicComponent,props:t=>{let e="/wallet";if(Object.keys(t.query).length>0){e+="?";for(const[a,s]of Object.entries(t.query))e+=`${a}=${s}&`;e=e.slice(0,-1)}return{fetchUrl:e,scripts:["/static/js/wallet.js"]}}},{path:"/admin",name:"Admin",component:DynamicComponent,props:{fetchUrl:"/admin",scripts:["/static/js/admin.js"]}},{path:"/users",name:"Users",component:DynamicComponent,props:{fetchUrl:"/users",scripts:["/static/js/users.js"]}},{path:"/audit",name:"Audit",component:DynamicComponent,props:{fetchUrl:"/audit",scripts:["/static/js/audit.js"]}},{path:"/extensions",name:"Extensions",component:DynamicComponent,props:{fetchUrl:"/extensions",scripts:["/static/js/extensions.js"]}},{path:"/extensions/builder",name:"ExtensionsBuilder",component:DynamicComponent,props:{fetchUrl:"/extensions/builder",scripts:["/static/js/extensions_builder.js"]}},{path:"/account",name:"Account",component:DynamicComponent,props:{fetchUrl:"/account",scripts:["/static/js/account.js"]}},{path:"/wallets",name:"Wallets",component:DynamicComponent,props:{fetchUrl:"/wallets",scripts:["/static/js/wallets.js"]}},{path:"/node",name:"Node",component:PageNode},{path:"/node/public",name:"NodePublic",component:PageNodePublic},{path:"/payments",name:"Payments",component:PagePayments}];window.router=VueRouter.createRouter({history:VueRouter.createWebHistory(),routes:routes}),window.app.mixin({computed:{isVueRoute(){const t=window.location.pathname,e=window.router.resolve(t);return e?.matched?.length>0}}}),window.app.use(VueQrcodeReader),window.app.use(Quasar,{config:{loading:{spinner:Quasar.QSpinnerBars}}}),window.app.use(window.i18n),window.app.provide("g",g),window.app.use(window.router),window.app.component("DynamicComponent",DynamicComponent),window.app.mount("#vue"); +window.PagePayments={template:"#page-payments",mixins:[window.windowMixin],data:()=>({payments:[],dailyChartData:[],searchDate:{from:null,to:null},searchData:{wallet_id:null,payment_hash:null,memo:null,internal_memo:null},statusFilters:{success:!0,pending:!0,failed:!0,incoming:!0,outgoing:!0},chartData:{showPaymentStatus:!0,showPaymentTags:!0,showBalance:!0,showWalletsSize:!1,showBalanceInOut:!1,showPaymentCountInOut:!1},searchOptions:{status:[]},paymentsTable:{columns:[{name:"status",align:"left",label:"Status",field:"status",sortable:!1},{name:"created_at",align:"left",label:"Created At",field:"created_at",sortable:!0},{name:"amount",align:"right",label:"Amount",field:"amount",sortable:!0},{name:"amountFiat",align:"right",label:"Fiat",field:"amountFiat",sortable:!1},{name:"fee_sats",align:"left",label:"Fee",field:"fee_sats",sortable:!0},{name:"tag",align:"left",label:"Tag",field:"tag",sortable:!1},{name:"memo",align:"left",label:"Memo",field:"memo",sortable:!1,max_length:20},{name:"internal_memo",align:"left",label:"Internal Memo",field:"internal_memo",sortable:!1,max_length:20},{name:"wallet_id",align:"left",label:"Wallet (ID)",field:"wallet_id",sortable:!1},{name:"payment_hash",align:"left",label:"Payment Hash",field:"payment_hash",sortable:!1}],pagination:{sortBy:"created_at",rowsPerPage:25,page:1,descending:!0,rowsNumber:10},search:null,hideEmpty:!0,loading:!1},chartsReady:!1,showDetails:!1,paymentDetails:null,lnbitsBalance:0}),async mounted(){this.chartsReady=!0,await this.$nextTick(),this.initCharts(),await this.fetchPayments()},computed:{},methods:{async fetchPayments(t){const e=Object.entries(this.searchData).reduce(((t,[e,a])=>a?(t[e]=a,t):t),{});delete e["time[ge]"],delete e["time[le]"],this.searchDate.from&&(e["time[ge]"]=this.searchDate.from+"T00:00:00"),this.searchDate.to&&(e["time[le]"]=this.searchDate.to+"T23:59:59"),this.paymentsTable.filter=e;try{const e=LNbits.utils.prepareFilterQuery(this.paymentsTable,t),{data:a}=await LNbits.api.request("GET",`/api/v1/payments/all/paginated?${e}`);this.paymentsTable.pagination.rowsNumber=a.total,this.payments=a.data.map((t=>(t.extra&&t.extra.tag&&(t.tag=t.extra.tag),t.timeFrom=moment.utc(t.created_at).local().fromNow(),t.outgoing=t.amount<0,t.amount=new Intl.NumberFormat(window.LOCALE).format(t.amount/1e3)+" sats",t.extra?.wallet_fiat_amount&&(t.amountFiat=this.formatCurrency(t.extra.wallet_fiat_amount,t.extra.wallet_fiat_currency)),t.extra?.internal_memo&&(t.internal_memo=t.extra.internal_memo),t.fee_sats=new Intl.NumberFormat(window.LOCALE).format(t.fee/1e3)+" sats",t)))}catch(t){console.error(t),LNbits.utils.notifyApiError(t)}finally{this.updateCharts(t)}},async searchPaymentsBy(t,e){t&&(this.searchData[t]=e),await this.fetchPayments()},clearDateSeach(){this.searchDate={from:null,to:null},delete this.paymentsTable.filter["time[ge]"],delete this.paymentsTable.filter["time[le]"],this.fetchPayments()},searchByDate(){"string"==typeof this.searchDate&&(this.searchDate={from:this.searchDate,to:this.searchDate}),this.searchDate.from&&(this.paymentsTable.filter["time[ge]"]=this.searchDate.from+"T00:00:00"),this.searchDate.to&&(this.paymentsTable.filter["time[le]"]=this.searchDate.to+"T23:59:59"),this.fetchPayments()},handleFilterChanged(){const{success:t,pending:e,failed:a,incoming:s,outgoing:i}=this.statusFilters;delete this.searchData["status[ne]"],delete this.searchData["status[eq]"],t&&e&&a||(t&&e?this.searchData["status[ne]"]="failed":t&&a?this.searchData["status[ne]"]="pending":a&&e?this.searchData["status[ne]"]="success":t?this.searchData["status[eq]"]="success":e?this.searchData["status[eq]"]="pending":a&&(this.searchData["status[eq]"]="failed")),delete this.searchData["amount[ge]"],delete this.searchData["amount[le]"],s&&i||(s?this.searchData["amount[ge]"]="0":i&&(this.searchData["amount[le]"]="0")),this.fetchPayments()},showDetailsToggle(t){return this.paymentDetails=t,this.showDetails=!this.showDetails},formatDate:t=>LNbits.utils.formatDateString(t),formatCurrency(t,e){try{return LNbits.utils.formatCurrency(t,e)}catch(e){return console.error(e),`${t} ???`}},shortify:(t,e=10)=>(valueLength=(t||"").length,valueLength<=e?t:`${t.substring(0,5)}...${t.substring(valueLength-5,valueLength)}`),async updateCharts(t){let e=LNbits.utils.prepareFilterQuery(this.paymentsTable,t);try{const{data:t}=await LNbits.api.request("GET",`/api/v1/payments/stats/count?${e}&count_by=status`);t.sort(((t,e)=>t.field-e.field)).reverse(),this.searchOptions.status=t.map((t=>t.field)),this.paymentsStatusChart.data.datasets[0].data=t.map((t=>t.total)),this.paymentsStatusChart.data.labels=[...this.searchOptions.status],this.paymentsStatusChart.update()}catch(t){console.warn(t),LNbits.utils.notifyApiError(t)}try{const{data:t}=await LNbits.api.request("GET",`/api/v1/payments/stats/wallets?${e}`),a=t.map((t=>t.balance/t.payments_count)),s=Math.min(...a),i=Math.max(...a),n=t=>Math.floor(3+22*(t-s)/(i-s)),o=this.randomColors(20),l=t.map(((t,e)=>({data:[{x:t.payments_count,y:t.balance,r:n(Math.max(t.balance/t.payments_count,5))}],label:t.wallet_name,wallet_id:t.wallet_id,backgroundColor:o[e%100],hoverOffset:4})));this.paymentsWalletsChart.data.datasets=l,this.paymentsWalletsChart.update()}catch(t){console.warn(t),LNbits.utils.notifyApiError(t)}try{const{data:t}=await LNbits.api.request("GET",`/api/v1/payments/stats/count?${e}&count_by=tag`);this.searchOptions.tag=t.map((t=>t.field)),this.searchOptions.status.sort(),this.paymentsTagsChart.data.datasets[0].data=t.map((t=>t.total)),this.paymentsTagsChart.data.labels=t.map((t=>t.field||"core")),this.paymentsTagsChart.update()}catch(t){console.warn(t),LNbits.utils.notifyApiError(t)}try{const e=Object.entries(this.searchData).reduce(((t,[e,a])=>a?(t[e]=a,t):t),{}),a={...this.paymentsTable,filter:e},s=LNbits.utils.prepareFilterQuery(a,t);let{data:i}=await LNbits.api.request("GET",`/api/v1/payments/stats/daily?${s}`);const n=this.searchDate.from+"T00:00:00",o=this.searchDate.to+"T23:59:59";this.lnbitsBalance=i.length?i[i.length-1].balance:0,i=i.filter((t=>this.searchDate.from&&this.searchDate.to?t.date>=n&&t.date<=o:this.searchDate.from?t.date>=n:!this.searchDate.to||t.date<=o)),this.paymentsDailyChart.data.datasets=[{label:"Balance",data:i.map((t=>t.balance)),pointStyle:!1,borderWidth:2,tension:.7,fill:1},{label:"Fees",data:i.map((t=>t.fee)),pointStyle:!1,borderWidth:1,tension:.4,fill:1}],this.paymentsDailyChart.data.labels=i.map((t=>t.date.substring(0,10))),this.paymentsDailyChart.update(),this.paymentsBalanceInOutChart.data.datasets=[{label:"Incoming Payments Balance",data:i.map((t=>t.balance_in))},{label:"Outgoing Payments Balance",data:i.map((t=>t.balance_out))}],this.paymentsBalanceInOutChart.data.labels=i.map((t=>t.date.substring(0,10))),this.paymentsBalanceInOutChart.update(),this.paymentsCountInOutChart.data.datasets=[{label:"Incoming Payments Count",data:i.map((t=>t.count_in))},{label:"Outgoing Payments Count",data:i.map((t=>-t.count_out))}],this.paymentsCountInOutChart.data.labels=i.map((t=>t.date.substring(0,10))),this.paymentsCountInOutChart.update()}catch(t){console.warn(t),LNbits.utils.notifyApiError(t)}},async initCharts(){const t=this.$q.localStorage.getItem("lnbits.payments.chartData")||{};this.chartData={...this.chartData,...t},this.chartsReady?(this.paymentsStatusChart=new Chart(this.$refs.paymentsStatusChart.getContext("2d"),{type:"doughnut",options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:!1}},onClick:(t,e,a)=>{if(e[0]){const t=e[0].index;this.searchPaymentsBy("status",a.data.labels[t])}}},data:{datasets:[{label:"",data:[],backgroundColor:["rgb(0, 205, 86)","rgb(64, 72, 78)","rgb(255, 99, 132)"],hoverOffset:4}]}}),this.paymentsWalletsChart=new Chart(this.$refs.paymentsWalletsChart.getContext("2d"),{type:"bubble",options:{responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1},title:{display:!1}},onClick:(t,e,a)=>{if(e[0]){const t=e[0].datasetIndex;this.searchPaymentsBy("wallet_id",a.data.datasets[t].wallet_id)}}},data:{datasets:[{label:"",data:[],backgroundColor:this.randomColors(20),hoverOffset:4}]}}),this.paymentsTagsChart=new Chart(this.$refs.paymentsTagsChart.getContext("2d"),{type:"pie",options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:!1},legend:{display:!1,title:{display:!1,text:"Tags"}}},onClick:(t,e,a)=>{if(e[0]){const t=e[0].index;this.searchPaymentsBy("tag",a.data.labels[t])}}},data:{datasets:[{label:"",data:[],backgroundColor:this.randomColors(10),hoverOffset:4}]}}),this.paymentsDailyChart=new Chart(this.$refs.paymentsDailyChart.getContext("2d"),{type:"line",options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:!1},legend:{display:!0,title:{display:!1,text:"Tags"}}}},data:{datasets:[{label:"",data:[],backgroundColor:this.randomColors(10),hoverOffset:4}]}}),this.paymentsBalanceInOutChart=new Chart(this.$refs.paymentsBalanceInOutChart.getContext("2d"),{type:"bar",options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:!1},legend:{display:!0,title:{display:!1,text:"Tags"}}},scales:{x:{stacked:!0},y:{stacked:!0}}},data:{datasets:[{label:"",data:[],backgroundColor:this.randomColors(50),hoverOffset:4}]}}),this.paymentsCountInOutChart=new Chart(this.$refs.paymentsCountInOutChart.getContext("2d"),{type:"bar",options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:!1},legend:{display:!0,title:{display:!1,text:""}}},scales:{x:{stacked:!0},y:{stacked:!0}}},data:{datasets:[{label:"",data:[],backgroundColor:this.randomColors(80),hoverOffset:4}]}})):console.warn("Charts are not ready yet. Initialization delayed.")},saveChartsPreferences(){this.$q.localStorage.set("lnbits.payments.chartData",this.chartData)},randomColors(t=1){const e=[];for(let a=1;a<=10;a++)for(let s=1;s<=10;s++)e.push(`rgb(${s*t*33%200}, ${71*(a+s+t)%255}, ${(a+30*t)%255})`);return e}}},window.PageNode={mixins:[window.windowMixin],template:"#page-node",config:{globalProperties:{LNbits:LNbits,msg:"hello"}},data(){return{isSuperUser:!1,wallet:{},tab:"dashboard",payments:1e3,info:{},channel_stats:{},channels:{data:[],filter:""},activeBalance:{},ranks:{},peers:{data:[],filter:""},connectPeerDialog:{show:!1,data:{}},setFeeDialog:{show:!1,data:{fee_ppm:0,fee_base_msat:0}},openChannelDialog:{show:!1,data:{}},closeChannelDialog:{show:!1,data:{}},nodeInfoDialog:{show:!1,data:{}},transactionDetailsDialog:{show:!1,data:{}},states:[{label:"Active",value:"active",color:"green"},{label:"Pending",value:"pending",color:"orange"},{label:"Inactive",value:"inactive",color:"grey"},{label:"Closed",value:"closed",color:"red"}],stateFilters:[{label:"Active",value:"active"},{label:"Pending",value:"pending"}],paymentsTable:{data:[],columns:[{name:"pending",label:""},{name:"date",align:"left",label:this.$t("date"),field:"date",sortable:!0},{name:"sat",align:"right",label:this.$t("amount")+" ("+LNBITS_DENOMINATION+")",field:t=>this.formatMsat(t.amount),sortable:!0},{name:"fee",align:"right",label:this.$t("fee")+" (m"+LNBITS_DENOMINATION+")",field:"fee"},{name:"destination",align:"right",label:"Destination",field:"destination"},{name:"memo",align:"left",label:this.$t("memo"),field:"memo"}],pagination:{rowsPerPage:10,page:1,rowsNumber:10},filter:null},invoiceTable:{data:[],columns:[{name:"pending",label:""},{name:"paid_at",field:"paid_at",align:"left",label:"Paid at",sortable:!0},{name:"expiry",label:this.$t("expiry"),field:"expiry",align:"left",sortable:!0},{name:"amount",label:this.$t("amount")+" ("+LNBITS_DENOMINATION+")",field:t=>this.formatMsat(t.amount),sortable:!0},{name:"memo",align:"left",label:this.$t("memo"),field:"memo"}],pagination:{rowsPerPage:10,page:1,rowsNumber:10},filter:null}}},created(){this.getInfo(),this.get1MLStats()},watch:{tab(t){"transactions"!==t||this.paymentsTable.data.length?"channels"!==t||this.channels.data.length||(this.getChannels(),this.getPeers()):(this.getPayments(),this.getInvoices())}},computed:{checkChanges(){return!_.isEqual(this.settings,this.formData)},filteredChannels(){return this.stateFilters?this.channels.data.filter((t=>this.stateFilters.find((({value:e})=>e==t.state)))):this.channels.data},totalBalance(){return this.filteredChannels.reduce(((t,e)=>(t.local_msat+=e.balance.local_msat,t.remote_msat+=e.balance.remote_msat,t.total_msat+=e.balance.total_msat,t)),{local_msat:0,remote_msat:0,total_msat:0})}},methods:{formatMsat:t=>LNbits.utils.formatMsat(t),api(t,e,a){const s=new URLSearchParams(a?.query);return LNbits.api.request(t,`/node/api/v1${e}?${s}`,{},a?.data).catch((t=>{LNbits.utils.notifyApiError(t)}))},getChannel(t){return this.api("GET",`/channels/${t}`).then((t=>{this.setFeeDialog.data.fee_ppm=t.data.fee_ppm,this.setFeeDialog.data.fee_base_msat=t.data.fee_base_msat}))},getChannels(){return this.api("GET","/channels").then((t=>{this.channels.data=t.data}))},getInfo(){return this.api("GET","/info").then((t=>{this.info=t.data,this.channel_stats=t.data.channel_stats})).catch((()=>{this.info={},this.channel_stats={}}))},get1MLStats(){return this.api("GET","/rank").then((t=>{this.ranks=t.data})).catch((()=>{this.ranks={}}))},getPayments(t){t&&(this.paymentsTable.pagination=t.pagination);let e=this.paymentsTable.pagination;const a={limit:e.rowsPerPage,offset:(e.page-1)*e.rowsPerPage??0};return this.api("GET","/payments",{query:a}).then((t=>{this.paymentsTable.data=t.data.data,this.paymentsTable.pagination.rowsNumber=t.data.total}))},getInvoices(t){t&&(this.invoiceTable.pagination=t.pagination);let e=this.invoiceTable.pagination;const a={limit:e.rowsPerPage,offset:(e.page-1)*e.rowsPerPage??0};return this.api("GET","/invoices",{query:a}).then((t=>{this.invoiceTable.data=t.data.data,this.invoiceTable.pagination.rowsNumber=t.data.total}))},getPeers(){return this.api("GET","/peers").then((t=>{this.peers.data=t.data}))},connectPeer(){this.api("POST","/peers",{data:this.connectPeerDialog.data}).then((()=>{this.connectPeerDialog.show=!1,this.getPeers()}))},disconnectPeer(t){LNbits.utils.confirmDialog("Do you really wanna disconnect this peer?").onOk((()=>{this.api("DELETE",`/peers/${t}`).then((t=>{Quasar.Notify.create({message:"Disconnected",icon:null}),this.needsRestart=!0,this.getPeers()}))}))},setChannelFee(t){this.api("PUT",`/channels/${t}`,{data:this.setFeeDialog.data}).then((t=>{this.setFeeDialog.show=!1,this.getChannels()})).catch(LNbits.utils.notifyApiError)},openChannel(){this.api("POST","/channels",{data:this.openChannelDialog.data}).then((t=>{this.openChannelDialog.show=!1,this.getChannels()})).catch((t=>{console.log(t)}))},showCloseChannelDialog(t){this.closeChannelDialog.show=!0,this.closeChannelDialog.data={force:!1,short_id:t.short_id,...t.point}},closeChannel(){this.api("DELETE","/channels",{query:this.closeChannelDialog.data}).then((t=>{this.closeChannelDialog.show=!1,this.getChannels()}))},showSetFeeDialog(t){this.setFeeDialog.show=!0,this.setFeeDialog.channel_id=t,this.getChannel(t)},showOpenChannelDialog(t){this.openChannelDialog.show=!0,this.openChannelDialog.data={peer_id:t,funding_amount:0}},showNodeInfoDialog(t){this.nodeInfoDialog.show=!0,this.nodeInfoDialog.data=t},showTransactionDetailsDialog(t){this.transactionDetailsDialog.show=!0,this.transactionDetailsDialog.data=t},shortenNodeId:t=>t?t.substring(0,5)+"..."+t.substring(t.length-5):"..."}},window.PageNodePublic={template:"#page-node-public",mixins:[window.windowMixin],data:()=>({enabled:!1,isSuperUser:!1,wallet:{},tab:"dashboard",payments:1e3,info:{},channel_stats:{},channels:[],activeBalance:{},ranks:{},peers:[],connectPeerDialog:{show:!1,data:{}},openChannelDialog:{show:!1,data:{}},closeChannelDialog:{show:!1,data:{}},nodeInfoDialog:{show:!1,data:{}},states:[{label:"Active",value:"active",color:"green"},{label:"Pending",value:"pending",color:"orange"},{label:"Inactive",value:"inactive",color:"grey"},{label:"Closed",value:"closed",color:"red"}]}),created(){this.getInfo(),this.get1MLStats()},methods:{formatMsat:t=>LNbits.utils.formatMsat(t),api:(t,e,a)=>LNbits.api.request(t,"/node/public/api/v1"+e,{},a),getInfo(){this.api("GET","/info",{}).then((t=>{this.info=t.data,this.channel_stats=t.data.channel_stats,this.enabled=!0})).catch((()=>{this.info={},this.channel_stats={}}))},get1MLStats(){this.api("GET","/rank",{}).then((t=>{this.ranks=t.data})).catch((()=>{this.ranks={}}))}}},window.PageAudit={template:"#page-audit",mixins:[window.windowMixin],data:()=>({chartsReady:!1,auditEntries:[],searchData:{user_id:"",ip_address:"",request_type:"",component:"",request_method:"",response_code:"",path:""},searchOptions:{component:[],request_method:[],response_code:[]},auditTable:{columns:[{name:"created_at",align:"center",label:"Date",field:"created_at",sortable:!0},{name:"duration",align:"left",label:"Duration (sec)",field:"duration",sortable:!0},{name:"component",align:"left",label:"Component",field:"component",sortable:!1},{name:"request_method",align:"left",label:"Method",field:"request_method",sortable:!1},{name:"response_code",align:"left",label:"Code",field:"response_code",sortable:!1},{name:"user_id",align:"left",label:"User Id",field:"user_id",sortable:!1},{name:"ip_address",align:"left",label:"IP Address",field:"ip_address",sortable:!1},{name:"path",align:"left",label:"Path",field:"path",sortable:!1}],pagination:{sortBy:"created_at",rowsPerPage:10,page:1,descending:!0,rowsNumber:10},search:null,hideEmpty:!0,loading:!1},auditDetailsDialog:{data:null,show:!1}}),async created(){},async mounted(){this.chartsReady=!0,await this.$nextTick(),this.initCharts(),await this.fetchAudit()},methods:{formatDate:t=>LNbits.utils.formatDateString(t),async fetchAudit(t){try{const e=LNbits.utils.prepareFilterQuery(this.auditTable,t),{data:a}=await LNbits.api.request("GET",`/audit/api/v1?${e}`);this.auditTable.pagination.rowsNumber=a.total,this.auditEntries=a.data,await this.fetchAuditStats(t)}catch(t){console.warn(t),LNbits.utils.notifyApiError(t)}finally{this.auditTable.loading=!1}},async fetchAuditStats(t){try{const e=LNbits.utils.prepareFilterQuery(this.auditTable,t),{data:a}=await LNbits.api.request("GET",`/audit/api/v1/stats?${e}`),s=a.request_method.map((t=>t.field));this.searchOptions.request_method=[...new Set(this.searchOptions.request_method.concat(s))],this.requestMethodChart.data.labels=s,this.requestMethodChart.data.datasets[0].data=a.request_method.map((t=>t.total)),this.requestMethodChart.update();const i=a.response_code.map((t=>t.field));this.searchOptions.response_code=[...new Set(this.searchOptions.response_code.concat(i))],this.responseCodeChart.data.labels=i,this.responseCodeChart.data.datasets[0].data=a.response_code.map((t=>t.total)),this.responseCodeChart.update();const n=a.component.map((t=>t.field));this.searchOptions.component=[...new Set(this.searchOptions.component.concat(n))],this.componentUseChart.data.labels=n,this.componentUseChart.data.datasets[0].data=a.component.map((t=>t.total)),this.componentUseChart.update(),this.longDurationChart.data.labels=a.long_duration.map((t=>t.field)),this.longDurationChart.data.datasets[0].data=a.long_duration.map((t=>t.total)),this.longDurationChart.update()}catch(t){console.warn(t),LNbits.utils.notifyApiError(t)}},async searchAuditBy(t,e){t&&(this.searchData[t]=e),this.auditTable.filter=Object.entries(this.searchData).reduce(((t,[e,a])=>a?(t[e]=a,t):t),{}),await this.fetchAudit()},showDetailsDialog(t){const e=JSON.parse(t?.request_details||"");try{e.body&&(e.body=JSON.parse(e.body))}catch(t){}this.auditDetailsDialog.data=JSON.stringify(e,null,4),this.auditDetailsDialog.show=!0},shortify:t=>(valueLength=(t||"").length,valueLength<=10?t:`${t.substring(0,5)}...${t.substring(valueLength-5,valueLength)}`),async initCharts(){this.chartsReady?(this.responseCodeChart=new Chart(this.$refs.responseCodeChart.getContext("2d"),{type:"doughnut",options:{responsive:!0,plugins:{legend:{position:"bottom"},title:{display:!1,text:"HTTP Response Codes"}},onClick:(t,e,a)=>{if(e[0]){const t=e[0].index;this.searchAuditBy("response_code",a.data.labels[t])}}},data:{datasets:[{label:"",data:[20,10],backgroundColor:["rgb(100, 99, 200)","rgb(54, 162, 235)","rgb(255, 205, 86)","rgb(255, 5, 86)","rgb(25, 205, 86)","rgb(255, 205, 250)"]}],labels:[]}}),this.requestMethodChart=new Chart(this.$refs.requestMethodChart.getContext("2d"),{type:"bar",options:{responsive:!0,maintainAspectRatio:!1,plugins:{title:{display:!1}},onClick:(t,e,a)=>{if(e[0]){const t=e[0].index;this.searchAuditBy("request_method",a.data.labels[t])}}},data:{datasets:[{label:"",data:[],backgroundColor:["rgb(255, 99, 132)","rgb(54, 162, 235)","rgb(255, 205, 86)","rgb(255, 5, 86)","rgb(25, 205, 86)","rgb(255, 205, 250)"],hoverOffset:4}]}}),this.componentUseChart=new Chart(this.$refs.componentUseChart.getContext("2d"),{type:"pie",options:{responsive:!0,plugins:{legend:{position:"xxx"},title:{display:!1,text:"Components"}},onClick:(t,e,a)=>{if(e[0]){const t=e[0].index;this.searchAuditBy("component",a.data.labels[t])}}},data:{datasets:[{data:[],backgroundColor:["rgb(255, 99, 132)","rgb(54, 162, 235)","rgb(255, 205, 86)","rgb(255, 5, 86)","rgb(25, 205, 86)","rgb(255, 205, 250)","rgb(100, 205, 250)","rgb(120, 205, 250)","rgb(140, 205, 250)","rgb(160, 205, 250)"],hoverOffset:4}]}}),this.longDurationChart=new Chart(this.$refs.longDurationChart.getContext("2d"),{type:"bar",options:{responsive:!0,indexAxis:"y",maintainAspectRatio:!1,plugins:{legend:{title:{display:!1,text:"Long Duration"}}},onClick:(t,e,a)=>{if(e[0]){const t=e[0].index;this.searchAuditBy("path",a.data.labels[t])}}},data:{datasets:[{label:"",data:[],backgroundColor:["rgb(255, 99, 132)","rgb(54, 162, 235)","rgb(255, 205, 86)","rgb(255, 5, 86)","rgb(25, 205, 86)","rgb(255, 205, 250)","rgb(100, 205, 250)","rgb(120, 205, 250)","rgb(140, 205, 250)","rgb(160, 205, 250)"],hoverOffset:4}]}})):console.warn("Charts are not ready yet. Initialization delayed.")}}},window.app.component("lnbits-qrcode",{mixins:[window.windowMixin],template:"#lnbits-qrcode",components:{QrcodeVue:QrcodeVue},props:{value:{type:String,required:!0},nfc:{type:Boolean,default:!1},showButtons:{type:Boolean,default:!0},href:{type:String,default:""},margin:{type:Number,default:3},maxWidth:{type:Number,default:450},logo:{type:String,default:LNBITS_QR_LOGO}},data:()=>({nfcTagWriting:!1,nfcSupported:"undefined"!=typeof NDEFReader}),methods:{clickQrCode(t){if(""===this.href)return this.copyText(this.value),t.preventDefault(),t.stopPropagation(),!1},async writeNfcTag(){try{if(!this.nfcSupported)throw{toString:function(){return"NFC not supported on this device or browser."}};const t=new NDEFReader;this.nfcTagWriting=!0,this.$q.notify({message:"Tap your NFC tag to write the LNURL-withdraw link to it."}),await t.write({records:[{recordType:"url",data:this.value,lang:"en"}]}),this.nfcTagWriting=!1,this.$q.notify({type:"positive",message:"NFC tag written successfully."})}catch(t){this.nfcTagWriting=!1,this.$q.notify({type:"negative",message:t?t.toString():"An unexpected error has occurred."})}},downloadSVG(){const t=this.$refs.qrCode.$el;if(!t)return void console.error("SVG element not found");let e=(new XMLSerializer).serializeToString(t);e.match(/^]+xmlns="http:\/\/www\.w3\.org\/2000\/svg"/)||(e=e.replace(/^({tab:"bech32",lnurl:""}),methods:{setLnurl(){if("bech32"==this.tab){const t=(new TextEncoder).encode(this.url),e=NostrTools.nip19.encodeBytes("lnurl",t);this.lnurl=`lightning:${e.toUpperCase()}`}else"lud17"==this.tab&&(this.url.startsWith("http://")?this.lnurl=this.url.replace("http://",this.prefix+"://"):this.lnurl=this.url.replace("https://",this.prefix+"://"));this.$emit("update:lnurl",this.lnurl)}},watch:{url(){this.setLnurl()},tab(){this.setLnurl()}},created(){this.setLnurl()}}),window.app.component("lnbits-funding-sources",{template:"#lnbits-funding-sources",mixins:[window.windowMixin],props:["form-data","allowed-funding-sources"],methods:{getFundingSourceLabel(t){const e=this.rawFundingSources.find((e=>e[0]===t));return e?e[1]:t},showQRValue(t){this.qrValue=t,this.showQRDialog=!0}},computed:{fundingSources(){let t=[];for(const[e,a,s]of this.rawFundingSources){const a={};if(null!==s)for(let[t,e]of Object.entries(s))a[t]="string"==typeof e?{label:e,value:null}:e||{};t.push([e,a])}return new Map(t)},sortedAllowedFundingSources(){return this.allowedFundingSources.sort()}},data:()=>({hideInput:!0,showQRDialog:!1,qrValue:"",rawFundingSources:[["VoidWallet","Void Wallet",null],["FakeWallet","Fake Wallet",{fake_wallet_secret:"Secret",lnbits_denomination:'"sats" or 3 Letter Custom Denomination'}],["CLNRestWallet","Core Lightning Rest (plugin)",{clnrest_url:"Endpoint",clnrest_ca:"ca.pem",clnrest_cert:"server.pem",clnrest_readonly_rune:"Rune used for readonly requests",clnrest_invoice_rune:"Rune used for creating invoices",clnrest_pay_rune:"Rune used for paying invoices using pay",clnrest_renepay_rune:"Rune used for paying invoices using renepay",clnrest_last_pay_index:"Ignores any invoices paid prior to or including this index. 0 is equivalent to not specifying and negative value is invalid.",clnrest_nodeid:"Node id"}],["CoreLightningWallet","Core Lightning",{corelightning_rpc:"Endpoint",corelightning_pay_command:"Custom Pay Command"}],["CoreLightningRestWallet","Core Lightning Rest (legacy)",{corelightning_rest_url:"Endpoint",corelightning_rest_cert:"Certificate",corelightning_rest_macaroon:"Macaroon"}],["LndRestWallet","Lightning Network Daemon (LND Rest)",{lnd_rest_endpoint:"Endpoint",lnd_rest_cert:"Certificate",lnd_rest_macaroon:"Macaroon",lnd_rest_macaroon_encrypted:"Encrypted Macaroon",lnd_rest_route_hints:"Enable Route Hints",lnd_rest_allow_self_payment:"Allow Self Payment"}],["LndWallet","Lightning Network Daemon (LND)",{lnd_grpc_endpoint:"Endpoint",lnd_grpc_cert:"Certificate",lnd_grpc_port:"Port",lnd_grpc_macaroon:"GRPC Macaroon",lnd_grpc_invoice_macaroon:"GRPC Invoice Macaroon",lnd_grpc_admin_macaroon:"GRPC Admin Macaroon",lnd_grpc_macaroon_encrypted:"Encrypted Macaroon"}],["LnTipsWallet","LN.Tips",{lntips_api_endpoint:"Endpoint",lntips_api_key:"API Key"}],["LNPayWallet","LN Pay",{lnpay_api_endpoint:"Endpoint",lnpay_api_key:"API Key",lnpay_wallet_key:"Wallet Key"}],["EclairWallet","Eclair (ACINQ)",{eclair_url:"URL",eclair_pass:"Password"}],["LNbitsWallet","LNbits",{lnbits_endpoint:"Endpoint",lnbits_key:"Admin Key"}],["BlinkWallet","Blink",{blink_api_endpoint:"Endpoint",blink_ws_endpoint:"WebSocket",blink_token:"Key"}],["AlbyWallet","Alby",{alby_api_endpoint:"Endpoint",alby_access_token:"Key"}],["BoltzWallet","Boltz",{boltz_client_endpoint:"Endpoint",boltz_client_macaroon:"Admin Macaroon path or hex",boltz_client_cert:"Certificate path or hex",boltz_client_wallet:"Wallet Name",boltz_client_password:"Wallet Password (can be empty)",boltz_mnemonic:{label:"Liquid mnemonic (copy into greenwallet)",readonly:!0,copy:!0,qrcode:!0}}],["ZBDWallet","ZBD",{zbd_api_endpoint:"Endpoint",zbd_api_key:"Key"}],["PhoenixdWallet","Phoenixd",{phoenixd_api_endpoint:"Endpoint",phoenixd_api_password:"Key"}],["OpenNodeWallet","OpenNode",{opennode_api_endpoint:"Endpoint",opennode_key:"Key"}],["ClicheWallet","Cliche (NBD)",{cliche_endpoint:"Endpoint"}],["SparkWallet","Spark",{spark_url:"Endpoint",spark_token:"Token"}],["NWCWallet","Nostr Wallet Connect",{nwc_pairing_url:"Pairing URL"}],["BreezSdkWallet","Breez SDK",{breez_api_key:"Breez API Key",breez_greenlight_seed:"Greenlight Seed",breez_greenlight_device_key:"Greenlight Device Key",breez_greenlight_device_cert:"Greenlight Device Cert",breez_greenlight_invite_code:"Greenlight Invite Code"}],["StrikeWallet","Strike (alpha)",{strike_api_endpoint:"API Endpoint",strike_api_key:"API Key"}],["BreezLiquidSdkWallet","Breez Liquid SDK",{breez_liquid_api_key:"Breez API Key (can be empty)",breez_liquid_seed:"Liquid seed phrase",breez_liquid_fee_offset_sat:"Offset amount in sats to increase fee limit"}]]})}),window.app.component("lnbits-extension-settings-form",{name:"lnbits-extension-settings-form",template:"#lnbits-extension-settings-form",props:["options","adminkey","endpoint"],methods:{async updateSettings(){if(!this.settings)return Quasar.Notify.create({message:"No settings to update",type:"negative"});try{const{data:t}=await LNbits.api.request("PUT",this.endpoint,this.adminkey,this.settings);this.settings=t}catch(t){LNbits.utils.notifyApiError(t)}},async getSettings(){try{const{data:t}=await LNbits.api.request("GET",this.endpoint,this.adminkey);this.settings=t}catch(t){LNbits.utils.notifyApiError(t)}},async resetSettings(){LNbits.utils.confirmDialog("Are you sure you want to reset the settings?").onOk((async()=>{try{await LNbits.api.request("DELETE",this.endpoint,this.adminkey),await this.getSettings()}catch(t){LNbits.utils.notifyApiError(t)}}))}},async created(){await this.getSettings()},data:()=>({settings:void 0})}),window.app.component("lnbits-extension-settings-btn-dialog",{template:"#lnbits-extension-settings-btn-dialog",name:"lnbits-extension-settings-btn-dialog",props:["options","adminkey","endpoint"],data:()=>({show:!1})}),window.app.component("lnbits-data-fields",{name:"lnbits-data-fields",template:"#lnbits-data-fields",props:["fields","hide-advanced"],data:()=>({fieldTypes:[{label:"Text",value:"str"},{label:"Integer",value:"int"},{label:"Float",value:"float"},{label:"Boolean",value:"bool"},{label:"Date Time",value:"datetime"},{label:"JSON",value:"json"},{label:"Wallet Select",value:"wallet"},{label:"Currency Select",value:"currency"}],fieldsTable:{columns:[{name:"name",align:"left",label:"Field Name",field:"name",sortable:!0},{name:"type",align:"left",label:"Type",field:"type",sortable:!1},{name:"label",align:"left",label:"UI Label",field:"label",sortable:!0},{name:"hint",align:"left",label:"UI Hint",field:"hint",sortable:!1},{name:"optional",align:"left",label:"Optional",field:"optional",sortable:!1}],pagination:{sortBy:"name",rowsPerPage:100,page:1,rowsNumber:100},search:null,hideEmpty:!0}}),methods:{addField:function(){this.fields.push({name:"field_name_"+(this.fields.length+1),type:"text",label:"",hint:"",optional:!0,sortable:!0,searchable:!0,editable:!0,fields:[]})},removeField:function(t){const e=this.fields.indexOf(t);e>-1&&this.fields.splice(e,1)}},async created(){this.hideAdvanced||this.fieldsTable.columns.push({name:"editable",align:"left",label:"UI Editable",field:"editable",sortable:!1},{name:"sortable",align:"left",label:"Sortable",field:"sortable",sortable:!1},{name:"searchable",align:"left",label:"Searchable",field:"searchable",sortable:!1})}}),window.app.component("payment-list",{name:"payment-list",template:"#payment-list",props:["update","lazy","wallet"],mixins:[window.windowMixin],data(){return{denomination:LNBITS_DENOMINATION,payments:[],paymentsTable:{columns:[{name:"time",align:"left",label:this.$t("memo")+"/"+this.$t("date"),field:"date",sortable:!0},{name:"amount",align:"right",label:this.$t("amount")+" ("+LNBITS_DENOMINATION+")",field:"sat",sortable:!0}],pagination:{rowsPerPage:10,page:1,sortBy:"time",descending:!0,rowsNumber:10},search:"",filter:{"status[ne]":"failed"},loading:!1},searchDate:{from:null,to:null},searchStatus:{success:!0,pending:!0,failed:!1,incoming:!0,outgoing:!0},exportTagName:"",exportPaymentTagList:[],paymentsCSV:{columns:[{name:"pending",align:"left",label:"Pending",field:"pending"},{name:"memo",align:"left",label:this.$t("memo"),field:"memo"},{name:"time",align:"left",label:this.$t("date"),field:"date",sortable:!0},{name:"amount",align:"right",label:this.$t("amount")+" ("+LNBITS_DENOMINATION+")",field:"sat",sortable:!0},{name:"fee",align:"right",label:this.$t("fee")+" (m"+LNBITS_DENOMINATION+")",field:"fee"},{name:"tag",align:"right",label:this.$t("tag"),field:"tag"},{name:"payment_hash",align:"right",label:this.$t("payment_hash"),field:"payment_hash"},{name:"payment_proof",align:"right",label:this.$t("payment_proof"),field:"payment_proof"},{name:"webhook",align:"right",label:this.$t("webhook"),field:"webhook"},{name:"fiat_currency",align:"right",label:"Fiat Currency",field:t=>t.extra.wallet_fiat_currency},{name:"fiat_amount",align:"right",label:"Fiat Amount",field:t=>t.extra.wallet_fiat_amount}],preimage:null,loading:!1},hodlInvoice:{show:!1,payment:null,preimage:null}}},computed:{currentWallet(){return this.wallet||this.g.wallet},filteredPayments(){const t=this.paymentsTable.search;return t&&""!==t?LNbits.utils.search(this.payments,t):this.payments},paymentsOmitter(){return this.$q.screen.lt.md&&this.mobileSimple?this.payments.length>0?[this.payments[0]]:[]:this.payments},pendingPaymentsExist(){return-1!==this.payments.findIndex((t=>t.pending))}},methods:{searchByDate(){"string"==typeof this.searchDate&&(this.searchDate={from:this.searchDate,to:this.searchDate}),this.searchDate.from&&(this.paymentsTable.filter["time[ge]"]=this.searchDate.from+"T00:00:00"),this.searchDate.to&&(this.paymentsTable.filter["time[le]"]=this.searchDate.to+"T23:59:59"),this.fetchPayments()},clearDateSeach(){this.searchDate={from:null,to:null},delete this.paymentsTable.filter["time[ge]"],delete this.paymentsTable.filter["time[le]"],this.fetchPayments()},fetchPayments(t){this.$emit("filter-changed",{...this.paymentsTable.filter});const e=LNbits.utils.prepareFilterQuery(this.paymentsTable,t);return LNbits.api.getPayments(this.currentWallet,e).then((t=>{this.paymentsTable.loading=!1,this.paymentsTable.pagination.rowsNumber=t.data.total,this.payments=t.data.data.map((t=>LNbits.map.payment(t)))})).catch((t=>{this.paymentsTable.loading=!1,g.user.admin?this.fetchPaymentsAsAdmin(this.currentWallet.id,e):LNbits.utils.notifyApiError(t)}))},fetchPaymentsAsAdmin(t,e){return e=(e||"")+"&wallet_id="+t,LNbits.api.request("GET","/api/v1/payments/all/paginated?"+e).then((t=>{this.paymentsTable.loading=!1,this.paymentsTable.pagination.rowsNumber=t.data.total,this.payments=t.data.data.map((t=>LNbits.map.payment(t)))})).catch((t=>{this.paymentsTable.loading=!1,LNbits.utils.notifyApiError(t)}))},checkPayment(t){LNbits.api.getPayment(this.g.wallet,t).then((t=>{this.update=!this.update,"success"==t.data.status&&Quasar.Notify.create({type:"positive",message:this.$t("payment_successful")}),"pending"==t.data.status&&Quasar.Notify.create({type:"info",message:this.$t("payment_pending")})})).catch(LNbits.utils.notifyApiError)},showHoldInvoiceDialog(t){this.hodlInvoice.show=!0,this.hodlInvoice.preimage="",this.hodlInvoice.payment=t},cancelHoldInvoice(t){LNbits.api.cancelInvoice(this.g.wallet,t).then((()=>{this.update=!this.update,Quasar.Notify.create({type:"positive",message:this.$t("invoice_cancelled")})})).catch(LNbits.utils.notifyApiError)},settleHoldInvoice(t){LNbits.api.settleInvoice(this.g.wallet,t).then((()=>{this.update=!this.update,Quasar.Notify.create({type:"positive",message:this.$t("invoice_settled")})})).catch(LNbits.utils.notifyApiError)},paymentTableRowKey:t=>t.payment_hash+t.amount,exportCSV(t=!1){const e=this.paymentsTable.pagination,a={sortby:e.sortBy??"time",direction:e.descending?"desc":"asc"},s=new URLSearchParams(a);LNbits.api.getPayments(this.g.wallet,s).then((e=>{let a=e.data.data.map(LNbits.map.payment),s=this.paymentsCSV.columns;if(t){this.exportPaymentTagList.length&&(a=a.filter((t=>this.exportPaymentTagList.includes(t.tag))));const t=Object.keys(a.reduce(((t,e)=>({...t,...e.details})),{})).map((t=>({name:t,align:"right",label:t.charAt(0).toUpperCase()+t.slice(1).replace(/([A-Z])/g," $1"),field:e=>e.details[t],format:t=>"object"==typeof t?JSON.stringify(t):t})));s=this.paymentsCSV.columns.concat(t)}LNbits.utils.exportCSV(s,a,this.g.wallet.name+"-payments")}))},addFilterTag(){if(!this.exportTagName)return;const t=this.exportTagName.trim();this.exportPaymentTagList=this.exportPaymentTagList.filter((e=>e!==t)),this.exportPaymentTagList.push(t),this.exportTagName=""},removeExportTag(t){this.exportPaymentTagList=this.exportPaymentTagList.filter((e=>e!==t))},formatCurrency(t,e){try{return LNbits.utils.formatCurrency(t,e)}catch(e){return console.error(e),`${t} ???`}},handleFilterChanged(){const{success:t,pending:e,failed:a,incoming:s,outgoing:i}=this.searchStatus;delete this.paymentsTable.filter["status[ne]"],delete this.paymentsTable.filter["status[eq]"],t&&e&&a||(t&&e?this.paymentsTable.filter["status[ne]"]="failed":t&&a?this.paymentsTable.filter["status[ne]"]="pending":a&&e?this.paymentsTable.filter["status[ne]"]="success":t?this.paymentsTable.filter["status[eq]"]="success":e?this.paymentsTable.filter["status[eq]"]="pending":a&&(this.paymentsTable.filter["status[eq]"]="failed")),delete this.paymentsTable.filter["amount[ge]"],delete this.paymentsTable.filter["amount[le]"],s&&i||(s?this.paymentsTable.filter["amount[ge]"]=0:i&&(this.paymentsTable.filter["amount[le]"]=0))}},watch:{"paymentsTable.search":{handler(){const t={};this.paymentsTable.search&&(t.search=this.paymentsTable.search),this.fetchPayments()}},lazy(t){!0===t&&this.fetchPayments()},update(){this.fetchPayments()},"g.updatePayments"(){this.fetchPayments()},"g.wallet":{handler(t){this.fetchPayments()},deep:!0}},created(){void 0===this.lazy&&this.fetchPayments()}}),window.app.component(QrcodeVue),window.app.component("lnbits-extension-rating",{template:"#lnbits-extension-rating",name:"lnbits-extension-rating",props:["rating"]}),window.app.component("lnbits-fsat",{template:"{{ fsat }}",props:{amount:{type:Number,default:0}},computed:{fsat(){return LNbits.utils.formatSat(this.amount)}}}),window.app.component("lnbits-wallet-list",{mixins:[window.windowMixin],template:"#lnbits-wallet-list",props:["balance"],data:()=>({activeWallet:null,balance:0,showForm:!1,walletName:"",LNBITS_DENOMINATION:LNBITS_DENOMINATION}),methods:{createWallet(){LNbits.api.createWallet(this.g.user.wallets[0],this.walletName)}},created(){document.addEventListener("updateWalletBalance",this.updateWalletBalance)}}),window.app.component("lnbits-extension-list",{mixins:[window.windowMixin],template:"#lnbits-extension-list",data:()=>({extensions:[],searchTerm:""}),watch:{"g.user.extensions":{handler(t){this.loadExtensions()},deep:!0}},computed:{userExtensions(){return this.updateUserExtensions(this.searchTerm)}},methods:{async loadExtensions(){try{const{data:t}=await LNbits.api.request("GET","/api/v1/extension");this.extensions=t.map((t=>LNbits.map.extension(t))).sort(((t,e)=>t.name.localeCompare(e.name)))}catch(t){LNbits.utils.notifyApiError(t)}},updateUserExtensions(t){const e=window.location.pathname,a=this.g.user.extensions;return this.extensions.filter((t=>a.includes(t.code))).filter((e=>!t||`${e.code} ${e.name} ${e.short_description} ${e.url}`.toLocaleLowerCase().includes(t.toLocaleLowerCase()))).map((t=>(t.isActive=e.startsWith(t.url),t)))}},async created(){await this.loadExtensions()}}),window.app.component("lnbits-manage",{mixins:[window.windowMixin],template:"#lnbits-manage",props:["showAdmin","showNode","showExtensions","showUsers","showAudit"],methods:{isActive:t=>window.location.pathname===t},data:()=>({extensions:[]})}),window.app.component("lnbits-payment-details",{mixins:[window.windowMixin],template:"#lnbits-payment-details",props:["payment"],mixins:[window.windowMixin],data:()=>({LNBITS_DENOMINATION:LNBITS_DENOMINATION}),computed:{hasPreimage(){return this.payment.preimage&&"0000000000000000000000000000000000000000000000000000000000000000"!==this.payment.preimage},hasExpiry(){return!!this.payment.expiry},hasSuccessAction(){return this.hasPreimage&&this.payment.extra&&this.payment.extra.success_action},webhookStatusColor(){return this.payment.webhook_status>=300||this.payment.webhook_status<0?"red-10":this.payment.webhook_status?"green-10":"cyan-7"},webhookStatusText(){return this.payment.webhook_status?this.payment.webhook_status:"not sent yet"},hasTag(){return this.payment.extra&&!!this.payment.extra.tag},extras(){if(!this.payment.extra)return[];let t=_.omit(this.payment.extra,["tag","success_action"]);return Object.keys(t).map((e=>({key:e,value:t[e]})))}}}),window.app.component("lnbits-lnurlpay-success-action",{mixins:[window.windowMixin],template:"#lnbits-lnurlpay-success-action",props:["payment","success_action"],data(){return{decryptedValue:this.success_action.ciphertext}},mounted(){if("aes"!==this.success_action.tag)return null;decryptLnurlPayAES(this.success_action,this.payment.preimage).then((t=>{this.decryptedValue=t}))}}),window.app.component("lnbits-notifications-btn",{template:"#lnbits-notifications-btn",mixins:[window.windowMixin],props:["pubkey"],data:()=>({isSupported:!1,isSubscribed:!1,isPermissionGranted:!1,isPermissionDenied:!1}),methods:{urlB64ToUint8Array(t){const e=(t+"=".repeat((4-t.length%4)%4)).replace(/\-/g,"+").replace(/_/g,"/"),a=atob(e),s=new Uint8Array(a.length);for(let t=0;te!==t)),this.$q.localStorage.set("lnbits.webpush.subscribedUsers",JSON.stringify(e))},isUserSubscribed(t){return(JSON.parse(this.$q.localStorage.getItem("lnbits.webpush.subscribedUsers"))||[]).includes(t)},subscribe(){this.isSupported&&!this.isPermissionDenied&&(Notification.requestPermission().then((t=>{this.isPermissionGranted="granted"===t,this.isPermissionDenied="denied"===t})).catch(console.log),navigator.serviceWorker.ready.then((t=>{navigator.serviceWorker.getRegistration().then((t=>{t.pushManager.getSubscription().then((e=>{if(null===e||!this.isUserSubscribed(this.g.user.id)){const e={applicationServerKey:this.urlB64ToUint8Array(this.pubkey),userVisibleOnly:!0};t.pushManager.subscribe(e).then((t=>{LNbits.api.request("POST","/api/v1/webpush",null,{subscription:JSON.stringify(t)}).then((t=>{this.saveUserSubscribed(t.data.user),this.isSubscribed=!0})).catch(LNbits.utils.notifyApiError)}))}})).catch(console.log)}))})))},unsubscribe(){navigator.serviceWorker.ready.then((t=>{t.pushManager.getSubscription().then((t=>{t&&LNbits.api.request("DELETE","/api/v1/webpush?endpoint="+btoa(t.endpoint),null).then((()=>{this.removeUserSubscribed(this.g.user.id),this.isSubscribed=!1})).catch(LNbits.utils.notifyApiError)}))})).catch(console.log)},checkSupported(){let t="https:"===window.location.protocol,e="serviceWorker"in navigator,a="Notification"in window,s="PushManager"in window;return this.isSupported=t&&e&&a&&s,this.isSupported||console.log("Notifications disabled because requirements are not met:",{HTTPS:t,"Service Worker API":e,"Notification API":a,"Push API":s}),this.isSupported},async updateSubscriptionStatus(){await navigator.serviceWorker.ready.then((t=>{t.pushManager.getSubscription().then((t=>{this.isSubscribed=!!t&&this.isUserSubscribed(this.g.user.id)}))})).catch(console.log)}},created(){this.isPermissionDenied="denied"===Notification.permission,this.checkSupported()&&this.updateSubscriptionStatus()}}),window.app.component("lnbits-dynamic-fields",{template:"#lnbits-dynamic-fields",mixins:[window.windowMixin],props:["options","modelValue"],data:()=>({formData:null,rules:[t=>!!t||"Field is required"]}),methods:{applyRules(t){return t?this.rules:[]},buildData(t,e={}){return t.reduce(((t,a)=>(a.options?.length?t[a.name]=this.buildData(a.options,e[a.name]):t[a.name]=e[a.name]??a.default,t)),{})},handleValueChanged(){this.$emit("update:model-value",this.formData)}},created(){this.formData=this.buildData(this.options,this.modelValue)}}),window.app.component("lnbits-dynamic-chips",{template:"#lnbits-dynamic-chips",mixins:[window.windowMixin],props:["modelValue"],data:()=>({chip:"",chips:[]}),methods:{addChip(){this.chip&&(this.chips.push(this.chip),this.chip="",this.$emit("update:model-value",this.chips.join(",")))},removeChip(t){this.chips.splice(t,1),this.$emit("update:model-value",this.chips.join(","))}},created(){"string"==typeof this.modelValue?this.chips=this.modelValue.split(","):this.chips=[...this.modelValue]}}),window.app.component("lnbits-update-balance",{template:"#lnbits-update-balance",mixins:[window.windowMixin],props:["wallet_id","small_btn"],computed:{denomination:()=>LNBITS_DENOMINATION,admin:()=>user.super_user},data:()=>({credit:0}),methods:{updateBalance(t){LNbits.api.updateBalance(t.value,this.wallet_id).then((e=>{if(!0!==e.data.success)throw new Error(e.data);credit=parseInt(t.value),Quasar.Notify.create({type:"positive",message:this.$t("credit_ok",{amount:credit}),icon:null}),this.credit=0,t.value=0,t.set()})).catch(LNbits.utils.notifyApiError)}}}),window.app.component("user-id-only",{template:"#user-id-only",mixins:[window.windowMixin],props:{allowed_new_users:Boolean,authAction:String,authMethod:String,usr:String,wallet:String},data(){return{user:this.usr,walletName:this.wallet}},methods:{showLogin(t){this.$emit("show-login",t)},showRegister(t){this.$emit("show-register",t)},loginUsr(){this.$emit("update:usr",this.user),this.$emit("login-usr")},createWallet(){this.$emit("update:wallet",this.walletName),this.$emit("create-wallet")}},computed:{showInstantLogin(){return"username-password"!==this.authMethod||"register"!==this.authAction}},created(){}}),window.app.component("username-password",{template:"#username-password",mixins:[window.windowMixin],props:{allowed_new_users:Boolean,authMethods:Array,authAction:String,username:String,password_1:String,password_2:String,resetKey:String},data(){return{oauth:["nostr-auth-nip98","google-auth","github-auth","keycloak-auth"],username:this.userName,password:this.password_1,passwordRepeat:this.password_2,reset_key:this.resetKey,keycloakOrg:LNBITS_AUTH_KEYCLOAK_ORG||"Keycloak",keycloakIcon:LNBITS_AUTH_KEYCLOAK_ICON}},methods:{login(){this.$emit("update:userName",this.username),this.$emit("update:password_1",this.password),this.$emit("login")},register(){this.$emit("update:userName",this.username),this.$emit("update:password_1",this.password),this.$emit("update:password_2",this.passwordRepeat),this.$emit("register")},reset(){this.$emit("update:resetKey",this.reset_key),this.$emit("update:password_1",this.password),this.$emit("update:password_2",this.passwordRepeat),this.$emit("reset")},validateUsername:t=>new RegExp("^(?=[a-zA-Z0-9._]{2,20}$)(?!.*[_.]{2})[^_.].*[^_.]$").test(t),async signInWithNostr(){try{const t=await this.createNostrToken();if(!t)return;resp=await LNbits.api.loginByProvider("nostr",{Authorization:t},{}),window.location.href="/wallet"}catch(t){console.warn(t);const e=t?.response?.data?.detail||`${t}`;Quasar.Notify.create({type:"negative",message:"Failed to sign in with Nostr.",caption:e})}},async createNostrToken(){try{if(!window.nostr?.signEvent)return void Quasar.Notify.create({type:"negative",message:"No Nostr signing app detected.",caption:'Is "window.nostr" present?'});const t=`${window.location}nostr`,e="POST",a=await NostrTools.nip98.getToken(t,e,(t=>async function(t){try{const{data:e}=await LNbits.api.getServerHealth();return t.created_at=e.server_time,await window.nostr.signEvent(t)}catch(t){console.error(t),Quasar.Notify.create({type:"negative",message:"Failed to sign nostr event.",caption:`${t}`})}}(t)),!0);if(!await NostrTools.nip98.validateToken(a,t,e))throw new Error("Invalid signed token!");return a}catch(t){console.warn(t),Quasar.Notify.create({type:"negative",message:"Failed create Nostr event.",caption:`${t}`})}}},computed:{showOauth(){return this.oauth.some((t=>this.authMethods.includes(t)))}},created(){}}),window.app.component("separator-text",{template:"#separator-text",props:{text:String,uppercase:{type:Boolean,default:!1},color:{type:String,default:"grey"}}}),window.app.component("lnbits-node-ranks",{props:["ranks"],data:()=>({stats:[{label:"Capacity",key:"capacity"},{label:"Channels",key:"channelcount"},{label:"Age",key:"age"},{label:"Growth",key:"growth"},{label:"Availability",key:"availability"}]}),template:"\n \n
\n
1ml Node Rank
\n
\n
\n
{{ stat.label }}
\n
\n {{ (ranks && ranks[stat.key]) ?? '-' }}\n
\n
\n
\n
\n
\n "}),window.app.component("lnbits-channel-stats",{props:["stats"],data:()=>({states:[{label:"Active",value:"active",color:"green"},{label:"Pending",value:"pending",color:"orange"},{label:"Inactive",value:"inactive",color:"grey"},{label:"Closed",value:"closed",color:"red"}]}),template:"\n \n
\n
Channels
\n
\n
\n
\n {{ state.label }}\n
\n
\n {{ (stats?.counts && stats.counts[state.value]) ?? \"-\" }}\n
\n
\n
\n
\n
\n "}),window.app.component("lnbits-stat",{props:["title","amount","msat","btc"],computed:{value(){return this.amount??(this.btc?LNbits.utils.formatSat(this.btc):LNbits.utils.formatMsat(this.msat))}},template:"\n \n \n
\n {{ title }}\n
\n
\n {{ value }}\n sats\n BTC\n
\n
\n
\n "}),window.app.component("lnbits-node-qrcode",{props:["info"],mixins:[window.windowMixin],template:'\n \n \n
\n
\n \n
\n No addresses available\n
\n
\n
\n
\n \n Public Key Click to copy \n \n \n
\n '}),window.app.component("lnbits-channel-balance",{props:["balance","color"],methods:{formatMsat:t=>LNbits.utils.formatMsat(t)},template:'\n
\n
\n \n Local: {{ formatMsat(balance.local_msat) }}\n sats\n \n \n Remote: {{ formatMsat(balance.remote_msat) }}\n sats\n \n
\n\n \n
\n \n {{ balance.alias }}\n \n
\n \n
\n '}),window.app.component("lnbits-date",{props:["ts"],computed:{date(){return LNbits.utils.formatDate(this.ts)},dateFrom(){return moment.utc(this.date).local().fromNow()}},template:"\n
\n {{ this.date }}\n {{ this.dateFrom }}\n
\n "}),window.app.component("lnbits-node-info",{props:["info"],data:()=>({showDialog:!1}),mixins:[window.windowMixin],methods:{shortenNodeId:t=>t?t.substring(0,5)+"..."+t.substring(t.length-5):"..."},template:"\n
\n
{{ this.info.alias }}
\n
\n
{{ this.info.backend_name }}
\n \n #{{ this.info.color }}\n \n
{{ shortenNodeId(this.info.id) }}
\n \n \n
\n \n \n \n
\n "}),window.app.component("lnbits-stat",{props:["title","amount","msat","btc"],computed:{value(){return this.amount??(this.btc?LNbits.utils.formatSat(this.btc):LNbits.utils.formatMsat(this.msat))}},template:"\n \n \n
\n {{ title }}\n
\n
\n {{ value }}\n sats\n BTC\n
\n
\n
\n "});const DynamicComponent={props:{fetchUrl:{type:String,required:!0},scripts:{type:Array,default:()=>[]}},data:()=>({keys:[]}),async mounted(){await this.loadDynamicContent()},methods:{loadScript:async t=>new Promise(((e,a)=>{const s=document.querySelector(`script[src="${t}"]`);s&&s.remove();const i=document.createElement("script");i.src=t,i.async=!0,i.onload=e,i.onerror=()=>a(new Error(`Failed to load script: ${t}`)),document.head.appendChild(i)})),async loadDynamicContent(){this.$q.loading.show();try{const t=this.fetchUrl.split("#")[0],e=await fetch(t,{credentials:"include",headers:{Accept:"text/html","X-Requested-With":"XMLHttpRequest"}}),a=await e.text(),s=new DOMParser,i=s.parseFromString(a,"text/html").querySelector("#window-vars-script");i&&new Function(i.innerHTML)(),await this.loadScript("/static/js/base.js");for(const t of this.scripts)await this.loadScript(t);const n=this.$router.currentRoute.value.meta.previousRouteName;n&&window.app._context.components[n]&&delete window.app._context.components[n];const o=`${this.$route.name}PageLogic`,l=window[o];if(!l)throw new Error(`Component logic '${o}' not found. Ensure it is defined in the script.`);l.mixins=l.mixins||[],window.windowMixin&&l.mixins.push(window.windowMixin),window.app.component(this.$route.name,{...l,template:a}),delete window[o],this.$forceUpdate()}catch(t){console.error("Error loading dynamic content:",t)}finally{this.$q.loading.hide()}}},watch:{$route(t,e){routes.map((t=>t.name)).includes(t.name)?(this.$router.currentRoute.value.meta.previousRouteName=e.name,this.loadDynamicContent()):console.log(`Route '${t.name}' is not valid. Leave this one to Fastapi.`)}},template:'\n \n '},routes=[{path:"/wallet",name:"Wallet",component:DynamicComponent,props:t=>{let e="/wallet";if(Object.keys(t.query).length>0){e+="?";for(const[a,s]of Object.entries(t.query))e+=`${a}=${s}&`;e=e.slice(0,-1)}return{fetchUrl:e,scripts:["/static/js/wallet.js"]}}},{path:"/admin",name:"Admin",component:DynamicComponent,props:{fetchUrl:"/admin",scripts:["/static/js/admin.js"]}},{path:"/users",name:"Users",component:DynamicComponent,props:{fetchUrl:"/users",scripts:["/static/js/users.js"]}},{path:"/extensions",name:"Extensions",component:DynamicComponent,props:{fetchUrl:"/extensions",scripts:["/static/js/extensions.js"]}},{path:"/extensions/builder",name:"ExtensionsBuilder",component:DynamicComponent,props:{fetchUrl:"/extensions/builder",scripts:["/static/js/extensions_builder.js"]}},{path:"/account",name:"Account",component:DynamicComponent,props:{fetchUrl:"/account",scripts:["/static/js/account.js"]}},{path:"/wallets",name:"Wallets",component:DynamicComponent,props:{fetchUrl:"/wallets",scripts:["/static/js/wallets.js"]}},{path:"/node",name:"Node",component:PageNode},{path:"/node/public",name:"NodePublic",component:PageNodePublic},{path:"/payments",name:"Payments",component:PagePayments},{path:"/audit",name:"Audit",component:PageAudit}];window.router=VueRouter.createRouter({history:VueRouter.createWebHistory(),routes:routes}),window.app.mixin({computed:{isVueRoute(){const t=window.location.pathname,e=window.router.resolve(t);return e?.matched?.length>0}}}),window.app.use(VueQrcodeReader),window.app.use(Quasar,{config:{loading:{spinner:Quasar.QSpinnerBars}}}),window.app.use(window.i18n),window.app.provide("g",g),window.app.use(window.router),window.app.component("DynamicComponent",DynamicComponent),window.app.mount("#vue"); diff --git a/lnbits/static/js/init-app.js b/lnbits/static/js/init-app.js index d5086d1f..408edd9d 100644 --- a/lnbits/static/js/init-app.js +++ b/lnbits/static/js/init-app.js @@ -157,15 +157,6 @@ const routes = [ scripts: ['/static/js/users.js'] } }, - { - path: '/audit', - name: 'Audit', - component: DynamicComponent, - props: { - fetchUrl: '/audit', - scripts: ['/static/js/audit.js'] - } - }, { path: '/extensions', name: 'Extensions', @@ -216,6 +207,11 @@ const routes = [ path: '/payments', name: 'Payments', component: PagePayments + }, + { + path: '/audit', + name: 'Audit', + component: PageAudit } ] diff --git a/lnbits/static/js/audit.js b/lnbits/static/js/pages/audit.js similarity index 99% rename from lnbits/static/js/audit.js rename to lnbits/static/js/pages/audit.js index 48ae6cfc..ef4d87a7 100644 --- a/lnbits/static/js/audit.js +++ b/lnbits/static/js/pages/audit.js @@ -1,4 +1,5 @@ -window.AuditPageLogic = { +window.PageAudit = { + template: '#page-audit', mixins: [window.windowMixin], data() { return { diff --git a/lnbits/static/vendor.json b/lnbits/static/vendor.json index ed2052ee..3c5d48af 100644 --- a/lnbits/static/vendor.json +++ b/lnbits/static/vendor.json @@ -46,6 +46,7 @@ "js/pages/payments.js", "js/pages/node.js", "js/pages/node-public.js", + "js/pages/audit.js", "js/components/lnbits-qrcode.js", "js/components/lnbits-qrcode-lnurl.js", "js/components/lnbits-funding-sources.js", diff --git a/lnbits/templates/pages.vue b/lnbits/templates/pages.vue index 6b797eac..99f0da28 100644 --- a/lnbits/templates/pages.vue +++ b/lnbits/templates/pages.vue @@ -1 +1,2 @@ -{% include('pages/payments.vue') %} {% include('pages/node.vue') %} +{% include('pages/payments.vue') %} {% include('pages/node.vue') %} {% +include('pages/audit.vue') %} diff --git a/lnbits/templates/pages/audit.vue b/lnbits/templates/pages/audit.vue new file mode 100644 index 00000000..2761ea65 --- /dev/null +++ b/lnbits/templates/pages/audit.vue @@ -0,0 +1,201 @@ + diff --git a/package.json b/package.json index eef65bb8..e709b479 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "js/pages/payments.js", "js/pages/node.js", "js/pages/node-public.js", + "js/pages/audit.js", "js/components/lnbits-qrcode.js", "js/components/lnbits-qrcode-lnurl.js", "js/components/lnbits-funding-sources.js",