diff --git a/lnbits/static/bundle-components.min.js b/lnbits/static/bundle-components.min.js index 43c3f6825..f405e22d2 100644 --- a/lnbits/static/bundle-components.min.js +++ b/lnbits/static/bundle-components.min.js @@ -1 +1 @@ -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(e){if(""===this.href)return this.copyText(this.value),e.preventDefault(),e.stopPropagation(),!1},async writeNfcTag(){try{if(!this.nfcSupported)throw{toString:function(){return"NFC not supported on this device or browser."}};const e=new NDEFReader;this.nfcTagWriting=!0,this.$q.notify({message:"Tap your NFC tag to write the LNURL-withdraw link to it."}),await e.write({records:[{recordType:"url",data:this.value,lang:"en"}]}),this.nfcTagWriting=!1,this.$q.notify({type:"positive",message:"NFC tag written successfully."})}catch(e){this.nfcTagWriting=!1,this.$q.notify({type:"negative",message:e?e.toString():"An unexpected error has occurred."})}},downloadSVG(){const e=this.$refs.qrCode.$el;if(!e)return void console.error("SVG element not found");let t=(new XMLSerializer).serializeToString(e);t.match(/^]+xmlns="http:\/\/www\.w3\.org\/2000\/svg"/)||(t=t.replace(/^({tab:"bech32",lnurl:""}),methods:{setLnurl(){if("bech32"==this.tab){const e=(new TextEncoder).encode(this.url),t=NostrTools.nip19.encodeBytes("lnurl",e);this.lnurl=`lightning:${t.toUpperCase()}`}else"lud17"==this.tab&&(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(e){const t=this.rawFundingSources.find((t=>t[0]===e));return t?t[1]:e},showQRValue(e){this.qrValue=e,this.showQRDialog=!0}},computed:{fundingSources(){let e=[];for(const[t,i,n]of this.rawFundingSources){const i={};if(null!==n)for(let[e,t]of Object.entries(n))i[e]="string"==typeof t?{label:t,value:null}:t||{};e.push([t,i])}return new Map(e)},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_admin_macaroon:"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:e}=await LNbits.api.request("PUT",this.endpoint,this.adminkey,this.settings);this.settings=e}catch(e){LNbits.utils.notifyApiError(e)}},async getSettings(){try{const{data:e}=await LNbits.api.request("GET",this.endpoint,this.adminkey);this.settings=e}catch(e){LNbits.utils.notifyApiError(e)}},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(e){LNbits.utils.notifyApiError(e)}}))}},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("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:e=>e.extra.wallet_fiat_currency},{name:"fiat_amount",align:"right",label:"Fiat Amount",field:e=>e.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 e=this.paymentsTable.search;return e&&""!==e?LNbits.utils.search(this.payments,e):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((e=>e.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(e){this.$emit("filter-changed",{...this.paymentsTable.filter});const t=LNbits.utils.prepareFilterQuery(this.paymentsTable,e);return LNbits.api.getPayments(this.currentWallet,t).then((e=>{this.paymentsTable.loading=!1,this.paymentsTable.pagination.rowsNumber=e.data.total,this.payments=e.data.data.map((e=>LNbits.map.payment(e)))})).catch((e=>{this.paymentsTable.loading=!1,g.user.admin?this.fetchPaymentsAsAdmin(this.currentWallet.id,t):LNbits.utils.notifyApiError(e)}))},fetchPaymentsAsAdmin(e,t){return t=(t||"")+"&wallet_id="+e,LNbits.api.request("GET","/api/v1/payments/all/paginated?"+t).then((e=>{this.paymentsTable.loading=!1,this.paymentsTable.pagination.rowsNumber=e.data.total,this.payments=e.data.data.map((e=>LNbits.map.payment(e)))})).catch((e=>{this.paymentsTable.loading=!1,LNbits.utils.notifyApiError(e)}))},checkPayment(e){LNbits.api.getPayment(this.g.wallet,e).then((e=>{this.update=!this.update,"success"==e.data.status&&Quasar.Notify.create({type:"positive",message:this.$t("payment_successful")}),"pending"==e.data.status&&Quasar.Notify.create({type:"info",message:this.$t("payment_pending")})})).catch(LNbits.utils.notifyApiError)},showHoldInvoiceDialog(e){this.hodlInvoice.show=!0,this.hodlInvoice.preimage="",this.hodlInvoice.payment=e},cancelHoldInvoice(e){LNbits.api.cancelInvoice(this.g.wallet,e).then((()=>{this.update=!this.update,Quasar.Notify.create({type:"positive",message:this.$t("invoice_cancelled")})})).catch(LNbits.utils.notifyApiError)},settleHoldInvoice(e){LNbits.api.settleInvoice(this.g.wallet,e).then((()=>{this.update=!this.update,Quasar.Notify.create({type:"positive",message:this.$t("invoice_settled")})})).catch(LNbits.utils.notifyApiError)},paymentTableRowKey:e=>e.payment_hash+e.amount,exportCSV(e=!1){const t=this.paymentsTable.pagination,i={sortby:t.sortBy??"time",direction:t.descending?"desc":"asc"},n=new URLSearchParams(i);LNbits.api.getPayments(this.g.wallet,n).then((t=>{let i=t.data.data.map(LNbits.map.payment),n=this.paymentsCSV.columns;if(e){this.exportPaymentTagList.length&&(i=i.filter((e=>this.exportPaymentTagList.includes(e.tag))));const e=Object.keys(i.reduce(((e,t)=>({...e,...t.details})),{})).map((e=>({name:e,align:"right",label:e.charAt(0).toUpperCase()+e.slice(1).replace(/([A-Z])/g," $1"),field:t=>t.details[e],format:e=>"object"==typeof e?JSON.stringify(e):e})));n=this.paymentsCSV.columns.concat(e)}LNbits.utils.exportCSV(n,i,this.g.wallet.name+"-payments")}))},addFilterTag(){if(!this.exportTagName)return;const e=this.exportTagName.trim();this.exportPaymentTagList=this.exportPaymentTagList.filter((t=>t!==e)),this.exportPaymentTagList.push(e),this.exportTagName=""},removeExportTag(e){this.exportPaymentTagList=this.exportPaymentTagList.filter((t=>t!==e))},formatCurrency(e,t){try{return LNbits.utils.formatCurrency(e,t)}catch(t){return console.error(t),`${e} ???`}},handleFilterChanged(){const{success:e,pending:t,failed:i,incoming:n,outgoing:s}=this.searchStatus;delete this.paymentsTable.filter["status[ne]"],delete this.paymentsTable.filter["status[eq]"],e&&t&&i||(e&&t?this.paymentsTable.filter["status[ne]"]="failed":e&&i?this.paymentsTable.filter["status[ne]"]="pending":i&&t?this.paymentsTable.filter["status[ne]"]="success":e?this.paymentsTable.filter["status[eq]"]="success":t?this.paymentsTable.filter["status[eq]"]="pending":i&&(this.paymentsTable.filter["status[eq]"]="failed")),delete this.paymentsTable.filter["amount[ge]"],delete this.paymentsTable.filter["amount[le]"],n&&s||(n?this.paymentsTable.filter["amount[ge]"]=0:s&&(this.paymentsTable.filter["amount[le]"]=0))}},watch:{"paymentsTable.search":{handler(){const e={};this.paymentsTable.search&&(e.search=this.paymentsTable.search),this.fetchPayments()}},lazy(e){!0===e&&this.fetchPayments()},update(){this.fetchPayments()},"g.updatePayments"(){this.fetchPayments()},"g.wallet":{handler(e){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(e){this.loadExtensions()},deep:!0}},computed:{userExtensions(){return this.updateUserExtensions(this.searchTerm)}},methods:{async loadExtensions(){try{const{data:e}=await LNbits.api.request("GET","/api/v1/extension");this.extensions=e.map((e=>LNbits.map.extension(e))).sort(((e,t)=>e.name.localeCompare(t.name)))}catch(e){LNbits.utils.notifyApiError(e)}},updateUserExtensions(e){const t=window.location.pathname,i=this.g.user.extensions;return this.extensions.filter((e=>i.includes(e.code))).filter((t=>!e||`${t.code} ${t.name} ${t.short_description} ${t.url}`.toLocaleLowerCase().includes(e.toLocaleLowerCase()))).map((e=>(e.isActive=t.startsWith(e.url),e)))}},async created(){await this.loadExtensions()}}),window.app.component("lnbits-manage",{mixins:[window.windowMixin],template:"#lnbits-manage",props:["showAdmin","showNode","showExtensions","showUsers","showAudit"],methods:{isActive:e=>window.location.pathname===e},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 e=_.omit(this.payment.extra,["tag","success_action"]);return Object.keys(e).map((t=>({key:t,value:e[t]})))}}}),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((e=>{this.decryptedValue=e}))}}),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(e){const t=(e+"=".repeat((4-e.length%4)%4)).replace(/\-/g,"+").replace(/_/g,"/"),i=atob(t),n=new Uint8Array(i.length);for(let e=0;et!==e)),this.$q.localStorage.set("lnbits.webpush.subscribedUsers",JSON.stringify(t))},isUserSubscribed(e){return(JSON.parse(this.$q.localStorage.getItem("lnbits.webpush.subscribedUsers"))||[]).includes(e)},subscribe(){this.isSupported&&!this.isPermissionDenied&&(Notification.requestPermission().then((e=>{this.isPermissionGranted="granted"===e,this.isPermissionDenied="denied"===e})).catch(console.log),navigator.serviceWorker.ready.then((e=>{navigator.serviceWorker.getRegistration().then((e=>{e.pushManager.getSubscription().then((t=>{if(null===t||!this.isUserSubscribed(this.g.user.id)){const t={applicationServerKey:this.urlB64ToUint8Array(this.pubkey),userVisibleOnly:!0};e.pushManager.subscribe(t).then((e=>{LNbits.api.request("POST","/api/v1/webpush",null,{subscription:JSON.stringify(e)}).then((e=>{this.saveUserSubscribed(e.data.user),this.isSubscribed=!0})).catch(LNbits.utils.notifyApiError)}))}})).catch(console.log)}))})))},unsubscribe(){navigator.serviceWorker.ready.then((e=>{e.pushManager.getSubscription().then((e=>{e&&LNbits.api.request("DELETE","/api/v1/webpush?endpoint="+btoa(e.endpoint),null).then((()=>{this.removeUserSubscribed(this.g.user.id),this.isSubscribed=!1})).catch(LNbits.utils.notifyApiError)}))})).catch(console.log)},checkSupported(){let e="https:"===window.location.protocol,t="serviceWorker"in navigator,i="Notification"in window,n="PushManager"in window;return this.isSupported=e&&t&&i&&n,this.isSupported||console.log("Notifications disabled because requirements are not met:",{HTTPS:e,"Service Worker API":t,"Notification API":i,"Push API":n}),this.isSupported},async updateSubscriptionStatus(){await navigator.serviceWorker.ready.then((e=>{e.pushManager.getSubscription().then((e=>{this.isSubscribed=!!e&&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:[e=>!!e||"Field is required"]}),methods:{applyRules(e){return e?this.rules:[]},buildData(e,t={}){return e.reduce(((e,i)=>(i.options?.length?e[i.name]=this.buildData(i.options,t[i.name]):e[i.name]=t[i.name]??i.default,e)),{})},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(e){this.chips.splice(e,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(e){LNbits.api.updateBalance(e.value,this.wallet_id).then((t=>{if(!0!==t.data.success)throw new Error(t.data);credit=parseInt(e.value),Quasar.Notify.create({type:"positive",message:this.$t("credit_ok",{amount:credit}),icon:null}),this.credit=0,e.value=0,e.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(e){this.$emit("show-login",e)},showRegister(e){this.$emit("show-register",e)},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:e=>new RegExp("^(?=[a-zA-Z0-9._]{2,20}$)(?!.*[_.]{2})[^_.].*[^_.]$").test(e),async signInWithNostr(){try{const e=await this.createNostrToken();if(!e)return;resp=await LNbits.api.loginByProvider("nostr",{Authorization:e},{}),window.location.href="/wallet"}catch(e){console.warn(e);const t=e?.response?.data?.detail||`${e}`;Quasar.Notify.create({type:"negative",message:"Failed to sign in with Nostr.",caption:t})}},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 e=`${window.location}nostr`,t="POST",i=await NostrTools.nip98.getToken(e,t,(e=>async function(e){try{const{data:t}=await LNbits.api.getServerHealth();return e.created_at=t.server_time,await window.nostr.signEvent(e)}catch(e){console.error(e),Quasar.Notify.create({type:"negative",message:"Failed to sign nostr event.",caption:`${e}`})}}(e)),!0);if(!await NostrTools.nip98.validateToken(i,e,t))throw new Error("Invalid signed token!");return i}catch(e){console.warn(e),Quasar.Notify.create({type:"negative",message:"Failed create Nostr event.",caption:`${e}`})}}},computed:{showOauth(){return this.oauth.some((e=>this.authMethods.includes(e)))}},created(){}}),window.app.component("separator-text",{template:"#separator-text",props:{text:String,uppercase:{type:Boolean,default:!1},color:{type:String,default:"grey"}}});const DynamicComponent={props:{fetchUrl:{type:String,required:!0},scripts:{type:Array,default:()=>[]}},data:()=>({keys:[]}),async mounted(){await this.loadDynamicContent()},methods:{loadScript:async e=>new Promise(((t,i)=>{const n=document.querySelector(`script[src="${e}"]`);n&&n.remove();const s=document.createElement("script");s.src=e,s.async=!0,s.onload=t,s.onerror=()=>i(new Error(`Failed to load script: ${e}`)),document.head.appendChild(s)})),async loadDynamicContent(){this.$q.loading.show();try{const e=this.fetchUrl.split("#")[0],t=await fetch(e,{credentials:"include",headers:{Accept:"text/html","X-Requested-With":"XMLHttpRequest"}}),i=await t.text(),n=new DOMParser,s=n.parseFromString(i,"text/html").querySelector("#window-vars-script");s&&new Function(s.innerHTML)(),await this.loadScript("/static/js/base.js");for(const e of this.scripts)await this.loadScript(e);const a=this.$router.currentRoute.value.meta.previousRouteName;a&&window.app._context.components[a]&&delete window.app._context.components[a];const o=`${this.$route.name}PageLogic`,r=window[o];if(!r)throw new Error(`Component logic '${o}' not found. Ensure it is defined in the script.`);r.mixins=r.mixins||[],window.windowMixin&&r.mixins.push(window.windowMixin),window.app.component(this.$route.name,{...r,template:i}),delete window[o],this.$forceUpdate()}catch(e){console.error("Error loading dynamic content:",e)}finally{this.$q.loading.hide()}}},watch:{$route(e,t){routes.map((e=>e.name)).includes(e.name)?(this.$router.currentRoute.value.meta.previousRouteName=t.name,this.loadDynamicContent()):console.log(`Route '${e.name}' is not valid. Leave this one to Fastapi.`)}},template:'\n \n '},routes=[{path:"/wallet",name:"Wallet",component:DynamicComponent,props:e=>{let t="/wallet";if(Object.keys(e.query).length>0){t+="?";for(const[i,n]of Object.entries(e.query))t+=`${i}=${n}&`;t=t.slice(0,-1)}return{fetchUrl:t,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:"/payments",name:"Payments",component:DynamicComponent,props:{fetchUrl:"/payments",scripts:["/static/js/payments.js"]}},{path:"/extensions",name:"Extensions",component:DynamicComponent,props:{fetchUrl:"/extensions",scripts:["/static/js/extensions.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:DynamicComponent,props:{fetchUrl:"/node",scripts:["/static/js/node.js"]}}];window.router=VueRouter.createRouter({history:VueRouter.createWebHistory(),routes:routes}),window.app.mixin({computed:{isVueRoute(){const e=window.location.pathname,t=window.router.resolve(e);return t?.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.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(e){if(""===this.href)return this.copyText(this.value),e.preventDefault(),e.stopPropagation(),!1},async writeNfcTag(){try{if(!this.nfcSupported)throw{toString:function(){return"NFC not supported on this device or browser."}};const e=new NDEFReader;this.nfcTagWriting=!0,this.$q.notify({message:"Tap your NFC tag to write the LNURL-withdraw link to it."}),await e.write({records:[{recordType:"url",data:this.value,lang:"en"}]}),this.nfcTagWriting=!1,this.$q.notify({type:"positive",message:"NFC tag written successfully."})}catch(e){this.nfcTagWriting=!1,this.$q.notify({type:"negative",message:e?e.toString():"An unexpected error has occurred."})}},downloadSVG(){const e=this.$refs.qrCode.$el;if(!e)return void console.error("SVG element not found");let t=(new XMLSerializer).serializeToString(e);t.match(/^]+xmlns="http:\/\/www\.w3\.org\/2000\/svg"/)||(t=t.replace(/^({tab:"bech32",lnurl:""}),methods:{setLnurl(){if("bech32"==this.tab){const e=(new TextEncoder).encode(this.url),t=NostrTools.nip19.encodeBytes("lnurl",e);this.lnurl=`lightning:${t.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(e){const t=this.rawFundingSources.find((t=>t[0]===e));return t?t[1]:e},showQRValue(e){this.qrValue=e,this.showQRDialog=!0}},computed:{fundingSources(){let e=[];for(const[t,i,s]of this.rawFundingSources){const i={};if(null!==s)for(let[e,t]of Object.entries(s))i[e]="string"==typeof t?{label:t,value:null}:t||{};e.push([t,i])}return new Map(e)},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_admin_macaroon:"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:e}=await LNbits.api.request("PUT",this.endpoint,this.adminkey,this.settings);this.settings=e}catch(e){LNbits.utils.notifyApiError(e)}},async getSettings(){try{const{data:e}=await LNbits.api.request("GET",this.endpoint,this.adminkey);this.settings=e}catch(e){LNbits.utils.notifyApiError(e)}},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(e){LNbits.utils.notifyApiError(e)}}))}},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("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:e=>e.extra.wallet_fiat_currency},{name:"fiat_amount",align:"right",label:"Fiat Amount",field:e=>e.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 e=this.paymentsTable.search;return e&&""!==e?LNbits.utils.search(this.payments,e):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((e=>e.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(e){this.$emit("filter-changed",{...this.paymentsTable.filter});const t=LNbits.utils.prepareFilterQuery(this.paymentsTable,e);return LNbits.api.getPayments(this.currentWallet,t).then((e=>{this.paymentsTable.loading=!1,this.paymentsTable.pagination.rowsNumber=e.data.total,this.payments=e.data.data.map((e=>LNbits.map.payment(e)))})).catch((e=>{this.paymentsTable.loading=!1,g.user.admin?this.fetchPaymentsAsAdmin(this.currentWallet.id,t):LNbits.utils.notifyApiError(e)}))},fetchPaymentsAsAdmin(e,t){return t=(t||"")+"&wallet_id="+e,LNbits.api.request("GET","/api/v1/payments/all/paginated?"+t).then((e=>{this.paymentsTable.loading=!1,this.paymentsTable.pagination.rowsNumber=e.data.total,this.payments=e.data.data.map((e=>LNbits.map.payment(e)))})).catch((e=>{this.paymentsTable.loading=!1,LNbits.utils.notifyApiError(e)}))},checkPayment(e){LNbits.api.getPayment(this.g.wallet,e).then((e=>{this.update=!this.update,"success"==e.data.status&&Quasar.Notify.create({type:"positive",message:this.$t("payment_successful")}),"pending"==e.data.status&&Quasar.Notify.create({type:"info",message:this.$t("payment_pending")})})).catch(LNbits.utils.notifyApiError)},showHoldInvoiceDialog(e){this.hodlInvoice.show=!0,this.hodlInvoice.preimage="",this.hodlInvoice.payment=e},cancelHoldInvoice(e){LNbits.api.cancelInvoice(this.g.wallet,e).then((()=>{this.update=!this.update,Quasar.Notify.create({type:"positive",message:this.$t("invoice_cancelled")})})).catch(LNbits.utils.notifyApiError)},settleHoldInvoice(e){LNbits.api.settleInvoice(this.g.wallet,e).then((()=>{this.update=!this.update,Quasar.Notify.create({type:"positive",message:this.$t("invoice_settled")})})).catch(LNbits.utils.notifyApiError)},paymentTableRowKey:e=>e.payment_hash+e.amount,exportCSV(e=!1){const t=this.paymentsTable.pagination,i={sortby:t.sortBy??"time",direction:t.descending?"desc":"asc"},s=new URLSearchParams(i);LNbits.api.getPayments(this.g.wallet,s).then((t=>{let i=t.data.data.map(LNbits.map.payment),s=this.paymentsCSV.columns;if(e){this.exportPaymentTagList.length&&(i=i.filter((e=>this.exportPaymentTagList.includes(e.tag))));const e=Object.keys(i.reduce(((e,t)=>({...e,...t.details})),{})).map((e=>({name:e,align:"right",label:e.charAt(0).toUpperCase()+e.slice(1).replace(/([A-Z])/g," $1"),field:t=>t.details[e],format:e=>"object"==typeof e?JSON.stringify(e):e})));s=this.paymentsCSV.columns.concat(e)}LNbits.utils.exportCSV(s,i,this.g.wallet.name+"-payments")}))},addFilterTag(){if(!this.exportTagName)return;const e=this.exportTagName.trim();this.exportPaymentTagList=this.exportPaymentTagList.filter((t=>t!==e)),this.exportPaymentTagList.push(e),this.exportTagName=""},removeExportTag(e){this.exportPaymentTagList=this.exportPaymentTagList.filter((t=>t!==e))},formatCurrency(e,t){try{return LNbits.utils.formatCurrency(e,t)}catch(t){return console.error(t),`${e} ???`}},handleFilterChanged(){const{success:e,pending:t,failed:i,incoming:s,outgoing:n}=this.searchStatus;delete this.paymentsTable.filter["status[ne]"],delete this.paymentsTable.filter["status[eq]"],e&&t&&i||(e&&t?this.paymentsTable.filter["status[ne]"]="failed":e&&i?this.paymentsTable.filter["status[ne]"]="pending":i&&t?this.paymentsTable.filter["status[ne]"]="success":e?this.paymentsTable.filter["status[eq]"]="success":t?this.paymentsTable.filter["status[eq]"]="pending":i&&(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 e={};this.paymentsTable.search&&(e.search=this.paymentsTable.search),this.fetchPayments()}},lazy(e){!0===e&&this.fetchPayments()},update(){this.fetchPayments()},"g.updatePayments"(){this.fetchPayments()},"g.wallet":{handler(e){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(e){this.loadExtensions()},deep:!0}},computed:{userExtensions(){return this.updateUserExtensions(this.searchTerm)}},methods:{async loadExtensions(){try{const{data:e}=await LNbits.api.request("GET","/api/v1/extension");this.extensions=e.map((e=>LNbits.map.extension(e))).sort(((e,t)=>e.name.localeCompare(t.name)))}catch(e){LNbits.utils.notifyApiError(e)}},updateUserExtensions(e){const t=window.location.pathname,i=this.g.user.extensions;return this.extensions.filter((e=>i.includes(e.code))).filter((t=>!e||`${t.code} ${t.name} ${t.short_description} ${t.url}`.toLocaleLowerCase().includes(e.toLocaleLowerCase()))).map((e=>(e.isActive=t.startsWith(e.url),e)))}},async created(){await this.loadExtensions()}}),window.app.component("lnbits-manage",{mixins:[window.windowMixin],template:"#lnbits-manage",props:["showAdmin","showNode","showExtensions","showUsers","showAudit"],methods:{isActive:e=>window.location.pathname===e},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 e=_.omit(this.payment.extra,["tag","success_action"]);return Object.keys(e).map((t=>({key:t,value:e[t]})))}}}),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((e=>{this.decryptedValue=e}))}}),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(e){const t=(e+"=".repeat((4-e.length%4)%4)).replace(/\-/g,"+").replace(/_/g,"/"),i=atob(t),s=new Uint8Array(i.length);for(let e=0;et!==e)),this.$q.localStorage.set("lnbits.webpush.subscribedUsers",JSON.stringify(t))},isUserSubscribed(e){return(JSON.parse(this.$q.localStorage.getItem("lnbits.webpush.subscribedUsers"))||[]).includes(e)},subscribe(){this.isSupported&&!this.isPermissionDenied&&(Notification.requestPermission().then((e=>{this.isPermissionGranted="granted"===e,this.isPermissionDenied="denied"===e})).catch(console.log),navigator.serviceWorker.ready.then((e=>{navigator.serviceWorker.getRegistration().then((e=>{e.pushManager.getSubscription().then((t=>{if(null===t||!this.isUserSubscribed(this.g.user.id)){const t={applicationServerKey:this.urlB64ToUint8Array(this.pubkey),userVisibleOnly:!0};e.pushManager.subscribe(t).then((e=>{LNbits.api.request("POST","/api/v1/webpush",null,{subscription:JSON.stringify(e)}).then((e=>{this.saveUserSubscribed(e.data.user),this.isSubscribed=!0})).catch(LNbits.utils.notifyApiError)}))}})).catch(console.log)}))})))},unsubscribe(){navigator.serviceWorker.ready.then((e=>{e.pushManager.getSubscription().then((e=>{e&&LNbits.api.request("DELETE","/api/v1/webpush?endpoint="+btoa(e.endpoint),null).then((()=>{this.removeUserSubscribed(this.g.user.id),this.isSubscribed=!1})).catch(LNbits.utils.notifyApiError)}))})).catch(console.log)},checkSupported(){let e="https:"===window.location.protocol,t="serviceWorker"in navigator,i="Notification"in window,s="PushManager"in window;return this.isSupported=e&&t&&i&&s,this.isSupported||console.log("Notifications disabled because requirements are not met:",{HTTPS:e,"Service Worker API":t,"Notification API":i,"Push API":s}),this.isSupported},async updateSubscriptionStatus(){await navigator.serviceWorker.ready.then((e=>{e.pushManager.getSubscription().then((e=>{this.isSubscribed=!!e&&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:[e=>!!e||"Field is required"]}),methods:{applyRules(e){return e?this.rules:[]},buildData(e,t={}){return e.reduce(((e,i)=>(i.options?.length?e[i.name]=this.buildData(i.options,t[i.name]):e[i.name]=t[i.name]??i.default,e)),{})},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(e){this.chips.splice(e,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(e){LNbits.api.updateBalance(e.value,this.wallet_id).then((t=>{if(!0!==t.data.success)throw new Error(t.data);credit=parseInt(e.value),Quasar.Notify.create({type:"positive",message:this.$t("credit_ok",{amount:credit}),icon:null}),this.credit=0,e.value=0,e.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(e){this.$emit("show-login",e)},showRegister(e){this.$emit("show-register",e)},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:e=>new RegExp("^(?=[a-zA-Z0-9._]{2,20}$)(?!.*[_.]{2})[^_.].*[^_.]$").test(e),async signInWithNostr(){try{const e=await this.createNostrToken();if(!e)return;resp=await LNbits.api.loginByProvider("nostr",{Authorization:e},{}),window.location.href="/wallet"}catch(e){console.warn(e);const t=e?.response?.data?.detail||`${e}`;Quasar.Notify.create({type:"negative",message:"Failed to sign in with Nostr.",caption:t})}},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 e=`${window.location}nostr`,t="POST",i=await NostrTools.nip98.getToken(e,t,(e=>async function(e){try{const{data:t}=await LNbits.api.getServerHealth();return e.created_at=t.server_time,await window.nostr.signEvent(e)}catch(e){console.error(e),Quasar.Notify.create({type:"negative",message:"Failed to sign nostr event.",caption:`${e}`})}}(e)),!0);if(!await NostrTools.nip98.validateToken(i,e,t))throw new Error("Invalid signed token!");return i}catch(e){console.warn(e),Quasar.Notify.create({type:"negative",message:"Failed create Nostr event.",caption:`${e}`})}}},computed:{showOauth(){return this.oauth.some((e=>this.authMethods.includes(e)))}},created(){}}),window.app.component("separator-text",{template:"#separator-text",props:{text:String,uppercase:{type:Boolean,default:!1},color:{type:String,default:"grey"}}});const DynamicComponent={props:{fetchUrl:{type:String,required:!0},scripts:{type:Array,default:()=>[]}},data:()=>({keys:[]}),async mounted(){await this.loadDynamicContent()},methods:{loadScript:async e=>new Promise(((t,i)=>{const s=document.querySelector(`script[src="${e}"]`);s&&s.remove();const n=document.createElement("script");n.src=e,n.async=!0,n.onload=t,n.onerror=()=>i(new Error(`Failed to load script: ${e}`)),document.head.appendChild(n)})),async loadDynamicContent(){this.$q.loading.show();try{const e=this.fetchUrl.split("#")[0],t=await fetch(e,{credentials:"include",headers:{Accept:"text/html","X-Requested-With":"XMLHttpRequest"}}),i=await t.text(),s=new DOMParser,n=s.parseFromString(i,"text/html").querySelector("#window-vars-script");n&&new Function(n.innerHTML)(),await this.loadScript("/static/js/base.js");for(const e of this.scripts)await this.loadScript(e);const a=this.$router.currentRoute.value.meta.previousRouteName;a&&window.app._context.components[a]&&delete window.app._context.components[a];const o=`${this.$route.name}PageLogic`,r=window[o];if(!r)throw new Error(`Component logic '${o}' not found. Ensure it is defined in the script.`);r.mixins=r.mixins||[],window.windowMixin&&r.mixins.push(window.windowMixin),window.app.component(this.$route.name,{...r,template:i}),delete window[o],this.$forceUpdate()}catch(e){console.error("Error loading dynamic content:",e)}finally{this.$q.loading.hide()}}},watch:{$route(e,t){routes.map((e=>e.name)).includes(e.name)?(this.$router.currentRoute.value.meta.previousRouteName=t.name,this.loadDynamicContent()):console.log(`Route '${e.name}' is not valid. Leave this one to Fastapi.`)}},template:'\n \n '},routes=[{path:"/wallet",name:"Wallet",component:DynamicComponent,props:e=>{let t="/wallet";if(Object.keys(e.query).length>0){t+="?";for(const[i,s]of Object.entries(e.query))t+=`${i}=${s}&`;t=t.slice(0,-1)}return{fetchUrl:t,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:"/payments",name:"Payments",component:DynamicComponent,props:{fetchUrl:"/payments",scripts:["/static/js/payments.js"]}},{path:"/extensions",name:"Extensions",component:DynamicComponent,props:{fetchUrl:"/extensions",scripts:["/static/js/extensions.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:DynamicComponent,props:{fetchUrl:"/node",scripts:["/static/js/node.js"]}}];window.router=VueRouter.createRouter({history:VueRouter.createWebHistory(),routes:routes}),window.app.mixin({computed:{isVueRoute(){const e=window.location.pathname,t=window.router.resolve(e);return t?.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/components/lnbits-qrcode-lnurl.js b/lnbits/static/js/components/lnbits-qrcode-lnurl.js index 31a50c406..3a2214f19 100644 --- a/lnbits/static/js/components/lnbits-qrcode-lnurl.js +++ b/lnbits/static/js/components/lnbits-qrcode-lnurl.js @@ -24,7 +24,11 @@ window.app.component('lnbits-qrcode-lnurl', { const bech32 = NostrTools.nip19.encodeBytes('lnurl', bytes) this.lnurl = `lightning:${bech32.toUpperCase()}` } else if (this.tab == 'lud17') { - this.lnurl = this.url.replace('https://', this.prefix + '://') + if (this.url.startsWith('http://')) { + this.lnurl = this.url.replace('http://', this.prefix + '://') + } else { + this.lnurl = this.url.replace('https://', this.prefix + '://') + } } this.$emit('update:lnurl', this.lnurl) }