diff --git a/api.ts b/api.ts index acf220c..a9a8024 100644 --- a/api.ts +++ b/api.ts @@ -1,8 +1,11 @@ import { Router } from "https://deno.land/x/oak/mod.ts"; +import { exec, OutputMode } from "https://deno.land/x/exec/mod.ts"; import { getBlocks } from "./blocks.ts"; import { getblockchaininfo, getblockcount, getblockhash } from "./jsonrpc/index.ts"; import Pageviews from "./pageviews/index.ts"; +import config from "./config/config.ts"; +import { bytesToHexString } from "./utils.ts"; const router = new Router(); @@ -23,6 +26,7 @@ router status: "ERROR", reason: "Missing param height", }); + return; } const blockhash = await getblockhash(Number.parseInt(context.params.height)); context.response.body = blockhash; @@ -37,4 +41,24 @@ router.get("/blocks", (context) => { context.response.body = blocks; }); +router.get("/invoice", async (context) => { + if (!config.donation) { + context.response.status = 400; + context.response.body = JSON.stringify({ + status: "ERROR", + reason: "Donation is not configured", + }); + return; + } + + const macaroonHeader = bytesToHexString(await Deno.readFile(config.donation.data.macaroon)); + // -d '{"memo":"Donation"}' + const command = `curl -X POST --cacert ${config.donation.data.cert} --header "Grpc-Metadata-macaroon: ${macaroonHeader}" ${config.donation.data.server}/v1/invoices`; + + const result = await exec(command, { + output: OutputMode.Capture, + }); + context.response.body = JSON.parse(result.output).payment_request; +}); + export default router; diff --git a/config/config.ts_TEMPLATE b/config/config.ts_TEMPLATE index 09dd438..536ba44 100644 --- a/config/config.ts_TEMPLATE +++ b/config/config.ts_TEMPLATE @@ -29,6 +29,21 @@ interface Config { // Threshold for the softfork to be locked in threshold: number; }; + + // Donation configuration, right now only supports lnd + donation?: { + // Backend type, currently only supports lnd + type: "lnd"; + // Data for the backend + data: { + // REST server + server: string; + // Path to tls.cert + cert: string; + // Path to invoice.macaroon + macaroon: string; + }; + }; } const config: Config = { @@ -49,6 +64,15 @@ const config: Config = { versionBit: 2, threshold: 1845, }, + + // donation: { + // type: "lnd", + // data: { + // server: "https://127.0.0.1:8080", + // cert: "/path/to/tls.cert", + // macaroon: "/path/to/invoice.macaroon", + // }, + // }, }; export default config; diff --git a/frontend/components/Donation.tsx b/frontend/components/Donation.tsx index b1f5320..665d90c 100644 --- a/frontend/components/Donation.tsx +++ b/frontend/components/Donation.tsx @@ -1,9 +1,6 @@ import React, { useState } from "https://esm.sh/react@17.0.2"; import styled from "https://esm.sh/styled-components"; import { QRCode } from "https://esm.sh/react-qr-svg"; -import { Link } from "https://deno.land/x/aleph@v0.2.28/mod.ts"; - -const DONATION_LNURL_PAY = "LNURL1DP68GURN8GHJ7MRWVF5HGUEWVDHK6TMVDE6HYMRS9ASHQ6F0WCCJ7MRWW4EXCTE38Y6QYWETXD"; export const DonateContainer = styled.div` text-align: center; @@ -18,22 +15,27 @@ export const DonateText = styled.a` `; export function Donation() { - const [showQr, setShowQr] = useState(false); + const [invoice, setInvoice] = useState(undefined); + + const fetchInvoice = async () => { + const result = await fetch("/invoice"); + setInvoice(await result.text()); + }; return ( - {!showQr && setShowQr(true)}>Donate via Lightning Network} - {showQr && ( + {!invoice && Donate via Lightning Network} + {invoice && (
{ - window.location.replace("lightning:" + DONATION_LNURL_PAY.toLowerCase()); + window.location.replace("lightning:" + invoice); }} >
diff --git a/run-server.sh b/run-server.sh index 7d420d7..e244263 100755 --- a/run-server.sh +++ b/run-server.sh @@ -6,4 +6,4 @@ mkdir frontend/dist deno run -A https://deno.land/x/aleph@v0.3.0-alpha.32/cli.ts build frontend --reload # Run backend -deno run --unstable --allow-net --allow-read --allow-write index.ts +deno run --unstable --allow-net --allow-read --allow-write --allow-run index.ts diff --git a/utils.ts b/utils.ts new file mode 100644 index 0000000..e05c02a --- /dev/null +++ b/utils.ts @@ -0,0 +1,5 @@ +export const bytesToHexString = (bytes: Uint8Array) => { + return bytes.reduce(function (memo: unknown, i: number) { + return memo + ("0" + i.toString(16)).slice(-2); //padd with leading 0 if <16 + }, ""); +};