diff options
| author | Adam Soltys <[email protected]> | 2026-02-28 14:14:30 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-02-28 14:14:30 -0800 |
| commit | a1632ae402d11fdd7ffc360de97c55e0b4fad677 (patch) | |
| tree | aad5a1ea73f95dcd055c1d0b4ebf2708c36b4e01 /routes | |
| parent | 76c7e52172f3dc25ee109434157d4d982ad9d2bd (diff) | |
| download | coinos-server-a1632ae402d11fdd7ffc360de97c55e0b4fad677.tar.gz coinos-server-a1632ae402d11fdd7ffc360de97c55e0b4fad677.zip | |
migrate to hono
Diffstat (limited to 'routes')
| -rw-r--r-- | routes/ecash.ts | 63 | ||||
| -rw-r--r-- | routes/email.ts | 7 | ||||
| -rw-r--r-- | routes/info.ts | 16 | ||||
| -rw-r--r-- | routes/invoices.ts | 51 | ||||
| -rw-r--r-- | routes/items.ts | 59 | ||||
| -rw-r--r-- | routes/lnurl.ts | 67 | ||||
| -rw-r--r-- | routes/locations.ts | 20 | ||||
| -rw-r--r-- | routes/nostr.ts | 139 | ||||
| -rw-r--r-- | routes/payments.ts | 358 | ||||
| -rw-r--r-- | routes/rates.ts | 12 | ||||
| -rw-r--r-- | routes/shopify.ts | 19 | ||||
| -rw-r--r-- | routes/square.ts | 24 | ||||
| -rw-r--r-- | routes/users.ts | 579 |
13 files changed, 681 insertions, 733 deletions
diff --git a/routes/ecash.ts b/routes/ecash.ts index b4c16404..fc6a8d3a 100644 --- a/routes/ecash.ts +++ b/routes/ecash.ts @@ -11,7 +11,7 @@ import { v4 } from "uuid"; import { PaymentType } from "$lib/types"; const { ecash: type } = PaymentType; -Error.stackTraceLimit = 100; // Set this to the desired limit +Error.stackTraceLimit = 100; const sendCash = async ({ amount, user }) => { const id = v4(); @@ -28,39 +28,33 @@ const sendCash = async ({ amount, user }) => { }; export default { - async save(req, res) { - const { - body: { token }, - } = req; + async save(c) { + const { token } = await c.req.json(); try { const id = v4(); await s(`cash:${id}`, token); - res.send({ id }); + return c.json({ id }); } catch (e) { err(e.message); - bail(res, e.message); + return bail(c, e.message); } }, - async get(req, res) { - const { - params: { id }, - } = req; + async get(c) { + const id = c.req.param("id"); try { const token = await get(id); const status = await check(token); - res.send({ token, status }); + return c.json({ token, status }); } catch (e) { err(e.message); - bail(res, e.message); + return bail(c, e.message); } }, - async claim(req, res) { - const { - body: { token }, - user, - } = req; + async claim(c) { + const { token } = await c.req.json(); + const user = c.get("user"); try { const amount = await claim(token); @@ -79,25 +73,25 @@ export default { await credit({ hash, amount, memo, ref: user.id, type }); - res.send({ ok: true }); + return c.json({ ok: true }); } catch (e) { err(e.message); - bail(res, e.message); + return bail(c, e.message); } }, - async mint(req, res) { - const { body, user } = req; + async mint(c) { + const body = await c.req.json(); + const user = c.get("user"); const amount = parseInt(body.amount); fail("disabled"); - res.send(await sendCash({ amount, user })); + return c.json(await sendCash({ amount, user })); }, - async melt(req, res) { - let { - body: { amount, bolt11: hash, preimage }, - user, - } = req; + async melt(c) { + const body = await c.req.json(); + const user = c.get("user"); + let { amount, bolt11: hash, preimage } = body; try { amount = Math.round(amount / 1000); const ref = preimage; @@ -133,15 +127,16 @@ export default { l(user.username, "sent", type, amount); emit(user.id, "payment", p); - res.send(p); + return c.json(p); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async receive(req, res) { + async receive(c) { try { - const { id, proofs, mint, memo } = req.body; + const body = await c.req.json(); + const { id, proofs, mint, memo } = body; const { uid: ref } = await getInvoice(id); const amount = await claim( @@ -153,11 +148,11 @@ export default { await credit({ hash: id, amount, memo, ref, type }); - res.send({ id }); + return c.json({ id }); } catch (e) { console.log(e); err(e.message); - bail(res, e.message); + return bail(c, e.message); } }, }; diff --git a/routes/email.ts b/routes/email.ts index 25c90ee7..ff6ffaeb 100644 --- a/routes/email.ts +++ b/routes/email.ts @@ -6,8 +6,9 @@ import { SESClient } from "@aws-sdk/client-ses"; import { SendEmailCommand } from "@aws-sdk/client-ses"; export default { - async send({ body }, res) { + async send(c) { try { + const body = await c.req.json(); const { email, message, username, token: response } = body; const Charset = "UTF-8"; @@ -48,9 +49,9 @@ export default { }), ); - res.send({ ok: true }); + return c.json({ ok: true }); } else { - bail(res, "failed captcha"); + return bail(c, "failed captcha"); } } catch (e) { console.log(e); diff --git a/routes/info.ts b/routes/info.ts index 35d1c315..adad02c6 100644 --- a/routes/info.ts +++ b/routes/info.ts @@ -5,23 +5,15 @@ import ln from "$lib/ln"; import { getDecodedToken } from "@cashu/cashu-ts"; export default { - async health(_, res) { + async health(c) { const status = getHealthStatus(); const httpStatus = status.healthy ? 200 : 503; - res.status(httpStatus).send(status); + return c.json(status, httpStatus); }, - async balances(_, res) { + async balances(c) { let total = 0; - // for await (const k of db.scanIterator({ MATCH: "balance:*" })) { - // total += parseInt(await db.get(k)); - // } - // - // for await (const k of archive.scanIterator({ MATCH: "balance:*" })) { - // total += parseInt(await archive.get(k)); - // } - const funds = await ln.listfunds(); const lnchannel = parseInt(funds.channels.reduce((a, b) => a + b.channel_sat, 0)); const lnwallet = parseInt(funds.outputs.reduce((a, b) => a + b.value, 0)); @@ -34,6 +26,6 @@ export default { lnwallet, }; - res.send(info); + return c.json(info); }, }; diff --git a/routes/invoices.ts b/routes/invoices.ts index 7f44a28d..6fc0b67a 100644 --- a/routes/invoices.ts +++ b/routes/invoices.ts @@ -6,11 +6,9 @@ import { bail, fail, fields, getAccount, getInvoice, getUser } from "$lib/utils" import rpc from "@coinos/rpc"; export default { - async get(req, res) { + async get(c) { try { - const { - params: { id }, - } = req; + const id = c.req.param("id"); if (id === "undefined") fail("invalid id"); const invoice = await getInvoice(id); @@ -21,36 +19,37 @@ export default { invoice.items ||= []; } - if (invoice) res.send(invoice); + if (invoice) return c.json(invoice); else fail("invoice not found"); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async create(req, res) { - let { body, user } = req; + async create(c) { + const body = await c.req.json(); + let user = c.get("user"); const { invoice } = body; if (body.user) user = body.user; - if (req.user.username === user.username) invoice.own = true; + if (c.get("user")?.username === user.username) invoice.own = true; else invoice.own = false; try { const result = await generate({ invoice, user }); - res.send(result); + return c.json(result); } catch (e) { console.trace(); console.log(e); - err("problem generating invoice", req.user?.username, body.user?.username, e.message); - bail(res, e.message); + err("problem generating invoice", c.get("user")?.username, body.user?.username, e.message); + return bail(c, e.message); } }, - async update(req, res) { + async update(c) { try { - const { id } = req.params; - const { body } = req; + const id = c.req.param("id"); + const body = await c.req.json(); const { tip, webhook, secret, received } = body.invoice; if (tip < 0) fail("Invalid tip"); @@ -61,7 +60,7 @@ export default { if (typeof tip !== "undefined") invoice.tip = tip; if (webhook && secret) { - if (invoice.uid !== req.user?.id) fail("Unauthorized"); + if (invoice.uid !== c.get("user")?.id) fail("Unauthorized"); invoice.webhook = webhook; invoice.secret = secret; } @@ -70,30 +69,32 @@ export default { await s(`invoice:${id}`, invoice); - res.send(invoice); + return c.json(invoice); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async list(req, res) { - const { id } = req.user; + async list(c) { + const user = c.get("user"); + const { id } = user; let invoices = await db.lRange(`${id}:invoices`, 0, -1); invoices = (await Promise.all(invoices.map((i) => getInvoice(i)))).filter(Boolean); - res.send(invoices); + return c.json(invoices); }, - async sign(req, res) { + async sign(c) { try { - const { address, message, type = "bitcoin" } = req.body; + const body = await c.req.json(); + const { address, message, type = "bitcoin" } = body; const node = rpc(config[type]); if (config[type].walletpass) await node.walletPassphrase(config[type].walletpass, 300); const signature = await node.signMessage({ address, message }); - res.send({ signature }); + return c.json({ signature }); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, }; diff --git a/routes/items.ts b/routes/items.ts index df1d86ea..b9706b4c 100644 --- a/routes/items.ts +++ b/routes/items.ts @@ -3,10 +3,8 @@ import { v4 } from "uuid"; import { bail, fail } from "$lib/utils"; export default { - async list(req, res) { - const { - params: { id: uid }, - } = req; + async list(c) { + const uid = c.req.param("id"); const items = []; for (const id of await db.lRange(`${uid}:items`, 0, -1)) { const item = await g(`item:${id}`); @@ -14,27 +12,24 @@ export default { else await db.lRem(`${uid}:items`, 0, id); } - res.send(items); + return c.json(items); }, - async get(req, res) { - const { - params: { id }, - } = req; + async get(c) { + const id = c.req.param("id"); try { const item = await g(`item:${id}`); if (!item) fail("Item not found"); - res.send(item); + return c.json(item); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async create(req, res) { - const { - body: item, - user: { id }, - } = req; + async create(c) { + const item = await c.req.json(); + const user = c.get("user"); + const { id } = user; try { if (!item.id) { item.id = v4(); @@ -46,33 +41,33 @@ export default { await s(`item:${item.id}`, item); - res.send(item); + return c.json(item); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async del(req, res) { - const { - body: { item }, - user: { id }, - } = req; + async del(c) { + const body = await c.req.json(); + const { item } = body; + const user = c.get("user"); + const { id } = user; try { const n = await db.lRem(`${id}:items`, 0, item.id); if (n) db.del(`item:${item.id}`); else fail("item not found"); - res.send({}); + return c.json({}); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async sort(req, res) { - const { - body: { items }, - user: { id }, - } = req; + async sort(c) { + const body = await c.req.json(); + const { items } = body; + const user = c.get("user"); + const { id } = user; try { await db.del(`${id}:items`); @@ -80,9 +75,9 @@ export default { await db.rPush(`${id}:items`, item.id); } - res.send({}); + return c.json({}); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, }; diff --git a/routes/lnurl.ts b/routes/lnurl.ts index cb74e2b5..9cd6e8d5 100644 --- a/routes/lnurl.ts +++ b/routes/lnurl.ts @@ -14,10 +14,8 @@ const host = URL.split("/").at(-1); const fiveMinutes = 1000 * 60 * 5; export default { - async encode(req, res) { - const { - query: { address }, - } = req; + async encode(c) { + const address = c.req.query("address"); const [name, domain] = address.split("@"); const url = `https://${domain}/.well-known/lnurlp/${name.toLowerCase().replace(/\s/g, "")}`; @@ -27,32 +25,29 @@ export default { } catch (e) { const m = `failed to lookup lightning address ${address}`; warn(m); - return bail(res, m); + return bail(c, m); } const enc = bech32.encode("lnurl", bech32.toWords(Buffer.from(url)), 20000); - res.send(enc); + return c.json(enc); }, - async decode(req, res) { - const { - query: { text }, - } = req; + async decode(c) { + const text = c.req.query("text"); try { const url = Buffer.from(bech32.fromWords(bech32.decode(text, 20000).words)).toString(); const r = await got(url).json(); - res.send(r); + return c.json(r); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async lnurlp(req, res) { - const { - params: { username }, - query: { minSendable = 1000, maxSendable = 100000000000 }, - } = req; + async lnurlp(c) { + const username = c.req.param("username"); + const minSendable = c.req.query("minSendable") || 1000; + const maxSendable = c.req.query("maxSendable") || 100000000000; try { const user = await getUser( username.replace("lightning:", "").replace(/\s/g, "").replace("=", "").toLowerCase(), @@ -69,7 +64,7 @@ export default { const id = v4(); await s(`lnurl:${id}`, uid); - res.send({ + return c.json({ allowsNostr: true, minSendable, maxSendable, @@ -82,15 +77,15 @@ export default { } catch (e) { if (!e.message.includes("found")) warn("problem generating lnurlp request", username, e.message); - bail(res, e.message); + return bail(c, e.message); } }, - async lnurl(req, res) { - const { - params: { id }, - query: { amount, comment, nostr }, - } = req; + async lnurl(c) { + const id = c.req.param("id"); + const amount = c.req.query("amount"); + const comment = c.req.query("comment"); + const nostr = c.req.query("nostr"); try { const iid = await g(`lnurl:${id}:invoice`); const uid = await g(`lnurl:${id}`); @@ -109,7 +104,6 @@ export default { if (nostr) { try { const event = JSON.parse(decodeURIComponent(nostr)); - // TODO: validate the event await s(`zap:${id}`, event); metadata = nostr; } catch (e) { @@ -128,31 +122,30 @@ export default { user, }); - res.send({ + return c.json({ pr: invoice.text, routes: [], verify: `${URL}/api/lnurl/verify/${invoice.id}`, }); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async verify(req, res) { - const { - params: { id }, - } = req; + async verify(c) { + const id = c.req.param("id"); const inv = await getInvoice(id); - if (!inv) return res.send({ status: "ERROR", reason: "Not found" }); + if (!inv) return c.json({ status: "ERROR", reason: "Not found" }); const { hash, received, amount, preimage } = inv; const settled = received >= amount; - res.send({ pr: hash, status: "OK", settled, preimage: preimage || null }); + return c.json({ pr: hash, status: "OK", settled, preimage: preimage || null }); }, - async pay(req, res) { - const { amount, username } = req.params; + async pay(c) { + const amount = c.req.param("amount"); + const username = c.req.param("username"); try { const user = await getUser(username); @@ -206,7 +199,7 @@ export default { await s(`lnurl:${id}`, uid); if (total > 0) await s(`lnurl:${id}:invoice`, invoice.id); - res.send({ + return c.json({ allowsNostr: true, minSendable: invoice.amount ? total : 1000, maxSendable: invoice.amount ? total : 10 * 1000 * SATS, @@ -219,7 +212,7 @@ export default { } catch (e) { if (!e.message.includes("found")) warn("problem generating lnurlp request", username, e.message); - bail(res, e.message); + return bail(c, e.message); } }, }; diff --git a/routes/locations.ts b/routes/locations.ts index 23b0f3b4..6dd364ec 100644 --- a/routes/locations.ts +++ b/routes/locations.ts @@ -1,19 +1,19 @@ import { db, g } from "$lib/db"; export default { - async list(_, res) { + async list(c) { const locations = await g("locations"); - res.send({ locations }); + return c.json({ locations }); }, - async nearby(req, res) { - const lat = parseFloat(req.query.lat); - const lon = parseFloat(req.query.lon); - const radius = parseFloat(req.query.radius) || 50; - const count = parseInt(req.query.count) || 50; + async nearby(c) { + const lat = parseFloat(c.req.query("lat")); + const lon = parseFloat(c.req.query("lon")); + const radius = parseFloat(c.req.query("radius")) || 50; + const count = parseInt(c.req.query("count")) || 50; if (isNaN(lat) || isNaN(lon)) { - return res.status(400).send({ error: "lat and lon required" }); + return c.json({ error: "lat and lon required" }, 400); } try { @@ -34,11 +34,11 @@ export default { if (loc) locations.push(loc); } - res.send({ locations }); + return c.json({ locations }); } catch (e) { console.log("nearby search failed", e); const locations = await g("locations"); - res.send({ locations }); + return c.json({ locations }); } }, }; diff --git a/routes/nostr.ts b/routes/nostr.ts index 4436a1c7..554a230c 100644 --- a/routes/nostr.ts +++ b/routes/nostr.ts @@ -22,20 +22,20 @@ import type { ProfilePointer } from "nostr-tools/nip19"; import { getZapEndpoint, makeZapRequest } from "nostr-tools/nip57"; export default { - async event(req, res) { + async event(c) { try { - let { id } = req.params; - const full = req.url.endsWith("full"); + let id = c.req.param("id"); + const full = c.req.url.endsWith("full"); if (id.startsWith("nevent")) id = decode(id).data.id; if (id.startsWith("note")) id = decode(id).data; const k = `event:${id}${full ? ":full" : ""}`; let event = await g(k); - if (event) return res.send(event); + if (event) return c.json(event); event = await get({ ids: [id] }); - if (!full) return res.send(event); + if (!full) return c.json(event); const parts = parseContent(event); @@ -98,15 +98,16 @@ export default { await db.set(k, JSON.stringify(event), { EX }); - res.send(event); + return c.json(event); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async parse(req, res) { - const { event } = req.body; + async parse(c) { + const body = await c.req.json(); + const { event } = body; const parts = parseContent(event); const names = {}; @@ -117,12 +118,12 @@ export default { } } - res.send({ parts, names }); + return c.json({ parts, names }); }, - async thread(req, res) { + async thread(c) { try { - const { id } = req.params; + const id = c.req.param("id"); const event = await get({ ids: [id] }); @@ -147,16 +148,16 @@ export default { } } - res.send(thread); + return c.json(thread); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async zaps(req, res) { + async zaps(c) { try { - let { id } = req.params; + let id = c.req.param("id"); if (id.startsWith("nevent")) id = decode(id).data.id; if (id.startsWith("note")) id = decode(id).data; const filter = { kinds: [9735], "#e": [id] }; @@ -165,7 +166,7 @@ export default { await sync("wss://relay.primal.net", filter); events = await scan(filter); } - if (!events.length) return res.send([]); + if (!events.length) return c.json([]); const zaps = []; for (const { tags } of events) { @@ -187,17 +188,19 @@ export default { } } - res.send(zaps.filter((z) => z.amount > 0)); + return c.json(zaps.filter((z) => z.amount > 0)); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async publish(req, res) { + async publish(c) { try { - const { event } = req.body; - const { pubkey } = req.user; + const body = await c.req.json(); + const { event } = body; + const user = c.get("user"); + const { pubkey } = user; await publish(event); @@ -206,17 +209,15 @@ export default { db.del(`${pubkey}:follows:n`); } - res.send({}); + return c.json({}); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async events(req, res) { - const { - params: { pubkey }, - } = req; + async events(c) { + const pubkey = c.req.param("pubkey"); try { const events = await q({ kinds: [1], authors: [pubkey], limit: 20 }); @@ -233,25 +234,25 @@ export default { } } - res.send(events); + return c.json(events); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async follows(req, res) { - const { - params: { pubkey }, - query: { limit = 20, offset = 0, pubkeysOnly }, - } = req; + async follows(c) { + const pubkey = c.req.param("pubkey"); + const limit = parseInt(c.req.query("limit") || "20"); + const offset = parseInt(c.req.query("offset") || "0"); + const pubkeysOnly = c.req.query("pubkeysOnly"); try { const k = `${pubkey}:follows${pubkeysOnly ? ":pubkeys" : ""}`; let follows = await g(k); - if (follows?.length) return res.send(follows); + if (follows?.length) return c.json(follows); const event = await get({ authors: [pubkey], kinds: [3] }); - if (!event) return res.send([]); + if (!event) return c.json([]); let pubkeys = event.tags.filter((tag) => tag[0] === "p").map((tag) => tag[1]); @@ -273,24 +274,23 @@ export default { await db.set(k, JSON.stringify(follows), { EX }); - res.send(follows); + return c.json(follows); } catch (e) { console.log("follows fail", e); - bail(res, e.message); + return bail(c, e.message); } }, - async followers(req, res) { - const { - params: { pubkey }, - query: { limit = 20, offset = 0 }, - } = req; + async followers(c) { + const pubkey = c.req.param("pubkey"); + const limit = parseInt(c.req.query("limit") || "20"); + const offset = parseInt(c.req.query("offset") || "0"); try { let followers = await g(`${pubkey}:followers`); - if (followers?.length) return res.send(followers); + if (followers?.length) return c.json(followers); const events = await q({ kinds: [3], "#p": [pubkey], limit }); - if (!events.length) return res.send([]); + if (!events.length) return c.json([]); const pubkeys = events.map((e) => e.pubkey).slice(offset, offset + limit); followers = ( @@ -306,28 +306,25 @@ export default { await db.set(`${pubkey}:followers`, JSON.stringify(followers), { EX }); - res.send(followers); + return c.json(followers); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async count(req, res) { + async count(c) { try { - const { pubkey } = req.params; - // res.send({ followers: 0, follows: 0 }); - res.send(await getCount(pubkey)); + const pubkey = c.req.param("pubkey"); + return c.json(await getCount(pubkey)); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async identities(req, res) { - const { - query: { name }, - } = req; + async identities(c) { + const name = c.req.query("name"); let names = {}; if (name) { names = { [name]: (await getUser(name, fields)).pubkey }; @@ -339,25 +336,26 @@ export default { } } - res.send({ names }); + return c.json({ names }); }, - async info(_, res) { - res.send({ pubkey: serverPubkey }); + async info(c) { + return c.json({ pubkey: serverPubkey }); }, - async profile(req, res) { - const { profile } = req.params; + async profile(c) { + const profile = c.req.param("profile"); const { data } = decode(profile); const { pubkey, relays } = data as ProfilePointer; const recipient = await getProfile(pubkey, relays); recipient.relays = relays; - res.send(recipient); + return c.json(recipient); }, - async zapRequest(req, res) { + async zapRequest(c) { try { - const { amount, id } = req.body; + const body = await c.req.json(); + const { amount, id } = body; const { pubkey } = await get({ ids: [id] }); const event = await makeZapRequest({ profile: pubkey, @@ -367,15 +365,16 @@ export default { comment: "", }); - res.send(event); + return c.json(event); } catch (e) { console.log(e); } }, - async zap(req, res) { + async zap(c) { try { - const { event } = req.body; + const body = await c.req.json(); + const { event } = body; const amount = event.tags.find((t) => t[0] === "amount")[1]; const pubkey = event.tags.find((t) => t[0] === "p")[1]; const content = JSON.stringify(await getProfile(pubkey)); @@ -386,10 +385,10 @@ export default { const url = `${callback}?amount=${amount}&nostr=${encodedEvent}`; const json = await got(url).json(); - res.send(json); + return c.json(json); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, }; diff --git a/routes/payments.ts b/routes/payments.ts index 33bf3381..ab7a6711 100644 --- a/routes/payments.ts +++ b/routes/payments.ts @@ -41,12 +41,13 @@ import got from "got"; import { v4 } from "uuid"; export default { - async info(_, res) { - res.send(await ln.getinfo()); + async info(c) { + return c.json(await ln.getinfo()); }, - async create(req, res) { - const { body, user } = req; + async create(c) { + const body = await c.req.json(); + const user = c.get("user"); let { amount, hash, fee, fund, memo, payreq, aid } = body; const balance = await getBalance(user.id); @@ -93,7 +94,6 @@ export default { await s(`payment:${p.id}`, p); await s(`payment:${txid}`, p.id); - // Instant vault notification try { const vaultInfo = await g(`arkaddr:${invoice.text}`); if (vaultInfo) { @@ -125,7 +125,6 @@ export default { throw e; } } else { - // Check if recipient account is non-custodial (vault) const recipientAccount = invoice?.aid ? await g(`account:${invoice.aid}`) : null; if (recipientAccount?.pubkey || recipientAccount?.seed) { p = await sendOnchain({ @@ -157,19 +156,24 @@ export default { } } - res.send(p); + return c.json(p); } catch (e) { warn(user.username, "payment failed", amount, balance, hash, payreq); err(e.message); - bail(res, e.message); + return bail(c, e.message); } }, - async list(req, res) { - let { - user: { id }, - query: { aid, start, end, limit, offset, received }, - } = req; + async list(c) { + const user = c.get("user"); + let { id } = user; + let aid = c.req.query("aid"); + const start = c.req.query("start"); + const end = c.req.query("end"); + let limit = c.req.query("limit"); + let offset = c.req.query("offset"); + const received = c.req.query("received"); + if (!aid || aid === "undefined") aid = id; const index = await db.lPos(`${id}:accounts`, aid); @@ -244,30 +248,27 @@ export default { const { length: count } = payments; if (limit) payments = payments.slice(offset, offset + limit); - res.send({ payments, count, incoming, outgoing }); + return c.json({ payments, count, incoming, outgoing }); }, - async get(req, res) { + async get(c) { try { - const { - params: { hash }, - } = req; + const hash = c.req.param("hash"); const p = await getPayment(hash); if (p?.type === PaymentType.internal) p.with = await getUser(p.ref, fields); if (p?.type === PaymentType.fund) p.with = await getUser(p.uid, fields); - res.send(p); + return c.json(p); } catch (e) { console.log(e); err("failed to get payment", e.message); - bail(res, e.message); + return bail(c, e.message); } }, - async parse(req, res) { - const { - body: { payreq }, - user, - } = req; + async parse(c) { + const body = await c.req.json(); + const { payreq } = body; + const user = c.get("user"); try { const hour = 1000 * 60 * 60; let nodes = await g("nodes"); @@ -297,20 +298,18 @@ export default { const covered = Math.min(creditBal, ourfee) || 0; ourfee -= covered; - res.send({ alias, amount, ourfee }); + return c.json({ alias, amount, ourfee }); } catch (e) { console.log(e); err("problem parsing", e.message); - bail(res, e.message); + return bail(c, e.message); } }, - async fund(req, res) { - const { - params: { id }, - } = req; + async fund(c) { + const id = c.req.param("id"); const amount = await getFundBalance(id); - if (amount === null) return bail(res, "fund not found"); + if (amount === null) return bail(c, "fund not found"); let payments = (await db.lRange(`fund:${id}:payments`, 0, -1)) || []; payments = await Promise.all(payments.map((hash) => gf(`payment:${hash}`))); @@ -319,30 +318,14 @@ export default { payments = payments.filter((p) => p); const authorization = await g(`authorization:${id}`); - res.send({ amount, authorization: authorization?.amount, payments }); + return c.json({ amount, authorization: authorization?.amount, payments }); }, - // async withdraw(req, res) { - // const { - // params: { name }, - // } = req; - // const { user } = req; - // const balance = await g(`fund:${name}`); - // const managers = await db.sMembers(`fund:${name}:managers`); - // if (managers.length && !managers.includes(user.id)) fail("Unauthorized"); - // res.send({ - // tag: "withdrawRequest", - // callback: `${URL}/api/lnurlw`, - // k1: name, - // defaultDescription: `Withdraw from coinos fund ${name}`, - // minWithdrawable: balance > 0 ? 1000 : 0, - // maxWithdrawable: balance * 1000, - // }); - // }, - - async authorize(req, res) { - const { id: uid } = req.user; - const { id, fiat, currency, amount } = req.body; + async authorize(c) { + const user = c.get("user"); + const { id: uid } = user; + const body = await c.req.json(); + const { id, fiat, currency, amount } = body; const managers = await db.sMembers(`fund:${id}:managers`); if (managers.length && !managers.includes(uid)) fail("Unauthorized"); @@ -355,14 +338,13 @@ export default { }; await s(`authorization:${id}`, authorization); - res.send({}); + return c.json({}); }, - async take(req, res) { - let { - body: { id, amount, invoice: iid }, - user, - } = req; + async take(c) { + const body = await c.req.json(); + const user = c.get("user"); + let { id, amount, invoice: iid } = body; try { amount = Number.parseInt(amount); if (amount <= 0) fail("Invalid amount"); @@ -421,15 +403,15 @@ export default { await db.lPush(`fund:${id}:payments`, payment.id); - res.send(payment); + return c.json(payment); } catch (e) { warn("problem withdrawing from fund", user.username, e.message); - bail(res, e.message); + return bail(c, e.message); } }, - async managers(req, res) { - const { name } = req.params; + async managers(c) { + const name = c.req.param("name"); const ids = await db.sMembers(`fund:${name}:managers`); @@ -437,12 +419,13 @@ export default { Boolean, ); - res.send(managers); + return c.json(managers); }, - async addManager(req, res) { - const { id, username } = req.body; - const { user } = req; + async addManager(c) { + const body = await c.req.json(); + const { id, username } = body; + const user = c.get("user"); const k = `fund:${id}:managers`; @@ -463,14 +446,15 @@ export default { if (!managers.length) managers = await Promise.all(ids.map(async (id) => await getUser(id, fields))); - res.send(managers); + return c.json(managers); }, - async deleteManager(req, res) { + async deleteManager(c) { try { - const { name } = req.params; - const { id: uid } = req.body; - const { user } = req; + const name = c.req.param("name"); + const body = await c.req.json(); + const { id: uid } = body; + const user = c.get("user"); const k = `fund:${name}:managers`; let managers = await db.sMembers(k); @@ -484,16 +468,15 @@ export default { const ids = await db.sMembers(k); managers = await Promise.all(ids.map(async (id) => await getUser(id, fields))); - res.send(managers); + return c.json(managers); } catch (e) {} }, - async confirm(req, res) { - const { - body: { txid, wallet, type }, - } = req; + async confirm(c) { + const body = await c.req.json(); + const { txid, wallet, type } = body; - if (type !== PaymentType.liquid) return res.send({}); + if (type !== PaymentType.liquid) return c.json({}); try { const node = rpc({ ...config[type], wallet }); @@ -546,17 +529,18 @@ export default { await completePayment(invoice, p, user); } } - res.send({}); + return c.json({}); } catch (e) { console.log(e); warn(`problem processing ${txid}`); - bail(res, e.message); + return bail(c, e.message); } }, - async txWebhook(req, res) { - const { txid, secret } = req.body || {}; - const headerSecret = req.headers["x-hook-secret"]; + async txWebhook(c) { + const body = await c.req.json(); + const { txid, secret } = body || {}; + const headerSecret = c.req.header("x-hook-secret"); const hookSecret = secret || headerSecret; try { @@ -566,59 +550,59 @@ export default { const tx = await getTx(txid); await processWatchedTx(tx); - res.send({}); + return c.json({}); } catch (e) { warn("problem processing tx webhook", e.message); - bail(res, e.message); + return bail(c, e.message); } }, - async fee(req, res) { - const { body, user } = req; + async fee(c) { + const body = await c.req.json(); + const user = c.get("user"); try { - res.send(await build({ ...body, user })); + return c.json(await build({ ...body, user })); } catch (e) { warn("problem estimating fee", e.message, user.username, body.amount, body.address); let msg = e.message; if (msg.includes("500")) msg = ""; - bail(res, `Failed to prepare transaction ${msg}`); + return bail(c, `Failed to prepare transaction ${msg}`); } }, - async send(req, res) { - const { body, user } = req; + async send(c) { + const body = await c.req.json(); + const user = c.get("user"); try { await requirePin({ body, user }); const { hash: txid } = await sendOnchain({ ...body, user }); const pid = await g(`payment:${txid}`); const p = await g(`payment:${pid}`); - res.send(p); + return c.json(p); } catch (e) { warn(user.username, "payment failed", e.message); - res.code(500).send(e.message); + return c.json(e.message, 500); } }, - async freeze(req, res) { - const { - body: { secret }, - } = req; + async freeze(c) { + const body = await c.req.json(); + const { secret } = body; try { if (secret !== config.adminpass) fail("unauthorized"); await s("freeze", true); - res.send("ok"); + return c.json("ok"); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async print(req, res) { - const { - body: { id }, - user, - } = req; + async print(c) { + const body = await c.req.json(); + const { id } = body; + const user = c.get("user"); try { const p = await gf(`payment:${id}`); if (!p) fail("Payment not found"); @@ -629,18 +613,17 @@ export default { mqtt.publish(username, `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`); - res.send({ ok: true }); + return c.json({ ok: true }); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async lnaddress(req, res) { - let { - params: { lnaddress, amount }, - body, - user, - } = req; + async lnaddress(c) { + const body = await c.req.json(); + const user = c.get("user"); + let lnaddress = c.req.param("lnaddress"); + let amount = c.req.param("amount"); const { fee } = body; try { lnaddress = decodeURIComponent(lnaddress); @@ -667,27 +650,25 @@ export default { await credit({ hash: pr, amount, memo, ref: user.id }); } else p = await sendLightning({ user, pr, amount, fee, memo }); - res.send(p); + return c.json(p); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async gateway(req, res) { - const { - body: { short_channel_id, webhook }, - } = req; + async gateway(c) { + const body = await c.req.json(); + const { short_channel_id, webhook } = body; await s(short_channel_id, webhook); - res.send({ ok: true }); + return c.json({ ok: true }); }, - async replace(req, res) { - const { - body: { id }, - user, - } = req; + async replace(c) { + const body = await c.req.json(); + const { id } = body; + const user = c.get("user"); try { const p = await gf(`payment:${id}`); if (!p) fail("Payment not found"); @@ -724,47 +705,50 @@ export default { if (!r[0].allowed) fail(`transaction rejected ${p.hex}`); warn("bump", user.username, p.hex); - res.send({ ok: true }); + return c.json({ ok: true }); } catch (e) { err("failed to bump payment", id, e.message); - bail(res, e.message); + return bail(c, e.message); } }, - async internal(req, res) { - const { - body: { username, amount }, - user: sender, - } = req; + async internal(c) { + const body = await c.req.json(); + const { username, amount } = body; + const sender = c.get("user"); const recipient = await getUser(username); - res.send(await sendInternal({ amount, sender, recipient })); + return c.json(await sendInternal({ amount, sender, recipient })); }, - async decode(req, res) { - const { bolt11 } = req.params; - res.send(await ln.decode(bolt11)); + async decode(c) { + const bolt11 = c.req.param("bolt11"); + return c.json(await ln.decode(bolt11)); }, - async fetchinvoice(req, res) { - const { amount, offer } = req.body; - res.send(await ln.fetchinvoice(offer, amount ? amount * 1000 : null)); + async fetchinvoice(c) { + const body = await c.req.json(); + const { amount, offer } = body; + return c.json(await ln.fetchinvoice(offer, amount ? amount * 1000 : null)); }, - async auth(req, res) { - console.log(req.query); - res.send(req.query); + async auth(c) { + const query = c.req.query(); + console.log(query); + return c.json(query); }, - async order(req, res) { - console.log(req.body); - res.send(req.body); + async order(c) { + const body = await c.req.json(); + console.log(body); + return c.json(body); }, - async sendinvoice(req, res) { + async sendinvoice(c) { try { - const { user } = req; - const { invreq } = req.body; + const user = c.get("user"); + const body = await c.req.json(); + const { invreq } = body; const { amount_msat, bolt12, pay_index } = await ln.sendinvoice({ invreq, @@ -782,14 +766,15 @@ export default { const p = await replay(pay_index); - res.send(p); + return c.json(p); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async ark(req, res) { - const { body, user } = req; + async ark(c) { + const body = await c.req.json(); + const user = c.get("user"); const { address, amount, aid } = body; try { @@ -812,16 +797,11 @@ export default { await s(`payment:${p.id}`, p); await s(`payment:${txid}`, p.id); - // Instant vault notification: if destination is a vault on our platform, - // create the receive payment immediately so the client doesn't have to - // wait for ASP indexer + arkSync backoff delays try { const vaultInfo = await g(`arkaddr:${address}`); if (vaultInfo) { const { aid: vaultAid, uid: vaultUid } = vaultInfo; - // Skip notification if this is a forward (e.g. vault → lightning); - // the funds are transient and will leave again immediately const isForward = await g(`custodial-ark-invoice:${address}`); const vaultOwner = await g(`user:${vaultUid}`); @@ -848,16 +828,17 @@ export default { throw e; } - res.send(p); + return c.json(p); } catch (e) { warn(user.username, "ark payment failed", e.message); - res.code(500).send(e.message); + return c.json(e.message, 500); } }, - async arkVaultSend(req, res) { + async arkVaultSend(c) { try { - const { body, user } = req; + const body = await c.req.json(); + const user = c.get("user"); const { hash, amount, aid } = body; const { rate, currency } = await getUserRate(user); @@ -871,15 +852,16 @@ export default { mapHashToId: true, }); - res.send(p); + return c.json(p); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async arkVaultReceive(req, res) { + async arkVaultReceive(c) { try { - const { body, user } = req; + const body = await c.req.json(); + const user = c.get("user"); const { amount, hash, aid } = body; if (amount <= 0) fail("Invalid amount"); @@ -895,19 +877,20 @@ export default { currency, }); - res.send(p); + return c.json(p); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async arkAddress(_, res) { - res.send(await getArkAddress()); + async arkAddress(c) { + return c.json(await getArkAddress()); }, - async arkReceive(req, res) { + async arkReceive(c) { try { - const { body, user } = req; + const body = await c.req.json(); + const user = c.get("user"); const { iid, amount, hash } = body; const { id: uid } = user; @@ -921,7 +904,6 @@ export default { if (hash) { await acquireArkLock(hash); - // Skip VTXO verification for vault→custodial (same user, debit handled by arkVaultSend) if (!(aid && aid !== uid)) { if (!(await verifyArkVtxo(hash))) fail("VTXO not found in server wallet"); } @@ -931,7 +913,6 @@ export default { const { rates } = await getUserRate(user); const rate = rates[currency]; - // Always credit custodial balance (Ark funds received by server) await tbCredit(uid, uid, type, amount, false); const p = await createArkPayment({ @@ -951,26 +932,25 @@ export default { await completePayment(invoice, p, user); - // Clean up custodial ark invoice index if (invoice.text) await db.del(`custodial-ark-invoice:${invoice.text}`); - res.send(p); + return c.json(p); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async arkSync(req, res) { + async arkSync(c) { try { - const { user } = req; - const { transactions = [], aid } = req.body; + const user = c.get("user"); + const body = await c.req.json(); + const { transactions = [], aid } = body; const { id: uid } = user; - l("arkSync", user.username, aid, "txs:", transactions.length, "bal:", req.body.balance); + l("arkSync", user.username, aid, "txs:", transactions.length, "bal:", body.balance); - // Per-account lock prevents concurrent syncs from racing const lockKey = `arksynclock:${aid}`; const gotLock = await db.set(lockKey, "1", { NX: true, EX: 30 }); - if (!gotLock) return res.send({ synced: 0, received: 0, payments: [] }); + if (!gotLock) return c.json({ synced: 0, received: 0, payments: [] }); try { const { rate, currency } = await getUserRate(user); @@ -983,7 +963,6 @@ export default { const hashes = [tx.arkTxid, tx.commitmentTxid, tx.hash].filter(Boolean); if (!hashes.length) continue; - // Check if any hash is already known let found = false; for (const h of hashes) { const existingId = await g(`payment:${aid}:${h}`); @@ -999,8 +978,6 @@ export default { } if (found) continue; - // Skip sent/zero transactions not recorded via vault-send — these are - // from internal wallet operations (settle, refresh) not real sends if (tx.amount <= 0) continue; const primaryHash = hashes[0]; @@ -1021,7 +998,6 @@ export default { if (tx.amount > 0) { received += tx.amount; - // Find an unpaid ark invoice for this account and mark it received const invoiceIds = await db.lRange(`${aid}:invoices`, 0, 20); for (const iid of invoiceIds) { const inv = await getInvoice(iid); @@ -1038,8 +1014,7 @@ export default { } } - // Reconcile expired VTXOs - const { balance } = req.body; + const { balance } = body; if (typeof balance === "number" && balance >= 0) { const paymentIds = await db.lRange(`${aid}:payments`, 0, -1); let expectedBalance = 0; @@ -1049,8 +1024,6 @@ export default { const pay = await g(`payment:${pid}`); if (pay) { expectedBalance += pay.amount; - // Skip reconciliation if there are recent payments the SDK - // may not have indexed yet (e.g. instant vault credits) if (pay.amount > 0 && now - pay.created < 120_000) { hasRecentPayments = true; } @@ -1075,7 +1048,6 @@ export default { } } - // Check for pending custodial ark invoice to forward let forward; if (received > 0) { const account = await g(`account:${aid}`); @@ -1098,12 +1070,12 @@ export default { } } - res.send({ synced, received, payments, forward }); + return c.json({ synced, received, payments, forward }); } finally { await db.del(lockKey); } } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, }; diff --git a/routes/rates.ts b/routes/rates.ts index e532ec78..d4426a6e 100644 --- a/routes/rates.ts +++ b/routes/rates.ts @@ -2,17 +2,17 @@ import { g } from "$lib/db"; import { rate } from "$lib/rates"; export default { - async fx(_, res) { + async fx(c) { const { fx } = await g("fx"); - res.send({ fx }); + return c.json({ fx }); }, - async last(_, res) { - res.send(rate || (await g("rate"))); + async last(c) { + return c.json(rate || (await g("rate"))); }, - async index(_, res) { + async index(c) { const { date, fx, ...rates } = await g("rates"); - res.send(rates); + return c.json(rates); }, }; diff --git a/routes/shopify.ts b/routes/shopify.ts index d955d73d..abd7c81b 100644 --- a/routes/shopify.ts +++ b/routes/shopify.ts @@ -3,21 +3,20 @@ import { g } from "$lib/db"; import { l, err } from "$lib/logging"; import { getPayment } from "$lib/utils"; -const query = `mutation orderMarkAsPaid($input: OrderMarkAsPaidInput!) { - orderMarkAsPaid(input: $input) { - order { id } - userErrors { +const query = `mutation orderMarkAsPaid($input: OrderMarkAsPaidInput!) { + orderMarkAsPaid(input: $input) { + order { id } + userErrors { field message } } }`; -export default async (req, res) => { - const { - body: { hash }, - params: { id }, - } = req; +export default async (c) => { + const body = await c.req.json(); + const { hash } = body; + const id = c.req.param("id"); const p = await getPayment(hash); const user = await g(`user:${p.uid}`); @@ -39,7 +38,7 @@ export default async (req, res) => { l("shopify success", r); - res.send(r); + return c.json(r); } catch (e) { err("problem marking shopify order as paid", e.message); } diff --git a/routes/square.ts b/routes/square.ts index f738e1ef..269cdd0d 100644 --- a/routes/square.ts +++ b/routes/square.ts @@ -12,11 +12,11 @@ import { v4 } from "uuid"; const { scopes, url, appId, clientSecret, environment } = config.square; export default { - async connect(req, res) { - const { user } = req; + async connect(c) { + const user = c.get("user"); if (await db.exists(`${user.id}:square`)) { - return res.send("connected"); + return c.json("connected"); } const codeVerifier = base64.encode(crypto.randomBytes(32)); @@ -27,13 +27,13 @@ export default { const state = base64.encode(crypto.randomBytes(12)); const scope = scopes.join("+"); - res.send( + return c.json( `${url}oauth2/authorize?client_id=${appId}&session=false&scope=${scope}&state=${state}&code_challenge=${challenge}`, ); }, - async auth(req, res) { - const { user } = req; + async auth(c) { + const user = c.get("user"); const codeVerifier = await g(`${user.id}:codeVerifier`); @@ -41,7 +41,7 @@ export default { environment, }); - const { code } = req.query; + const code = c.req.query("code"); try { const result = await client.oAuth.obtainToken({ @@ -53,15 +53,15 @@ export default { await s(`${user.id}:square`, result); await s(result.merchantId, user.id); - res.send({}); + return c.json({}); } catch (e) { console.log(e); } }, - async payment(req, res) { + async payment(c) { try { - const { body } = req; + const body = await c.req.json(); const { data, type, merchant_id } = body; const { payment } = data.object; @@ -86,9 +86,9 @@ export default { await generate({ invoice, user }); } - res.send({}); + return c.json({}); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, }; diff --git a/routes/users.ts b/routes/users.ts index 07e4fb85..f3b747af 100644 --- a/routes/users.ts +++ b/routes/users.ts @@ -27,6 +27,7 @@ import { getPublicKey, nip19, verifyEvent } from "nostr-tools"; import { hexToBytes } from "@noble/hashes/utils.js"; import { authenticator } from "otplib"; import { v4 } from "uuid"; +import { setCookie } from "hono/cookie"; import { PaymentType } from "$lib/types"; import { createBalanceAccount, getBalance, getCredit, getPending } from "$lib/tb"; @@ -36,16 +37,14 @@ import type { ProfilePointer } from "nostr-tools/nip19"; const { host } = new URL(process.env.URL); const relay = encodeURIComponent(config.publicRelay); -const verifyRecaptcha = async (response, req?) => { +const verifyRecaptcha = async (response, c?, body?) => { const { recaptcha: secret } = config; if (!secret) return true; - // Skip captcha for Tor users accessing via .onion - const host = req?.headers?.["host"] || ""; - if (host.endsWith(".onion")) return true; + const reqHost = c?.req?.header("host") || ""; + if (reqHost.endsWith(".onion")) return true; - // Skip captcha for allowlisted User-Agents (prefix match) - const uaRaw = req?.headers?.["user-agent"] || req?.headers?.["User-Agent"]; + const uaRaw = c?.req?.header("user-agent"); const ua = Array.isArray(uaRaw) ? uaRaw[0] : uaRaw; if (typeof ua === "string") { const prefixes = await db.sMembers("nocaptcha_ua"); @@ -55,20 +54,19 @@ const verifyRecaptcha = async (response, req?) => { } } - // Check for valid API key to bypass captcha - const apiKey = req?.headers?.["x-api-key"]; + const apiKey = c?.req?.header("x-api-key"); if (apiKey && (await db.sIsMember("apikeys", apiKey))) { return true; } - const { username } = req.body; + const { username } = body || {}; if (username && (await db.sIsMember("nocaptcha", username.toLowerCase().replace(/\s/g, "")))) return true; if (!response) return false; try { - const ip = req?.headers?.["cf-connecting-ip"] || req?.socket?.remoteAddress; + const ip = c?.req?.header("cf-connecting-ip") || c?.env?.ip; const { success } = await got .post("https://www.google.com/recaptcha/api/siteverify", { form: { @@ -87,8 +85,8 @@ const verifyRecaptcha = async (response, req?) => { export default { upload, - async me(req, res) { - const { user } = req; + async me(c) { + const user = c.get("user"); try { user.balance = await getBalance(user.id); user.locked = await ga(`balance:${user.id}`); @@ -104,15 +102,15 @@ export default { user.prompt = !!user.prompt; if (user.pubkey) user.npub = nip19.npubEncode(user.pubkey); - res.send(pick(user, whitelist)); + return c.json(pick(user, whitelist)); } catch (e) { console.log("problem fetching user", e); - res.code(500).send(e.message); + return c.json(e.message, 500); } }, - async list(req, res) { - const { user } = req; + async list(c) { + const user = c.get("user"); if (!user.admin) fail("unauthorized"); const users = []; @@ -140,13 +138,11 @@ export default { users.push(u); } - res.send(users); + return c.json(users); }, - async get(req, res) { - let { - params: { key }, - } = req; + async get(c) { + let key = c.req.param("key"); key = key.toLowerCase().replace(/\s/g, ""); try { if (key.startsWith("npub")) { @@ -162,43 +158,43 @@ export default { } const user = await getNostrUser(key); - res.send(pick(user, fields)); + return c.json(pick(user, fields)); } catch (e) { err("problem getting user", key, e.message); - res.code(500).send(e.message); + return c.json(e.message, 500); } }, - async create(req, res) { - const { body, headers } = req; + async create(c) { + const body = await c.req.json(); + const headers = c.req.header(); try { const ip = headers["cf-connecting-ip"]; if (!body.user) fail("no user object provided"); let { user } = body; - const fields = ["pubkey", "password", "username", "picture", "fresh", "authPubkey"]; - user = await register(pick(user, fields), ip); + const flds = ["pubkey", "password", "username", "picture", "fresh", "authPubkey"]; + user = await register(pick(user, flds), ip); const payload = { id: user.id }; const token = jwt.sign(payload, config.jwt); l("registered new user", user.username); - res.send({ ...pick(user, whitelist), sk: user.sk, token }); + return c.json({ ...pick(user, whitelist), sk: user.sk, token }); } catch (e) { err("problem registering", e.message); - res.code(500).send(e.message); + return c.json(e.message, 500); } }, - async disable2fa(req, res) { - const { - user, - body: { token }, - } = req; + async disable2fa(c) { + const user = c.get("user"); + const body = await c.req.json(); + const { token } = body; const { id, twofa, username, otpsecret } = user; if (twofa && !authenticator.check(token, otpsecret)) { - return res.code(401).send("2fa required"); + return c.json("2fa required", 401); } user.twofa = false; @@ -206,15 +202,14 @@ export default { emit(username, "user", user); emit(username, "otpsecret", user.otpsecret); l("disabled 2fa", username); - res.send({}); + return c.json({}); }, - async enable2fa(req, res) { + async enable2fa(c) { try { - const { - user, - body: { token }, - } = req; + const user = c.get("user"); + const body = await c.req.json(); + const { token } = body; const { id, otpsecret, username } = user; const isValid = authenticator.check(token, otpsecret); if (isValid) { @@ -222,21 +217,22 @@ export default { await s(`user:${id}`, user); emit(username, "user", user); } else { - return res.code(500).send("Invalid token"); + return c.json("Invalid token", 500); } l("enabled 2fa", username); - res.send({}); + return c.json({}); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async update(req, res) { - const { user, body } = req; + async update(c) { + const user = c.get("user"); + const body = await c.req.json(); try { - const { id: tokid } = jwt.verify(req.headers.authorization.split(" ")[1], config.jwt); + const { id: tokid } = jwt.verify(c.req.header("authorization").split(" ")[1], config.jwt); l("updating user", user.username, tokid); if (user.id !== tokid) fail("unauthorized"); @@ -265,8 +261,8 @@ export default { const event = JSON.parse(body.event); const challenge = event.tags.find((t) => t[0] === "challenge")[1]; - const c = await g(`challenge:${challenge}`); - if (!c) fail("Invalid or expired challenge"); + const ch = await g(`challenge:${challenge}`); + if (!ch) fail("Invalid or expired challenge"); if (!verifyEvent(event) || event.pubkey !== pubkey) fail("Invalid signature or challenge mismatch."); @@ -361,27 +357,28 @@ export default { if (user.nip5) await db.sAdd("nip5", `${user.username}:${user.pubkey}`); emit(user.id, "user", user); - res.send({ user }); + return c.json({ user }); } catch (e) { console.log(e); warn("failed to update", user.username, e.message); - bail(res, e.message); + return bail(c, e.message); } }, - async login(req, res) { + async login(c) { try { - let { username, password, token: twofa, recaptcha } = req.body; - const ip = req.headers["cf-connecting-ip"] || req.socket.remoteAddress; + const body = await c.req.json(); + let { username, password, token: twofa, recaptcha } = body; + const ip = c.req.header("cf-connecting-ip") || c.env?.ip || "unknown"; const ipKey = `ip:${ip}:login`; const ipCount = await db.incr(ipKey); if (ipCount === 1) await db.expire(ipKey, 10); - if (ipCount > 30) return res.code(429).send({}); + if (ipCount > 30) return c.json({}, 429); - const recaptchaOk = await verifyRecaptcha(recaptcha, req); + const recaptchaOk = await verifyRecaptcha(recaptcha, c, body); if (!recaptchaOk) { - return res.code(401).send("failed captcha"); + return c.json("failed captcha", 401); } username = username.toLowerCase().replace(/\s/g, ""); @@ -391,7 +388,7 @@ export default { const failures = await g(fk); const ipFailKey = `ip:${ip}:login:fail`; const ipFailures = await g(ipFailKey); - if (ipFailures > 20) return res.code(429).send({}); + if (ipFailures > 20) return c.json({}, 429); let user = await getUser(username); @@ -401,7 +398,7 @@ export default { if (!(await db.ttl(ipFailKey))) await db.expire(ipFailKey, 600); await db.incrBy(fk, 1); setTimeout(() => db.decrBy(fk, 1), 120000); - return res.code(401).send({}); + return c.json({}, 401); } if ( @@ -410,56 +407,57 @@ export default { ) { await db.incrBy(ipFailKey, 1); if (!(await db.ttl(ipFailKey))) await db.expire(ipFailKey, 600); - return res.code(401).send("2fa required"); + return c.json("2fa required", 401); } } - if (req.body.authPubkey && !user.authPubkey) { - user.authPubkey = req.body.authPubkey; + if (body.authPubkey && !user.authPubkey) { + user.authPubkey = body.authPubkey; await s(`user:${user.id}`, user); } - if (username !== "coinos") l("logged in", username, req.headers["cf-connecting-ip"]); + if (username !== "coinos") l("logged in", username, c.req.header("cf-connecting-ip")); const payload = { id: user.id }; const token = jwt.sign(payload, config.jwt); - res.cookie("token", token, { expires: new Date(Date.now() + 432000000) }); + setCookie(c, "token", token, { expires: new Date(Date.now() + 432000000), path: "/" }); user = pick(user, whitelist); - res.send({ user, token }); + return c.json({ user, token }); } catch (e) { console.log(e); - err("login error", e.message, req.socket.remoteAddress); - res.code(401).send({}); + err("login error", e.message, c.env?.ip); + return c.json({}, 401); } }, - async challenge(req, res) { + async challenge(c) { const id = v4(); await db.set(`challenge:${id}`, id, { EX: 300 }); - const username = req.query?.username; + const username = c.req.query("username"); let hasAuthKey = false; if (username) { const user = await getUser(username.toLowerCase().replace(/\s/g, "")); if (user?.authPubkey) hasAuthKey = true; } - res.send({ challenge: id, hasAuthKey }); + return c.json({ challenge: id, hasAuthKey }); }, - async authKeyLogin(req, res) { + async authKeyLogin(c) { try { - const { event, challenge, username: rawUsername, twofa, recaptcha } = req.body; - const ip = req.headers["cf-connecting-ip"] || req.socket.remoteAddress; + const body = await c.req.json(); + const { event, challenge, username: rawUsername, twofa, recaptcha } = body; + const ip = c.req.header("cf-connecting-ip") || c.env?.ip || "unknown"; const ipKey = `ip:${ip}:login`; const ipCount = await db.incr(ipKey); if (ipCount === 1) await db.expire(ipKey, 10); - if (ipCount > 30) return res.code(429).send({}); + if (ipCount > 30) return c.json({}, 429); - const recaptchaOk = await verifyRecaptcha(recaptcha, req); - if (!recaptchaOk) return res.code(401).send("failed captcha"); + const recaptchaOk = await verifyRecaptcha(recaptcha, c, body); + if (!recaptchaOk) return c.json("failed captcha", 401); - const c = await g(`challenge:${challenge}`); - if (!c) fail("Invalid or expired challenge"); + const ch = await g(`challenge:${challenge}`); + if (!ch) fail("Invalid or expired challenge"); const { kind } = event; if (kind !== 27235) fail("Invalid event"); @@ -477,35 +475,36 @@ export default { user.twofa && (typeof twofa === "undefined" || !authenticator.check(twofa, user.otpsecret)) ) { - return res.code(401).send("2fa required"); + return c.json("2fa required", 401); } l("authkey login", username, ip); const payload = { id: user.id }; const token = jwt.sign(payload, config.jwt); - res.cookie("token", token, { expires: new Date(Date.now() + 432000000) }); + setCookie(c, "token", token, { expires: new Date(Date.now() + 432000000), path: "/" }); user = pick(user, whitelist); - res.send({ user, token }); + return c.json({ user, token }); } catch (e) { console.log(e); - err("authkey login error", e.message, req.socket.remoteAddress); - res.code(401).send({}); + err("authkey login error", e.message, c.env?.ip); + return c.json({}, 401); } }, - async nostrAuth(req, res) { + async nostrAuth(c) { try { - const { event, challenge, twofa, recaptcha } = req.body; - const ip = req.headers["cf-connecting-ip"]; - const recaptchaOk = await verifyRecaptcha(recaptcha, req); + const body = await c.req.json(); + const { event, challenge, twofa, recaptcha } = body; + const ip = c.req.header("cf-connecting-ip"); + const recaptchaOk = await verifyRecaptcha(recaptcha, c, body); if (!recaptchaOk) { - return res.code(401).send("failed captcha"); + return c.json("failed captcha", 401); } - const c = await g(`challenge:${challenge}`); + const ch = await g(`challenge:${challenge}`); const { pubkey: key, kind } = event; if (kind !== 27235) fail("Invalid event"); - if (!c) fail("Invalid or expired login challenge"); + if (!ch) fail("Invalid or expired login challenge"); if (!verifyEvent(event) || event.tags.find((t) => t[0] === "challenge")?.[1] !== challenge) fail("Invalid signature or challenge mismatch."); @@ -536,86 +535,89 @@ export default { const payload = { id: user.id }; const token = jwt.sign(payload, config.jwt); - res.cookie("token", token, { expires: new Date(Date.now() + 432000000) }); + setCookie(c, "token", token, { expires: new Date(Date.now() + 432000000), path: "/" }); user = pick(user, whitelist); - res.send({ user, token }); + return c.json({ user, token }); } catch (e) { console.log(e); - err("nostr login error", e.message, req.socket.remoteAddress); - res.code(401).send({}); + err("nostr login error", e.message, c.env?.ip); + return c.json({}, 401); } }, - async subscriptions(req, res) { + async subscriptions(c) { try { - const { user } = req; + const user = c.get("user"); const subscriptions = await db.sMembers(`${user.id}:subscriptions`); - res.send(subscriptions); + return c.json(subscriptions); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async subscription(req, res) { + async subscription(c) { try { - const { subscription } = req.body; - const { id } = req.user; + const body = await c.req.json(); + const { subscription } = body; + const { id } = c.get("user"); await db.sAdd(`${id}:subscriptions`, JSON.stringify(subscription)); - res.send(subscription); + return c.json(subscription); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async deleteSubscription(req, res) { + async deleteSubscription(c) { try { - const { subscription } = req.body; - const { id } = req.user; + const body = await c.req.json(); + const { subscription } = body; + const { id } = c.get("user"); await db.sRem(`${id}:subscriptions`, JSON.stringify(subscription)); - res.send(subscription); + return c.json(subscription); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async password(req, res) { - const { - body: { password }, - user, - } = req; - if (!user.password) return res.send(true); + async password(c) { + const body = await c.req.json(); + const { password } = body; + const user = c.get("user"); + if (!user.password) return c.json(true); try { if (!password) fail("password not provided"); - res.send(await Bun.password.verify(password, user.password)); + return c.json(await Bun.password.verify(password, user.password)); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async pin(req, res) { - const { - body: { pin }, - user, - } = req; - res.send(!user.pin || user.pin === pin); + async pin(c) { + const body = await c.req.json(); + const { pin } = body; + const user = c.get("user"); + return c.json(!user.pin || user.pin === pin); }, - async otpsecret(req, res) { + async otpsecret(c) { try { - await requirePin(req); - const { otpsecret, username } = req.user; - res.send({ secret: otpsecret, username }); + const user = c.get("user"); + const body = await c.req.json(); + await requirePin({ body, user }); + const { otpsecret, username } = user; + return c.json({ secret: otpsecret, username }); } catch (e) { - res.code(500).send(e.message); + return c.json(e.message, 500); } }, - async contacts(req, res) { - const { params, user } = req; + async contacts(c) { + const user = c.get("user"); const { id } = user; + const limit = c.req.param("limit"); const lastlen = (await g(`${id}:lastlen`)) || 0; const len = await db.lLen(`${id}:payments`); const payments = (await db.lRange(`${id}:payments`, 0, len - lastlen)) || []; @@ -654,24 +656,21 @@ export default { c.trusted = true; }); - let { limit } = params; - limit ||= contacts.length; + let contactLimit = limit || contacts.length; contacts = contacts.filter((c) => !pins.includes(c.id)); - contacts = contacts.slice(0, limit); + contacts = contacts.slice(0, contactLimit); const combined = [...pinned, ...contacts]; - res.send(combined); + return c.json(combined); }, - async del(req, res) { - let { - params: { username }, - headers: { authorization }, - } = req; + async del(c) { + let username = c.req.param("username"); + const authorization = c.req.header("authorization"); fail("Unauthorized"); username = username.toLowerCase(); - if (!authorization?.includes(config.admin)) return res.code(401).send("unauthorized"); + if (!authorization?.includes(config.admin)) return c.json("unauthorized", 401); const { id, pubkey } = await g( `user:${await g(`user:${username.replace(/\s/g, "").toLowerCase()}`)}`, @@ -685,14 +684,13 @@ export default { db.del(`user:${id}`); db.del(`user:${pubkey}`); - res.send({}); + return c.json({}); }, - async reset(req, res) { - const { - body: { code, username, password }, - user: u, - } = req; + async reset(c) { + const body = await c.req.json(); + const { code, username, password } = body; + const u = c.get("user"); try { const admin = u?.admin; let id; @@ -704,7 +702,7 @@ export default { if (!user) fail("user not found"); - warn("password reset", user.username, code, req.headers["cf-connecting-ip"]); + warn("password reset", user.username, code, c.req.header("cf-connecting-ip")); user.pin = null; user.nsec = null; @@ -718,40 +716,38 @@ export default { await s(`user:${id}`, user); await db.del(`reset:${code}`); - res.send(pick(user, whitelist)); + return c.json(pick(user, whitelist)); } catch (e) { - err("password reset failed", e.message, req.headers["cf-connecting-ip"]); - bail(res, e.message); + err("password reset failed", e.message, c.req.header("cf-connecting-ip")); + return bail(c, e.message); } }, - async printerlogin(req, res) { - const { - body: { username, topic }, - } = req; - if (username === topic) res.send({ ok: true }); - else bail(res, "unauthorized"); + async printerlogin(c) { + const body = await c.req.json(); + const { username, topic } = body; + if (username === topic) return c.json({ ok: true }); + else return bail(c, "unauthorized"); }, - async acl(req, res) { - const { - body: { username, topic }, - } = req; - if (username === topic) res.send({ ok: true }); - else bail(res, "unauthorized"); + async acl(c) { + const body = await c.req.json(); + const { username, topic } = body; + if (username === topic) return c.json({ ok: true }); + else return bail(c, "unauthorized"); }, - async superuser(req, res) { - const { - body: { username }, - } = req; - if (username === config.mqtt2.username) res.send({ ok: true }); - else bail(res, "unauthorized"); + async superuser(c) { + const body = await c.req.json(); + const { username } = body; + if (username === config.mqtt2.username) return c.json({ ok: true }); + else return bail(c, "unauthorized"); }, - async request(req, res) { - const { email } = req.body; - const { user } = req; + async request(c) { + const body = await c.req.json(); + const { email } = body; + const user = c.get("user"); const { id } = user; try { @@ -777,16 +773,14 @@ export default { link, }); - res.send({ ok: true }); + return c.json({ ok: true }); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async verify(req, res) { - const { - params: { code }, - } = req; + async verify(c) { + const code = c.req.param("code"); try { const { id, email } = await g(`verify:${code}`); if (!id) fail("verification failed"); @@ -796,16 +790,15 @@ export default { await s(`user:${id}`, user); await s(`email:${email.toLowerCase()}`, id); - res.send(pick(user, fields)); + return c.json(pick(user, fields)); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async forgot(req, res) { - const { - body: { email }, - } = req; + async forgot(c) { + const body = await c.req.json(); + const { email } = body; try { const uid = await g(`email:${email.toLowerCase()}`); const user = await g(`user:${uid}`); @@ -821,47 +814,45 @@ export default { }); } - res.send({}); + return c.json({}); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async hidepay(req, res) { - const { - body: { username }, - } = req; + async hidepay(c) { + const body = await c.req.json(); + const { username } = body; const u = await getUser(username); u.hidepay = true; await s(`user:${u.id}`, u); - res.send({}); + return c.json({}); }, - async unlimit(req, res) { - const { - body: { username }, - } = req; + async unlimit(c) { + const body = await c.req.json(); + const { username } = body; const u = await getUser(username); u.unlimited = true; await s(`user:${u.id}`, u); - res.send({}); + return c.json({}); }, - async account(req, res) { - const { id } = req.params; - const { id: uid } = req.user; + async account(c) { + const id = c.req.param("id"); + const { id: uid } = c.get("user"); const pos = await db.lPos(`${uid}:accounts`, id); if (pos == null) fail("account not found"); const account = await g(`account:${id}`); if (account) account.balance = await getBalance(id); - res.send(account); + return c.json(account); }, - async accounts(req, res) { + async accounts(c) { try { - const { user } = req; + const user = c.get("user"); const accounts = []; for (const id of await db.lRange(`${user.id}:accounts`, 0, -1)) { @@ -886,17 +877,18 @@ export default { } } - res.send(accounts.reverse()); + return c.json(accounts.reverse()); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async createAccount(req, res) { + async createAccount(c) { try { - const { fingerprint, pubkey, name, seed, type, arkAddress, accountIndex } = req.body; - const { user } = req; + const body = await c.req.json(); + const { fingerprint, pubkey, name, seed, type, arkAddress, accountIndex } = body; + const user = c.get("user"); const { id: uid } = user; const id = v4(); @@ -917,14 +909,12 @@ export default { await createBalanceAccount(id); - // ARK accounts don't need Bitcoin Core wallet setup if (type === "ark") { const accountIds = await db.lRange(`${user.id}:accounts`, 0, -1); for (const accId of accountIds) { const acc = await g(`account:${accId}`); if (acc?.type === "ark") { - bail(res, "You already have an Ark vault"); - return; + return bail(c, "You already have an Ark vault"); } } const m = db @@ -934,11 +924,9 @@ export default { if (arkAddress) m.set(`arkaddr:${arkAddress}`, JSON.stringify({ aid: id, uid })); await m.exec(); - res.send(account); - return; + return c.json(account); } - // Bitcoin on-chain wallet setup let node = rpc(config[type]); await node.createWallet({ @@ -976,17 +964,18 @@ export default { .lPush(`${user.id}:accounts`, id) .exec(); - res.send(account); + return c.json(account); } catch (e) { - bail(res, e.message); + return bail(c, e.message); } }, - async updateAccount(req, res) { - const { id } = req.params; - const { id: uid } = req.user; + async updateAccount(c) { + const id = c.req.param("id"); + const { id: uid } = c.get("user"); + const body = await c.req.json(); const { name, autowithdraw, threshold, reserve, destination, currency, fingerprint, pubkey } = - req.body; + body; const pos = await db.lPos(`${uid}:accounts`, id); if (pos == null) fail("account not found"); @@ -1017,19 +1006,19 @@ export default { .catch((e) => console.error("importAccountHistory failed:", e.message)); } - res.send(account); + return c.json(account); }, - async deleteAccount(req, res) { + async deleteAccount(c) { try { - const { id } = req.body; - const { id: uid } = req.user; + const body = await c.req.json(); + const { id } = body; + const { id: uid } = c.get("user"); const account = await g(`account:${id}`); const pos = await db.lPos(`${uid}:accounts`, id); if (!(account && pos != null)) fail("account not found"); - // Only unload Bitcoin Core wallet for non-ARK accounts if (account.type !== "ark") { try { const node = rpc({ ...config[account.type], wallet: id }); @@ -1046,25 +1035,27 @@ export default { .del(`${id}:payments`) .exec(); - res.send({ ok: true }); + return c.json({ ok: true }); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async flash(req, res) { - const { ssid, key, token } = req.body; + async flash(c) { + const body = await c.req.json(); + const { ssid, key, token } = body; const cfg = `${ssid.trim()}\n${key.trim()}\n${token.trim()}\n`; await writeFile("./printer/config.txt", cfg, "utf8"); await $`./mklittlefs -c ./printer -p 256 -b 4096 -s 0x20000 ./littlefs.img`; - res.header("Content-Type", "application/octet-stream"); - return res.send(createReadStream("./littlefs.img")); + return new Response(Bun.file("./littlefs.img"), { + headers: { "Content-Type": "application/octet-stream" }, + }); }, - async app(req, res) { - const { pubkey } = req.params; - const { user } = req; + async app(c) { + const pubkey = c.req.param("pubkey"); + const user = c.get("user"); const app = await g(`app:${pubkey}`); if (app.uid !== user.id) fail("unauthorized"); @@ -1083,11 +1074,11 @@ export default { app.nwc = `nostr+walletconnect://${serverPubkey2}?relay=${relay}&secret=${app.secret}&lud16=${lud16}`; app.payments = payments.filter((p) => p); - res.send(app); + return c.json(app); }, - async apps(req, res) { - const { user } = req; + async apps(c) { + const user = c.get("user"); const pubkeys = await db.sMembers(`${user.id}:apps`); const apps = await Promise.all(pubkeys.map((p) => g(`app:${p}`))); @@ -1112,14 +1103,15 @@ export default { }), ); - res.send(apps); + return c.json(apps); }, - async updateApp(req, res) { + async updateApp(c) { try { - let { secret, pubkey, max_amount, max_fee, budget_renewal, name, notify } = req.body; + const body = await c.req.json(); + let { secret, pubkey, max_amount, max_fee, budget_renewal, name, notify } = body; - const { user } = req; + const user = c.get("user"); const uid = user.id; let app = await g(pubkey); @@ -1144,18 +1136,19 @@ export default { await s(`app:${pubkey}`, app); await db.sAdd(`${uid}:apps`, pubkey); - res.send({}); + return c.json({}); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async deleteApp(req, res) { + async deleteApp(c) { try { - const { user } = req; + const user = c.get("user"); const uid = user.id; - const { pubkey } = req.body; + const body = await c.req.json(); + const { pubkey } = body; const app = await g(`app:${pubkey}`); if (app && uid !== app.uid) { warn(app.uid, uid); @@ -1163,110 +1156,118 @@ export default { } await db.sRem(`${uid}:apps`, pubkey); await db.del(`app:${pubkey}`); - res.send({}); + return c.json({}); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async addPin(req, res) { - const { id: uid } = req.user; - const { id } = req.body; + async addPin(c) { + const { id: uid } = c.get("user"); + const body = await c.req.json(); + const { id } = body; await db.sAdd(`${uid}:pins`, id); - res.send({}); + return c.json({}); }, - async deletePin(req, res) { - const { id: uid } = req.user; - const { id } = req.body; + async deletePin(c) { + const { id: uid } = c.get("user"); + const body = await c.req.json(); + const { id } = body; await db.sRem(`${uid}:pins`, id); - res.send({}); + return c.json({}); }, - async trust(req, res) { - const { id } = req.user; - res.send(await db.sMembers(`${id}:trust`)); + async trust(c) { + const { id } = c.get("user"); + return c.json(await db.sMembers(`${id}:trust`)); }, - async addTrust(req, res) { - const { id: uid } = req.user; - const { id } = req.body; + async addTrust(c) { + const { id: uid } = c.get("user"); + const body = await c.req.json(); + const { id } = body; await db.sAdd(`${uid}:trust`, id); - res.send({}); + return c.json({}); }, - async deleteTrust(req, res) { - const { id: uid } = req.user; - const { id } = req.body; + async deleteTrust(c) { + const { id: uid } = c.get("user"); + const body = await c.req.json(); + const { id } = body; await db.sRem(`${uid}:trust`, id); - res.send({}); + return c.json({}); }, - async passkeyRegisterOptions(req, res) { + async passkeyRegisterOptions(c) { try { - const { user } = req; - const origin = req.body.origin || `https://${config.hostname}`; + const user = c.get("user"); + const body = await c.req.json(); + const origin = body.origin || `https://${config.hostname}`; const options = await generatePasskeyRegistration(user, origin); - res.send(options); + return c.json(options); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async passkeyRegisterVerify(req, res) { + async passkeyRegisterVerify(c) { try { - const { user } = req; - const origin = req.body.origin || `https://${config.hostname}`; - const credential = await verifyPasskeyRegistration(user, req.body, origin); + const user = c.get("user"); + const body = await c.req.json(); + const origin = body.origin || `https://${config.hostname}`; + const credential = await verifyPasskeyRegistration(user, body, origin); if (!user.passkeys) user.passkeys = []; user.passkeys.push(credential); await s(`user:${user.id}`, user); - res.send({ ok: true }); + return c.json({ ok: true }); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async passkeyLoginOptions(req, res) { + async passkeyLoginOptions(c) { try { - const origin = req.body.origin || `https://${config.hostname}`; + const body = await c.req.json(); + const origin = body.origin || `https://${config.hostname}`; const options = await generatePasskeyLogin(origin); - res.send(options); + return c.json(options); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async passkeyLoginVerify(req, res) { + async passkeyLoginVerify(c) { try { - const { credential, challengeId, origin: reqOrigin } = req.body; + const body = await c.req.json(); + const { credential, challengeId, origin: reqOrigin } = body; const origin = reqOrigin || `https://${config.hostname}`; const user = await verifyPasskeyLogin(credential, challengeId, origin); const payload = { id: user.id }; const token = jwt.sign(payload, config.jwt); - res.send({ user: pick(user, whitelist), token }); + return c.json({ user: pick(user, whitelist), token }); } catch (e) { console.log(e); - bail(res, e.message); + return bail(c, e.message); } }, - async credits(req, res) { - const { id } = req.user; + async credits(c) { + const { id } = c.get("user"); const bitcoin = await getCredit(id, "bitcoin"); const lightning = await getCredit(id, "lightning"); const liquid = await getCredit(id, "liquid"); - res.send({ bitcoin, lightning, liquid }); + return c.json({ bitcoin, lightning, liquid }); }, - async ro(req, res) { - const { user } = req; + async ro(c) { + const user = c.get("user"); const payload = { id: `${user.id}-ro` }; const token = jwt.sign(payload, config.jwt); - res.send(token); + return c.json(token); }, }; |
