Add nip05 and lnaddress endpoints

This commit is contained in:
austinkelsay 2024-09-16 17:13:23 -05:00
parent 365b54c498
commit ff3e907677
5 changed files with 161 additions and 0 deletions

View File

@ -14,6 +14,10 @@ module.exports = removeImports({
source: '/api/cron',
destination: '/api/cron',
},
{
source: "/.well-known/nostr.json",
destination: "/api/nip05",
}
];
},
});

View File

@ -0,0 +1,65 @@
import axios from "axios";
import crypto from "crypto";
import { runMiddleware, corsMiddleware } from "../../../utils/middleware";
import { verifyEvent } from 'nostr-tools/pure';
const BACKEND_URL = process.env.BACKEND_URL;
const RELAY_PUBKEY = process.env.RELAY_PUBKEY;
export default async function handler(req, res) {
await runMiddleware(req, res, corsMiddleware);
const { slug, ...queryParams } = req.query;
if (slug === 'austin') {
if (queryParams.amount) {
const amount = parseInt(queryParams.amount);
let metadata, metadataString, hash, descriptionHash;
if (queryParams.nostr) {
// This is a zap request
const zapRequest = JSON.parse(decodeURIComponent(queryParams.nostr));
// Verify the zap request
if (!verifyEvent(zapRequest)) {
res.status(400).json({ error: 'Invalid zap request' });
return;
}
// Validate zap request
if (zapRequest.kind !== 9734) {
res.status(400).json({ error: 'Invalid zap request' });
return;
}
metadataString = JSON.stringify(zapRequest);
hash = crypto.createHash('sha256').update(metadataString).digest('hex');
descriptionHash = Buffer.from(hash, 'hex').toString('base64');
} else {
// This is a regular lnurl-pay request
metadata = [
["text/plain", "PlebDevs LNURL endpoint, CHEERS!"]
];
metadataString = JSON.stringify(metadata);
hash = crypto.createHash('sha256').update(metadataString).digest('hex');
descriptionHash = Buffer.from(hash, 'hex').toString('base64');
}
// Convert amount from millisatoshis to satoshis
const value = amount / 1000;
if (value < 1) {
res.status(400).json({ error: 'Amount too low' });
return;
} else {
try {
const response = await axios.post(`${BACKEND_URL}/api/lnd`, { amount: value, description_hash: descriptionHash });
res.status(200).json({ pr: response.data });
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Failed to generate invoice' });
}
}
} else {
res.status(400).json({ error: 'Amount not specified' });
}
}
}

View File

@ -0,0 +1,50 @@
import axios from "axios";
import { finalizeEvent } from 'nostr-tools/pure';
import { SimplePool } from 'nostr-tools/pool';
const LND_HOST = process.env.LND_HOST;
const LND_MACAROON = process.env.LND_MACAROON;
const RELAY_PRIVKEY = process.env.RELAY_PRIVKEY;
export default async function handler(req, res) {
try {
const response = await axios.post(`https://${LND_HOST}/v1/invoices`, {
value: req.body.amount,
description_hash: req.body.description_hash
}, {
headers: {
'Grpc-Metadata-macaroon': LND_MACAROON,
}
});
const invoice = response.data.payment_request;
// If this is a zap, publish a zap receipt
if (req.body.zap_request) {
const zapRequest = JSON.parse(req.body.zap_request);
const zapReceipt = {
kind: 9735,
created_at: Math.floor(Date.now() / 1000),
content: '',
tags: [
['p', zapRequest.pubkey],
['e', zapRequest.id],
['bolt11', invoice],
['description', JSON.stringify(zapRequest)]
]
};
const signedZapReceipt = finalizeEvent(zapReceipt, RELAY_PRIVKEY);
// Publish zap receipt to relays
const pool = new SimplePool();
const relays = zapRequest.tags.find(tag => tag[0] === 'relays')?.[1] || [];
await pool.publish(relays, signedZapReceipt);
}
res.status(200).json(invoice);
} catch (error) {
console.error('Error (server) fetching data from LND:', error.message);
res.status(500).json({ message: 'Error fetching data' });
}
}

View File

@ -0,0 +1,32 @@
import { runMiddleware, corsMiddleware } from "../../../utils/middleware"
const BACKEND_URL = process.env.BACKEND_URL
const RELAY_PUBKEY = process.env.RELAY_PUBKEY
export default async function handler(req, res) {
await runMiddleware(req, res, corsMiddleware);
const { slug } = req.query
if (!slug || slug === 'undefined') {
res.status(404).json({ error: 'Not found' })
return
}
if (slug === 'austin') {
const metadata = [
["text/plain", "PlebDevs LNURL endpoint, CHEERS!"]
];
res.status(200).json({
callback: `${BACKEND_URL}/api/callback/austin`,
maxSendable: 10000000000,
minSendable: 1000,
metadata: JSON.stringify(metadata),
tag: 'payRequest',
allowsNostr: true,
nostrPubkey: RELAY_PUBKEY
})
return
}
}

10
src/pages/api/nip05.js Normal file
View File

@ -0,0 +1,10 @@
const nostrData = {
names: {
plebdevs:
"f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741",
},
};
export default async function Nip05(req, res) {
return res.status(200).json(nostrData);
}