diff options
| -rw-r--r-- | lib/api.ts | 1 | ||||
| -rw-r--r-- | lib/auth.ts | 6 | ||||
| -rw-r--r-- | lib/db.ts | 32 | ||||
| -rw-r--r-- | lib/invoices.ts | 4 | ||||
| -rw-r--r-- | lib/lightning.ts | 22 | ||||
| -rw-r--r-- | lib/logging.ts | 14 | ||||
| -rw-r--r-- | lib/mail.ts | 12 | ||||
| -rw-r--r-- | lib/migrate.ts | 20 | ||||
| -rw-r--r-- | lib/nostr.ts | 90 | ||||
| -rw-r--r-- | lib/nwc.ts | 82 | ||||
| -rw-r--r-- | lib/rates.ts | 22 | ||||
| -rw-r--r-- | lib/register.ts | 12 | ||||
| -rw-r--r-- | lib/sockets.ts | 25 | ||||
| -rw-r--r-- | lib/strfry.ts | 17 | ||||
| -rw-r--r-- | lib/upload.ts | 18 | ||||
| -rw-r--r-- | lib/webhooks.ts | 8 | ||||
| -rw-r--r-- | routes/email.ts | 14 | ||||
| -rw-r--r-- | routes/info.ts | 4 | ||||
| -rw-r--r-- | routes/invoices.ts | 2 | ||||
| -rw-r--r-- | routes/items.ts | 22 | ||||
| -rw-r--r-- | routes/lnurl.ts | 48 | ||||
| -rw-r--r-- | routes/locations.ts | 2 | ||||
| -rw-r--r-- | routes/nostr.ts | 70 | ||||
| -rw-r--r-- | routes/payments.ts | 2 | ||||
| -rw-r--r-- | routes/rates.ts | 2 | ||||
| -rw-r--r-- | routes/shopify.ts | 10 | ||||
| -rw-r--r-- | routes/users.ts | 20 |
27 files changed, 297 insertions, 284 deletions
@@ -2,4 +2,3 @@ export default { bitcoin: "https://mempool.space/api/v1", liquid: "https://liquid.network/api/v1", }; - diff --git a/lib/auth.ts b/lib/auth.ts index 0a8d43d3..29edcf61 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -8,7 +8,7 @@ export const admin = { preValidation: fastifyPassport.authenticate( "jwt", { session: false }, - function (req, res, err, user, info) { + (req, res, err, user, info) => { if (!user.admin) return res.code(401).send("unauthorized"); req.user = user; }, @@ -19,7 +19,7 @@ export const optional = { preValidation: fastifyPassport.authenticate( "jwt", { session: false }, - function (req, res, err, user, info) { + (req, res, err, user, info) => { req.user = user; }, ), @@ -45,7 +45,7 @@ export const jwtStrategy = new jwt.Strategy( secretOrKey: config.jwt, }, async (payload, next) => { - let user = await getUser(payload.id); + const user = await getUser(payload.id); next(null, user); }, ); @@ -3,7 +3,7 @@ import { fail, sleep } from "$lib/utils"; import { createClient, defineScript } from "redis"; import { warn, err } from "$lib/logging"; -let DEBIT = ` +const DEBIT = ` local balanceKey = KEYS[1] local creditKey = KEYS[2] local amount = tonumber(ARGV[1]) @@ -27,7 +27,7 @@ redis.call('decrby', balanceKey, tostring(math.floor(amount + tip + fee + ourfee return ourfee `; -let REVERSE = ` +const REVERSE = ` local paymentKey = KEYS[1] local balanceKey = KEYS[2] local creditKey = KEYS[3] @@ -51,19 +51,19 @@ end return pid `; -let debit = defineScript({ +const debit = defineScript({ NUMBER_OF_KEYS: 2, SCRIPT: DEBIT, transformArguments: (...args) => args.map((a) => a.toString()), }); -let reverse = defineScript({ +const reverse = defineScript({ NUMBER_OF_KEYS: 6, SCRIPT: REVERSE, transformArguments: (...args) => args.map((a) => a.toString()), }); -export let db = createClient({ +export const db = createClient({ url: config.db, scripts: { debit, reverse }, socket: { @@ -71,14 +71,14 @@ export let db = createClient({ }, }); -export let archive = createClient({ +export const archive = createClient({ url: config.archive, socket: { reconnectStrategy: (retries) => Math.min(retries * 50, 5000), }, }); -export let arc2 = createClient({ +export const arc2 = createClient({ url: config.arc2, socket: { reconnectStrategy: (retries) => Math.min(retries * 50, 5000), @@ -117,8 +117,8 @@ db.on("end", () => { export default db; -export let g = async (k) => { - let v = await db.get(k); +export const g = async (k) => { + const v = await db.get(k); try { return JSON.parse(v); } catch (e) { @@ -126,13 +126,13 @@ export let g = async (k) => { } }; -export let s = (k, v) => { +export const s = (k, v) => { if (k === "user:null" || k === "user:undefined") fail("null user"); db.set(k, JSON.stringify(v)); }; -export let ga = async (k) => { - let v = await archive.get(k); +export const ga = async (k) => { + const v = await archive.get(k); try { return JSON.parse(v); } catch (e) { @@ -140,7 +140,7 @@ export let ga = async (k) => { } }; -export let sa = (k, v) => { +export const sa = (k, v) => { if (k === "user:null" || k === "user:undefined") { warn("###### NULL USER #######"); console.trace(); @@ -148,15 +148,15 @@ export let sa = (k, v) => { archive.set(k, JSON.stringify(v)); }; -let retries = {}; -export let t = async (k, f) => { +const retries = {}; +export const t = async (k, f) => { try { await db.watch(k); await f(await db.get(k), db); } catch (err) { if (!err.message.includes("watch")) throw err; - let r = retries[k] || 0; + const r = retries[k] || 0; retries[k] = r + 1; if (r < 10) { diff --git a/lib/invoices.ts b/lib/invoices.ts index 749994b4..d79ead69 100644 --- a/lib/invoices.ts +++ b/lib/invoices.ts @@ -51,7 +51,9 @@ export const generate = async ({ invoice, user }) => { const id = v4(); - let hash, text, path; + let hash; + let text; + let path; if (account.seed) { type = "bitcoin"; diff --git a/lib/lightning.ts b/lib/lightning.ts index 7a35c9f2..ce39f0ad 100644 --- a/lib/lightning.ts +++ b/lib/lightning.ts @@ -6,8 +6,8 @@ import { credit, types } from "$lib/payments"; import { getPayment } from "$lib/utils"; export async function listenForLightning() { - let inv = await ln.waitanyinvoice((await g("pay_index")) || 0); - let { + const inv = await ln.waitanyinvoice((await g("pay_index")) || 0); + const { bolt11, description, pay_index, @@ -18,16 +18,16 @@ export async function listenForLightning() { await s("pay_index", pay_index); setTimeout(listenForLightning); - let received = Math.round(amount_received_msat / 1000); + const received = Math.round(amount_received_msat / 1000); try { if (!preimage) return; - let id = await g(`invoice:${bolt11}`); - let invoice = await g(`invoice:${id}`); + const id = await g(`invoice:${bolt11}`); + const invoice = await g(`invoice:${id}`); if (!invoice) return warn("received lightning with no invoice", bolt11); - if (invoice && invoice.memo) { + if (invoice?.memo) { try { if (JSON.parse(description).kind === 9734) await handleZap(inv); } catch (e) { @@ -36,10 +36,16 @@ export async function listenForLightning() { } } - let p = await getPayment(bolt11); + const p = await getPayment(bolt11); if (p) return warn("already processed", bolt11); - await credit({ hash:bolt11, amount:received, memo:invoice.memo, ref:preimage, type:types.lightning}); + await credit({ + hash: bolt11, + amount: received, + memo: invoice.memo, + ref: preimage, + type: types.lightning, + }); } catch (e) { console.log(e); err("problem receiving lightning payment", e.message); diff --git a/lib/logging.ts b/lib/logging.ts index 4913839a..aab110c2 100644 --- a/lib/logging.ts +++ b/lib/logging.ts @@ -1,12 +1,12 @@ import pino from "pino"; -export let l = (...msgs) => pino().info(msgs.join(" ")); -export let warn = (...msgs) => pino().warn(msgs.join(" ")); -export let err = (...msgs) => pino().error(msgs.join(" ")); +export const l = (...msgs) => pino().info(msgs.join(" ")); +export const warn = (...msgs) => pino().warn(msgs.join(" ")); +export const err = (...msgs) => pino().error(msgs.join(" ")); -export let line = () => { - let stack = new Error().stack; - let stackLine = stack.split("\n")[1]; - let match = stackLine.match(/at\s+(.*):(\d+):(\d+)/); +export const line = () => { + const stack = new Error().stack; + const stackLine = stack.split("\n")[1]; + const match = stackLine.match(/at\s+(.*):(\d+):(\d+)/); return `${match[1]}:${match[2]}`; }; diff --git a/lib/mail.ts b/lib/mail.ts index 16b3dbbf..033902aa 100644 --- a/lib/mail.ts +++ b/lib/mail.ts @@ -6,23 +6,23 @@ import handlebars from "handlebars"; import fs from "fs"; import path from "path"; -let Charset = "UTF-8"; +const Charset = "UTF-8"; -export let templates = { +export const templates = { verifyEmail: "templates/payments/verify.html", paymentReceived: "templates/payments/received.html", passwordReset: "templates/payments/reset.html", }; -export let mail = async (user, subject, template, params) => { +export const mail = async (user, subject, template, params) => { try { l("sending mail", user.username, subject); if (!user.email) return; - let source = fs.readFileSync(template, "utf8"); - let html = handlebars.compile(source)(params); + const source = fs.readFileSync(template, "utf8"); + const html = handlebars.compile(source)(params); - let client = new SESClient({ region: "us-east-2" }); + const client = new SESClient({ region: "us-east-2" }); await client.send( new SendEmailCommand({ diff --git a/lib/migrate.ts b/lib/migrate.ts index 1ff280ea..e0ec3b1e 100644 --- a/lib/migrate.ts +++ b/lib/migrate.ts @@ -2,8 +2,8 @@ import { err, warn, l, line } from "$lib/logging"; import { types } from "$lib/payments"; import { archive, db, g, ga, s } from "$lib/db"; -let queued = {}; -let migrating = {}; +const queued = {}; +const migrating = {}; async function migrate(id) { try { @@ -35,7 +35,7 @@ async function migrate(id) { l("migrating", username); try { - let multi = await db + const multi = await db .multi() .set(`user:${username}`, id) .set(`user:${user.pubkey}`, id) @@ -73,11 +73,11 @@ async function migrate(id) { let pid; while ((pid = await archive.rPop(`${id}:payments`))) { - let p = await ga(`payment:${pid}`); - let pos = await db.lPos(`${id}:payments`, pid); + const p = await ga(`payment:${pid}`); + const pos = await db.lPos(`${id}:payments`, pid); if (!p || pos) continue; if (p.type === types.internal && p.ref) { - let recipient = await g(`user:${p.ref}`); + const recipient = await g(`user:${p.ref}`); if (!recipient) queued[p.ref] = true; } @@ -90,8 +90,8 @@ async function migrate(id) { let iid; while ((iid = await archive.rPop(`${id}:invoices`))) { - let inv = await ga(`invoice:${iid}`); - let pos = await db.lPos(`${id}:invoices`, iid); + const inv = await ga(`invoice:${iid}`); + const pos = await db.lPos(`${id}:invoices`, iid); if (!inv || pos) continue; await db.lPush(`${id}:invoices`, iid); await s(`invoice:${iid}`, inv); @@ -114,10 +114,10 @@ async function migrate(id) { } } -let keepMigrating = async () => { +const keepMigrating = async () => { try { if (Object.keys(queued).length) { - let next = Object.keys(queued)[0]; + const next = Object.keys(queued)[0]; await migrate(next); } } catch (e) { diff --git a/lib/nostr.ts b/lib/nostr.ts index 07e5a773..82251baa 100644 --- a/lib/nostr.ts +++ b/lib/nostr.ts @@ -1,23 +1,23 @@ import config from "$config"; -import { g, s, db } from "$lib/db"; +import { db, g, s } from "$lib/db"; import { l } from "$lib/logging"; import { fail } from "$lib/utils"; -import { nip19, finalizeEvent, getPublicKey } from "nostr-tools"; -import { Relay } from "nostr-tools/relay"; +import { finalizeEvent, getPublicKey, nip19 } from "nostr-tools"; import { AbstractSimplePool } from "nostr-tools/abstract-pool"; +import { Relay } from "nostr-tools/relay"; export const serverPubkey = getPublicKey( nip19.decode(config.nostrKey).data as Uint8Array, ); -let alwaysTrue: any = (t: Event) => { +const alwaysTrue: any = (t: Event) => { t[Symbol("verified")] = true; return true; }; -export let pool = new AbstractSimplePool({ verifyEvent: alwaysTrue }); -let opts = { maxWait: 2000 }; +export const pool = new AbstractSimplePool({ verifyEvent: alwaysTrue }); +const opts = { maxWait: 2000 }; -export let anon = (pubkey) => ({ +export const anon = (pubkey) => ({ name: pubkey.substr(0, 6), pubkey, anon: true, @@ -26,78 +26,78 @@ export let anon = (pubkey) => ({ }); export async function send(ev, url = config.nostr) { - if (!(ev && ev.id)) return; - let r = await Relay.connect(url); + if (!ev?.id) return; + const r = await Relay.connect(url); await r.publish(ev); r.close(); } export async function handleZap(invoice) { - let pubkey = serverPubkey; - let sk = nip19.decode(config.nostrKey).data as Uint8Array; - let zapreq = JSON.parse(invoice.description); + const pubkey = serverPubkey; + const sk = nip19.decode(config.nostrKey).data as Uint8Array; + const zapreq = JSON.parse(invoice.description); if (!zapreq.tags || zapreq.tags.length === 0) { - fail(`No tags found`); + fail("No tags found"); } - let ptags = zapreq.tags.filter( - (t) => t && t.length && t.length >= 2 && t[0] === "p", + const ptags = zapreq.tags.filter( + (t) => t?.length && t.length >= 2 && t[0] === "p", ); if (ptags.length !== 1) { - fail(`None or multiple p tags found`); + fail("None or multiple p tags found"); } - let etags = zapreq.tags.filter( - (t) => t && t.length && t.length >= 2 && t[0] === "e", + const etags = zapreq.tags.filter( + (t) => t?.length && t.length >= 2 && t[0] === "e", ); if (!(etags.length === 0 || etags.length === 1)) { - fail(`Expected none or 1 e tags`); + fail("Expected none or 1 e tags"); } - let relays_tag = zapreq.tags.find( - (t) => t && t.length && t.length >= 2 && t[0] === "relays", + const relays_tag = zapreq.tags.find( + (t) => t?.length && t.length >= 2 && t[0] === "relays", ); if (!relays_tag) { - fail(`No relays tag found`); + fail("No relays tag found"); } - let relays = relays_tag.slice(1).filter((r) => r && r.startsWith("ws")); - let etag = etags.length > 0 && etags[0]; - let ptag = ptags[0]; + const relays = relays_tag.slice(1).filter((r) => r?.startsWith("ws")); + const etag = etags.length > 0 && etags[0]; + const ptag = ptags[0]; - let kind = 9735; - let created_at = invoice.paid_at; - let content = zapreq.content; + const kind = 9735; + const created_at = invoice.paid_at; + const content = zapreq.content; - let tags = [ptag]; + const tags = [ptag]; if (etag) tags.push(etag); tags.push(["bolt11", invoice.bolt11]); tags.push(["description", invoice.description]); tags.push(["preimage", invoice.payment_preimage]); - let ev = { pubkey, kind, created_at, content, tags }; - let signed = await finalizeEvent(ev, sk); + const ev = { pubkey, kind, created_at, content, tags }; + const signed = await finalizeEvent(ev, sk); l("sending receipt"); await Promise.allSettled( relays.map(async (url) => { - let r = await Relay.connect(url); + const r = await Relay.connect(url); await r.publish(signed); r.close(); }), ); } -export let getRelays = async (pubkey): Promise<any> => { - let { relays } = config; - let filter = { authors: [pubkey], kinds: [10002] }; - let event = await pool.get([config.nostr], filter, opts); +export const getRelays = async (pubkey): Promise<any> => { + const { relays } = config; + const filter = { authors: [pubkey], kinds: [10002] }; + const event = await pool.get([config.nostr], filter, opts); let read = relays; let write = relays; @@ -105,7 +105,7 @@ export let getRelays = async (pubkey): Promise<any> => { if (event) { read = []; write = []; - for (let r of event.tags) { + for (const r of event.tags) { if (r[0] !== "r") continue; if (!r[2] || r[2] === "write") write.push(r[1]); if (!r[2] || r[2] === "read") read.push(r[1]); @@ -119,11 +119,11 @@ export let getRelays = async (pubkey): Promise<any> => { return { read, write }; }; -export let getProfile = async (pubkey) => { +export const getProfile = async (pubkey) => { let profile = await g(`${pubkey}:profile`); if (!profile) { - let pubkeys = [pubkey]; + const pubkeys = [pubkey]; let relays = [config.nostr]; let filter: any = { authors: pubkeys, kinds: [0] }; @@ -153,12 +153,12 @@ export let getProfile = async (pubkey) => { return profile; }; -export let getCount = async (pubkey) => { +export const getCount = async (pubkey) => { let follows = await g(`${pubkey}:follows:n`); let followers = await g(`${pubkey}:followers:n`); if (follows === null) { - let filter: any = { kinds: [3], authors: [pubkey] }; + const filter: any = { kinds: [3], authors: [pubkey] }; let ev = await pool.get([config.nostr], filter, opts); if (!ev) { @@ -179,12 +179,12 @@ export let getCount = async (pubkey) => { } if (followers === null) { - let filter: any = { cache: ["user_infos", { pubkeys: [pubkey] }] }; - let infos = await pool.querySync([config.cache], filter, opts); + const filter: any = { cache: ["user_infos", { pubkeys: [pubkey] }] }; + const infos = await pool.querySync([config.cache], filter, opts); - let f = infos.find((e) => e.kind === 10000133); + const f = infos.find((e) => e.kind === 10000133); let counts = {}; - if (f && f.content) { + if (f?.content) { counts = JSON.parse(f.content); followers = counts[pubkey]; } @@ -10,13 +10,13 @@ import type { UnsignedEvent } from "nostr-tools"; import { generate } from "$lib/invoices"; import ln from "$lib/ln"; -let result = (result) => ({ result }); -let error = (error) => ({ error }); +const result = (result) => ({ result }); +const error = (error) => ({ error }); -let methods = ["pay_invoice", "get_balance", "get_info", "make_invoice"]; +const methods = ["pay_invoice", "get_balance", "get_info", "make_invoice"]; export default () => { - let r = new Relay("ws://strfry:7777"); + const r = new Relay("ws://strfry:7777"); r.on("open", (_) => { r.subscribe("nwc", { kinds: [23194], "#p": [serverPubkey] }); @@ -26,18 +26,18 @@ export default () => { try { if (sub !== "nwc") return; let { content, pubkey } = ev; - let sk = nip19.decode(config.nostrKey).data as Uint8Array; - let { params, method } = JSON.parse( + const sk = nip19.decode(config.nostrKey).data as Uint8Array; + const { params, method } = JSON.parse( await nip04.decrypt(sk, pubkey, content), ); if (!methods.includes(method)) return; - let uid = await g(pubkey); - let user = await g(`user:${uid}`); + const uid = await g(pubkey); + const user = await g(`user:${uid}`); - let result = await handle(method, params, user); - let payload = JSON.stringify({ result_type: method, ...result }); + const result = await handle(method, params, user); + const payload = JSON.stringify({ result_type: method, ...result }); content = await nip04.encrypt(sk, pubkey, payload); let response: UnsignedEvent = { @@ -59,19 +59,19 @@ export default () => { }); }; -let handle = (method, params, user) => +const handle = (method, params, user) => ({ async pay_invoice() { - let { invoice: pr } = params; - let { amount_msat, payee } = await ln.decode(pr); - let { id } = await ln.getinfo(); - let amount = Math.round(amount_msat / 1000); + const { invoice: pr } = params; + const { amount_msat, payee } = await ln.decode(pr); + const { id } = await ln.getinfo(); + const amount = Math.round(amount_msat / 1000); if (payee === id) { - let invoice = await getInvoice(pr); - let recipient = await g(`user:${invoice.uid}`); + const invoice = await getInvoice(pr); + const recipient = await g(`user:${invoice.uid}`); - let { id: preimage } = await sendInternal({ + const { id: preimage } = await sendInternal({ amount, invoice, recipient, @@ -79,8 +79,8 @@ let handle = (method, params, user) => }); if (invoice.memo.includes("9734")) { - let { invoices } = await ln.listinvoices({ invstring: pr }); - let inv = invoices[0]; + const { invoices } = await ln.listinvoices({ invstring: pr }); + const inv = invoices[0]; inv.payment_preimage = preimage; inv.paid_at = Math.floor(Date.now() / 1000); try { @@ -90,32 +90,30 @@ let handle = (method, params, user) => } } - return result({ preimage }); - } else { - await sendLightning({ - amount, - user, - pr, - maxfee: 50, - }); + } + await sendLightning({ + amount, + user, + pr, + maxfee: 50, + }); - for (let i = 0; i < 10; i++) { - let { pays } = await ln.listpays(pr); - let p = pays.find((p) => p.status === "complete"); - if (p) { - let { preimage } = p; - return result({ preimage }); - } - await sleep(2000); + for (let i = 0; i < 10; i++) { + const { pays } = await ln.listpays(pr); + const p = pays.find((p) => p.status === "complete"); + if (p) { + const { preimage } = p; + return result({ preimage }); } - - return error({ code: "INTERNAL", message: "Payment timed out" }); + await sleep(2000); } + + return error({ code: "INTERNAL", message: "Payment timed out" }); }, async get_info() { - let { alias, blockheight, color } = await ln.getinfo(); + const { alias, blockheight, color } = await ln.getinfo(); return result({ alias, color, @@ -133,16 +131,16 @@ let handle = (method, params, user) => }, async make_invoice() { - let { amount, description, description_hash, expiry } = params; + const { amount, description, description_hash, expiry } = params; - let invoice = { + const invoice = { amount: Math.round(amount / 1000), type: "lightning", memo: description, expiry, }; - let { hash, created: created_at } = await generate({ invoice, user }); + const { hash, created: created_at } = await generate({ invoice, user }); return result({ type: "incoming", diff --git a/lib/rates.ts b/lib/rates.ts index 0dc3f8aa..1778cdd1 100644 --- a/lib/rates.ts +++ b/lib/rates.ts @@ -8,24 +8,24 @@ import WebSocket from "ws"; export let rate; let last; let ws; -let connect = async () => { +const connect = async () => { if (ws && ws.readyState === 1 && Date.now() - last < 5000) return; if (ws) ws.terminate() && (await sleep(Math.round(Math.random() * 1000))); ws = new WebSocket("wss://stream.binance.com:9443/ws/btcusdt@ticker"); - ws.onmessage = async function (event) { + ws.onmessage = async (event) => { try { - let msg = JSON.parse(event.data); - let rates = (await g("rates")) || {}; - let { fx } = await g("fx") || {}; + const msg = JSON.parse(event.data); + const rates = (await g("rates")) || {}; + const { fx } = (await g("fx")) || {}; if (!fx) return; Object.keys(fx).map((symbol) => { rates[symbol] = msg.c * fx[symbol]; }); - rates["IRT"] = ( + rates.IRT = ( (await got("https://api.nobitex.ir/v2/orderbook/BTCIRT").json()) as any ).lastTradePrice; @@ -39,18 +39,18 @@ let connect = async () => { } }; - ws.onerror = async function (error) { + ws.onerror = async (error) => { err("binance socket error", error.message); }; return ws; }; -export let getFx = async () => { +export const getFx = async () => { connect(); - let date = 0, - fx = await g("fx"); + let date = 0; + let fx = await g("fx"); if (fx) ({ date, fx } = fx); if (Date.now() - date > 24 * 60 * 60 * 1000) { @@ -64,7 +64,7 @@ export let getFx = async () => { ({ fx } = (await got("https://coinos.io/api/fx").json()) as any); } - let USD = fx["USD"]; + const USD = fx.USD; Object.keys(fx).map((k) => { fx[k] = fx[k] / USD; diff --git a/lib/register.ts b/lib/register.ts index c2677c30..139aefcf 100644 --- a/lib/register.ts +++ b/lib/register.ts @@ -8,7 +8,7 @@ import { db } from "$lib/db"; import { l, warn } from "$lib/logging"; import { fail, getUser } from "$lib/utils"; -let valid = /^[\p{L}\p{N}]{2,24}$/u; +const valid = /^[\p{L}\p{N}]{2,24}$/u; export default async (user, ip) => { let { password, pubkey, username } = user; l("registering", username); @@ -18,10 +18,10 @@ export default async (user, ip) => { if (!valid.test(username)) fail("Usernames can only have letters and numbers"); - let id = v4(); + const id = v4(); user.id = id; - let exists = await getUser(username); + const exists = await getUser(username); if (exists) fail(`Username ${username} taken`); if (password) { @@ -34,7 +34,7 @@ export default async (user, ip) => { user.currency = "USD"; if (config.ipregistry) { try { - let { + const { location: { country: { code } }, }: any = await got( `https://api.ipregistry.co/${ip}?key=${config.ipregistry}&fields=location.country.code`, @@ -52,7 +52,7 @@ export default async (user, ip) => { user.migrated = true; user.locktime = 300; - let account = JSON.stringify({ + const account = JSON.stringify({ id, type: "ecash", name: "Spending", @@ -66,7 +66,7 @@ export default async (user, ip) => { .set(`account:${id}`, account) .set(`${pubkey}:follows:n`, 0) .set(`${pubkey}:followers:n`, 0) - .set(`${pubkey}:pubkeys`, "[]") + .set(`${pubkey}:pubkeys`, "[]") .lPush(`${id}:accounts`, id) .exec(); diff --git a/lib/sockets.ts b/lib/sockets.ts index 6344a889..1a5421f9 100644 --- a/lib/sockets.ts +++ b/lib/sockets.ts @@ -12,7 +12,7 @@ const users = {}; export const emit = (uid, type, data) => { if (type === "payment" && data.amount > 0) { for (let i = subscriptions.length - 1; i >= 0; i--) { - let s = subscriptions[i]; + const s = subscriptions[i]; if (s.invoice && s.invoice.id === data.iid) { s.ws.send(JSON.stringify({ type, data })); @@ -21,7 +21,7 @@ export const emit = (uid, type, data) => { } if (!store.sockets[uid]) return; - for (let id in store.sockets[uid]) { + for (const id in store.sockets[uid]) { const ws = store.sockets[uid][id]; ws.send(JSON.stringify({ type, data })); } @@ -42,12 +42,12 @@ export const broadcast = (type, data) => { }); }; -let track = async (ws, token) => { - let { id } = ws; - let { id: uid } = jwt.decode(token); +const track = async (ws, token) => { + const { id } = ws; + const { id: uid } = jwt.decode(token); if (!uid) fail("Invalid JWT token"); - let user = await getUser(uid); + const user = await getUser(uid); if (!user) fail(`User not found ${uid}`); if (!store.sockets[uid]) store.sockets[uid] = {}; @@ -75,7 +75,8 @@ Bun.serve({ }, websocket: { async message(ws: any, message: string) { - let type, data; + let type; + let data; try { ({ type, data } = JSON.parse(message)); @@ -104,7 +105,7 @@ Bun.serve({ if (e.message.includes("not found")) ws.send(JSON.stringify({ type: "logout" })); setTimeout( - () => ws && ws.close(code, `closing due to error ${e.message}`), + () => ws?.close(code, `closing due to error ${e.message}`), 1000, ); } @@ -131,10 +132,10 @@ Bun.serve({ all[id] = ws; }, close(ws: any) { - let { id } = ws; + const { id } = ws; try { const uid = users[id]; - if (store.sockets[uid] && store.sockets[uid][id]) { + if (store.sockets[uid]?.[id]) { delete store.sockets[uid][id]; } @@ -150,10 +151,10 @@ Bun.serve({ }, }); -export let sendHeartbeat = () => { +export const sendHeartbeat = () => { for (const uid in store.sockets) { for (const id in store.sockets[uid]) { - let ws = store.sockets[uid][id]; + const ws = store.sockets[uid][id]; ws.beats++; if (ws.beats > 10) ws.close(code, `${uid} ${id} lost heartbeat`); } diff --git a/lib/strfry.ts b/lib/strfry.ts index 029c8eee..9caa539e 100644 --- a/lib/strfry.ts +++ b/lib/strfry.ts @@ -1,24 +1,24 @@ import { createServer, connect } from "net"; import { randomUUID } from "crypto"; -let exec = async (cmd) => +const exec = async (cmd) => new Promise((resolve, reject) => { - let c = `/app/strfry ${cmd} 2>/dev/null`; - let id = randomUUID(); + const c = `/app/strfry ${cmd} 2>/dev/null`; + const id = randomUUID(); const resultSocketPath = `/sockets/result_${id}`; const resultServer = createServer((socket) => { let resultBuffer = ""; - let results = []; + const results = []; socket.on("data", (data) => { resultBuffer += data.toString(); - let parts = resultBuffer.split("\n"); + const parts = resultBuffer.split("\n"); for (let i = 0; i < parts.length - 1; i++) { try { - let parsedObject = JSON.parse(parts[i]); + const parsedObject = JSON.parse(parts[i]); results.push(parsedObject); } catch (e) { console.error(parts[i]); @@ -54,5 +54,6 @@ let exec = async (cmd) => ); }); -export let scan = (f) => exec(`scan '${JSON.stringify(f)}'\n`) -export let sync = (r, f) => exec(`sync ${r} --dir down --filter '${JSON.stringify(f)}'\n`) +export const scan = (f) => exec(`scan '${JSON.stringify(f)}'\n`); +export const sync = (r, f) => + exec(`sync ${r} --dir down --filter '${JSON.stringify(f)}'\n`); diff --git a/lib/upload.ts b/lib/upload.ts index 2be55a35..bc358ede 100644 --- a/lib/upload.ts +++ b/lib/upload.ts @@ -8,24 +8,28 @@ const sharp = require("sharp"); export default async (req, res) => { try { - let { + const { params: { type }, } = req; - let data = await req.file(); + const data = await req.file(); let buf = await data.toBuffer(); - let [format, ext] = (await fileTypeFromBuffer(buf)).mime.split("/"); + const [format, ext] = (await fileTypeFromBuffer(buf)).mime.split("/"); if (format !== "image" && !["jpg", "jpeg", "png"].includes(ext)) fail("unsupported file type"); - let w = type === "banner" ? 1920 : 240; - buf = await sharp(buf, { failOnError: false }).rotate().resize(w).webp().toBuffer(); + const w = type === "banner" ? 1920 : 240; + buf = await sharp(buf, { failOnError: false }) + .rotate() + .resize(w) + .webp() + .toBuffer(); - let hash = createHash("sha256").update(buf).digest("hex"); + const hash = createHash("sha256").update(buf).digest("hex"); - let filePath = `/home/bun/app/data/uploads/${hash}.webp`; + const filePath = `/home/bun/app/data/uploads/${hash}.webp`; writeFileSync(filePath, buf); res.send({ hash }); diff --git a/lib/webhooks.ts b/lib/webhooks.ts index b1f035a4..2a888991 100644 --- a/lib/webhooks.ts +++ b/lib/webhooks.ts @@ -6,13 +6,13 @@ export const callWebhook = async (invoice, payment) => { try { if (!invoice || !payment) return; - let { address, received, text, webhook, secret } = invoice; + const { address, received, text, webhook, secret } = invoice; if (webhook) { - let { amount, confirmed, hash, memo } = payment; + const { amount, confirmed, hash, memo } = payment; l("calling webhook", webhook, amount, hash, address, text); - let res = await got.post(webhook, { + const res = await got.post(webhook, { json: { address, amount, @@ -23,7 +23,7 @@ export const callWebhook = async (invoice, payment) => { text, secret, }, - https: { rejectUnauthorized: false } + https: { rejectUnauthorized: false }, }); return res; } diff --git a/routes/email.ts b/routes/email.ts index 63c88ab0..9f0a8228 100644 --- a/routes/email.ts +++ b/routes/email.ts @@ -8,12 +8,12 @@ import { SendEmailCommand } from "@aws-sdk/client-ses"; export default { async send({ body }, res) { try { - let { email, message, username, token: response } = body; + const { email, message, username, token: response } = body; - let Charset = "UTF-8"; + const Charset = "UTF-8"; - let { recaptcha: secret } = config; - let { success } = await got + const { recaptcha: secret } = config; + const { success } = await got .post("https://www.google.com/recaptcha/api/siteverify", { form: { secret, @@ -23,10 +23,10 @@ export default { .json(); if (success || response === config.adminpass) { - delete body.token; + body.token = undefined; warn("support request from", email); - let client = new SESClient({ region: "us-east-2" }); + const client = new SESClient({ region: "us-east-2" }); await client.send( new SendEmailCommand({ Destination: { @@ -42,7 +42,7 @@ export default { Charset, Data: body.subject || - "Support Request" + (username ? " From " + username : ""), + `Support Request${username ? ` From ${username}` : ""}`, }, }, ReplyToAddresses: [email], diff --git a/routes/info.ts b/routes/info.ts index f2e262f6..09361a01 100644 --- a/routes/info.ts +++ b/routes/info.ts @@ -9,11 +9,11 @@ export default { let total = 0; - for await (let k of db.scanIterator({ MATCH: "balance:*" })) { + for await (const k of db.scanIterator({ MATCH: "balance:*" })) { total += parseInt(await db.get(k)); } - for await (let k of archive.scanIterator({ MATCH: "balance:*" })) { + for await (const k of archive.scanIterator({ MATCH: "balance:*" })) { total += parseInt(await archive.get(k)); } diff --git a/routes/invoices.ts b/routes/invoices.ts index c571f4de..4f0f6feb 100644 --- a/routes/invoices.ts +++ b/routes/invoices.ts @@ -12,7 +12,7 @@ export default { if (typeof invoice === "string") invoice = await g(`invoice:${invoice}`); if (invoice) { - delete invoice.secret; + invoice.secret = undefined; invoice.user = pick(await g(`user:${invoice.uid}`), [ "id", "profile", diff --git a/routes/items.ts b/routes/items.ts index a7586a69..df1d86ea 100644 --- a/routes/items.ts +++ b/routes/items.ts @@ -4,12 +4,12 @@ import { bail, fail } from "$lib/utils"; export default { async list(req, res) { - let { + const { params: { id: uid }, } = req; - let items = []; - for (let id of await db.lRange(`${uid}:items`, 0, -1)) { - let item = await g(`item:${id}`); + const items = []; + for (const id of await db.lRange(`${uid}:items`, 0, -1)) { + const item = await g(`item:${id}`); if (item) items.push(item); else await db.lRem(`${uid}:items`, 0, id); } @@ -18,11 +18,11 @@ export default { }, async get(req, res) { - let { + const { params: { id }, } = req; try { - let item = await g(`item:${id}`); + const item = await g(`item:${id}`); if (!item) fail("Item not found"); res.send(item); } catch (e) { @@ -31,7 +31,7 @@ export default { }, async create(req, res) { - let { + const { body: item, user: { id }, } = req; @@ -53,12 +53,12 @@ export default { }, async del(req, res) { - let { + const { body: { item }, user: { id }, } = req; try { - let n = await db.lRem(`${id}:items`, 0, item.id); + const n = await db.lRem(`${id}:items`, 0, item.id); if (n) db.del(`item:${item.id}`); else fail("item not found"); @@ -69,14 +69,14 @@ export default { }, async sort(req, res) { - let { + const { body: { items }, user: { id }, } = req; try { await db.del(`${id}:items`); - for (let item of items) { + for (const item of items) { await db.rPush(`${id}:items`, item.id); } diff --git a/routes/lnurl.ts b/routes/lnurl.ts index fec516c1..a93e63e4 100644 --- a/routes/lnurl.ts +++ b/routes/lnurl.ts @@ -8,36 +8,36 @@ import { bech32 } from "bech32"; import { types } from "$lib/payments"; import { serverPubkey } from "$lib/nostr"; -let { URL } = process.env; -let host = URL.split("/").at(-1); +const { URL } = process.env; +const host = URL.split("/").at(-1); export default { async encode(req, res) { - let { + const { query: { address }, } = req; - let [name, domain] = address.split("@"); - let url = `https://${domain}/.well-known/lnurlp/${name}`; + const [name, domain] = address.split("@"); + const url = `https://${domain}/.well-known/lnurlp/${name}`; try { - let r = await got(url).json(); + const r = await got(url).json(); if (r.tag !== "payRequest") fail("not an ln address"); } catch (e) { - let m = `failed to lookup lightning address ${address}`; + const m = `failed to lookup lightning address ${address}`; warn(m); return bail(res, m); } - let enc = bech32.encode("lnurl", bech32.toWords(Buffer.from(url)), 20000); + const enc = bech32.encode("lnurl", bech32.toWords(Buffer.from(url)), 20000); res.send(enc); }, async decode(req, res) { - let { + const { query: { text }, } = req; try { - let url = Buffer.from( + const url = Buffer.from( bech32.fromWords(bech32.decode(text, 20000).words), ).toString(); @@ -48,19 +48,19 @@ export default { }, async lnurlp(req, res) { - let { + const { params: { username }, query: { minSendable = 1000, maxSendable = 100000000000 }, } = req; try { - let { id: uid } = await getUser(username); + const { id: uid } = await getUser(username); - let metadata = JSON.stringify([ + const metadata = JSON.stringify([ ["text/plain", `Paying ${username}@${host}`], ["text/identifier", `${username}@${host}`], ]); - let id = v4(); + const id = v4(); await s(`lnurl:${id}`, uid); res.send({ @@ -80,18 +80,18 @@ export default { }, async lnurl(req, res) { - let { + const { params: { id }, query: { amount, comment, nostr }, } = req; try { - let uid = await g(`lnurl:${id}`); - let user = await getUser(uid); + const uid = await g(`lnurl:${id}`); + const user = await getUser(uid); if (!user) fail("user not found"); let { username } = user; username = username.replace(/\s/g, "").toLowerCase(); - let memo = `Paying ${username}@${host}`; + const memo = `Paying ${username}@${host}`; let metadata = JSON.stringify([ ["text/plain", memo], ["text/identifier", `${username}@${host}`], @@ -99,7 +99,7 @@ export default { if (nostr) { try { - let event = JSON.parse(decodeURIComponent(nostr)); + const event = JSON.parse(decodeURIComponent(nostr)); // TODO: validate the event await s(`zap:${id}`, event); metadata = nostr; @@ -108,7 +108,7 @@ export default { } } - let { id: iid, text: pr } = await generate({ + const { id: iid, text: pr } = await generate({ invoice: { amount: Math.round(amount / 1000), memo: metadata, @@ -124,15 +124,15 @@ export default { }, async verify(req, res) { - let { + const { params: { id }, } = req; - let inv = await g(`invoice:${id}`); + const inv = await g(`invoice:${id}`); if (!inv) return res.send({ status: "ERROR", reason: "Not found" }); - let { received, amount, preimage } = inv; - let settled = received >= amount; + const { received, amount, preimage } = inv; + const settled = received >= amount; res.send({ status: "OK", settled, preimage }); }, diff --git a/routes/locations.ts b/routes/locations.ts index 5aaeb02b..d7a5b636 100644 --- a/routes/locations.ts +++ b/routes/locations.ts @@ -2,7 +2,7 @@ import { g } from "$lib/db"; export default { async list(_, res) { - let locations = await g("locations"); + const locations = await g("locations"); res.send({ locations }); }, }; diff --git a/routes/nostr.ts b/routes/nostr.ts index f6caa10d..1ad86baf 100644 --- a/routes/nostr.ts +++ b/routes/nostr.ts @@ -10,17 +10,17 @@ import { pool, } from "$lib/nostr"; -let opts = { maxWait: 2000 }; +const opts = { maxWait: 2000 }; export default { async event(req, res) { - let { + const { params: { id }, } = req; - let event = await g(`ev:${id}`); + const event = await g(`ev:${id}`); if (!event) return bail(res, "event not found"); - let { pubkey } = event; + const { pubkey } = event; event.user = (await g(`user:${pubkey}`)) || anon(pubkey); @@ -29,13 +29,13 @@ export default { async publish(req, res) { try { - let { event } = req.body; - let { pubkey } = req.user; - let { write: relays } = await getRelays(pubkey); + const { event } = req.body; + const { pubkey } = req.user; + const { write: relays } = await getRelays(pubkey); if (!relays.includes(config.nostr)) relays.push(config.nostr); let ok; - for (let url of relays) { + for (const url of relays) { try { await send(event, url); ok = true; @@ -54,7 +54,7 @@ export default { }, async follows(req, res) { - let { + const { params: { pubkey }, query: { pubkeysOnly, nocache }, } = req; @@ -63,7 +63,7 @@ export default { let follows = []; let pubkeys = await g(`${pubkey}:pubkeys`); - if (!pubkeys || !pubkeysOnly || nocache) { + if (!pubkeys || !pubkeysOnly || nocache) { let filter = { kinds: [3], authors: [pubkey] }; let ev = await pool.get([config.nostr], filter, opts); @@ -87,27 +87,27 @@ export default { } if (!pubkeys.length || pubkeysOnly) return res.send(pubkeys); - let cache = await g(`${pubkey}:follows`); + const cache = await g(`${pubkey}:follows`); if (cache && cache.t >= created_at) ({ follows } = cache); else { - let filter: any = { cache: ["user_infos", { pubkeys }] }; - let infos = await pool.querySync([config.cache], filter, opts); - let profiles = infos.filter((e) => e.kind === 0); + const filter: any = { cache: ["user_infos", { pubkeys }] }; + const infos = await pool.querySync([config.cache], filter, opts); + const profiles = infos.filter((e) => e.kind === 0); - let f = infos.find((e) => e.kind === 10000133); + const f = infos.find((e) => e.kind === 10000133); let counts = {}; if (f) counts = JSON.parse(f.content); - for (let p of profiles) { - let { content, pubkey } = p; - let user = JSON.parse(content); + for (const p of profiles) { + const { content, pubkey } = p; + const user = JSON.parse(content); user.count = counts[pubkey]; user.pubkey = pubkey; follows.push(user); } - let followKeys = follows.map((f) => f.pubkey); - let missing = pubkeys + const followKeys = follows.map((f) => f.pubkey); + const missing = pubkeys .filter((p) => !followKeys.includes(p)) .map((pubkey) => anon(pubkey)); @@ -125,34 +125,34 @@ export default { }, async followers(req, res) { - let { + const { params: { pubkey }, } = req; try { let filter: any = { kinds: [3], "#p": [pubkey] }; - let ev = await pool.get([config.nostr], filter, opts); + const ev = await pool.get([config.nostr], filter, opts); let created_at; if (ev) ({ created_at } = ev); let followers = []; - let cache = await g(`${pubkey}:followers`); + const cache = await g(`${pubkey}:followers`); if (cache && cache.t >= created_at) ({ followers } = cache); else { filter = { cache: ["user_followers", { pubkey }] }; - let data = await pool.querySync([config.cache], filter, opts); - for (let ev of data.filter((f) => f.kind === 0)) { - let { content, pubkey } = ev; - let user = JSON.parse(content); + const data = await pool.querySync([config.cache], filter, opts); + for (const ev of data.filter((f) => f.kind === 0)) { + const { content, pubkey } = ev; + const user = JSON.parse(content); user.pubkey = pubkey; followers.push(user); } - let pubkeys = followers.map((f) => f.pubkey); + const pubkeys = followers.map((f) => f.pubkey); filter = { cache: ["user_infos", { pubkeys }] }; - let infos = await pool.querySync([config.cache], filter, opts); + const infos = await pool.querySync([config.cache], filter, opts); let counts = {}; - let f = infos.find((e) => e.kind === 10000133); + const f = infos.find((e) => e.kind === 10000133); if (f) counts = JSON.parse(f.content); followers.map((f) => (f.count = counts[f.pubkey])); followers.sort((a: any, b: any) => b.count - a.count); @@ -169,7 +169,7 @@ export default { async count(req, res) { try { - let { pubkey } = req.params; + const { pubkey } = req.params; res.send(await getCount(pubkey)); } catch (e) { console.log(e); @@ -178,16 +178,16 @@ export default { }, async identities(req, res) { - let { + const { query: { name }, } = req; let names = {}; if (name) { names = { [name]: (await getUser(name)).pubkey }; } else { - let records = await db.sMembers("nip5"); - for (let s of records) { - let [name, pubkey] = s.split(":"); + const records = await db.sMembers("nip5"); + for (const s of records) { + const [name, pubkey] = s.split(":"); names[name] = pubkey; } } diff --git a/routes/payments.ts b/routes/payments.ts index 618e9195..21720e17 100644 --- a/routes/payments.ts +++ b/routes/payments.ts @@ -368,7 +368,7 @@ export default { ); let msg = e.message; if (msg.includes("500")) msg = ""; - bail(res, "Failed to prepare transaction " + msg); + bail(res, `Failed to prepare transaction ${msg}`); } }, diff --git a/routes/rates.ts b/routes/rates.ts index e904d438..67c111d7 100644 --- a/routes/rates.ts +++ b/routes/rates.ts @@ -3,7 +3,7 @@ import { rate } from "$lib/rates"; export default { async fx(_, res) { - let { fx } = await g("fx"); + const { fx } = await g("fx"); res.send({ fx }); }, diff --git a/routes/shopify.ts b/routes/shopify.ts index f5c26208..33592705 100644 --- a/routes/shopify.ts +++ b/routes/shopify.ts @@ -3,7 +3,7 @@ import { g } from "$lib/db"; import { err } from "$lib/logging"; import { getPayment } from "$lib/utils"; -let query = `mutation orderMarkAsPaid($input: OrderMarkAsPaidInput!) { +const query = `mutation orderMarkAsPaid($input: OrderMarkAsPaidInput!) { orderMarkAsPaid(input: $input) { order { id } userErrors { @@ -14,15 +14,15 @@ let query = `mutation orderMarkAsPaid($input: OrderMarkAsPaidInput!) { }`; export default async (req, res) => { - let { + const { body: { hash }, params: { id }, } = req; - let p = await getPayment(hash); - let user = await g(`user:${p.uid}`); + const p = await getPayment(hash); + const user = await g(`user:${p.uid}`); try { - let r = await got + const r = await got .post( `https://${user.shopifyStore}.myshopify.com/admin/api/2023-07/graphql.json`, { diff --git a/routes/users.ts b/routes/users.ts index 8bbef621..c1cce6e7 100644 --- a/routes/users.ts +++ b/routes/users.ts @@ -89,14 +89,14 @@ export default { if (nostr) { nostr.username = nostr.name || key.substr(0, 6); nostr.display = nostr.display_name || nostr.displayName; - delete nostr.display_name; - delete nostr.displayName; - delete nostr.name; + nostr.display_name = undefined; + nostr.displayName = undefined; + nostr.name = undefined; } if (user) { user.anon = false; - delete nostr.display; + nostr.display = undefined; } user = { @@ -211,12 +211,13 @@ export default { if (user.pin && !(pin === user.pin)) fail("Pin required"); if (typeof newpin !== "undefined" && newpin.length === 6) user.pin = newpin; - if (user.pin === "delete") delete user.pin; + if (user.pin === "delete") user.pin = undefined; let exists; if (username) exists = await getUser(username); - let token, oldname; + let token; + let oldname; if (user.username.toLowerCase() !== username.toLowerCase() && exists) { err("username taken", username, user.username, exists.username); fail("Username taken"); @@ -444,7 +445,7 @@ export default { headers: { authorization }, } = req; username = username.toLowerCase(); - if (!(authorization && authorization.includes(config.admin))) + if (!authorization?.includes(config.admin)) return res.code(401).send("unauthorized"); const { id, pubkey } = await g( @@ -467,8 +468,9 @@ export default { body: { code, username, password }, user: u, } = req; - const admin = u && u.admin; - let id, user; + const admin = u?.admin; + let id; + let user; if (admin) { id = await g(`user:${username.toLowerCase().replace(/\s/g, "")}`); |
