aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--biome.json2
-rwxr-xr-xbun.lockbbin208929 -> 208567 bytes
-rw-r--r--index.ts3
-rw-r--r--lib/mqtt.ts3
-rw-r--r--lib/notifications.ts89
-rw-r--r--lib/payments.ts74
-rw-r--r--lib/utils.ts8
-rw-r--r--lib/whitelist.ts1
-rw-r--r--package.json2
-rw-r--r--routes/payments.ts12
-rw-r--r--routes/users.ts38
11 files changed, 113 insertions, 119 deletions
diff --git a/biome.json b/biome.json
index 914646d2..d6693595 100644
--- a/biome.json
+++ b/biome.json
@@ -5,6 +5,6 @@
"vcs": {
"enabled": true,
"clientKind": "git",
- "useIgnoreFile": true
+ "useIgnoreFile": false
}
}
diff --git a/bun.lockb b/bun.lockb
index 3d5e4d09..60dbc3b3 100755
--- a/bun.lockb
+++ b/bun.lockb
Binary files differ
diff --git a/index.ts b/index.ts
index bec345ad..21c29250 100644
--- a/index.ts
+++ b/index.ts
@@ -102,7 +102,8 @@ app.post("/request", auth, users.request);
app.post("/forgot", users.forgot);
app.post("/login", users.login);
-app.post("/subscribe", auth, users.subscribe);
+app.post("/subscription", auth, users.subscription);
+app.post("/subscription/delete", auth, users.deleteSubscription);
app.post("/password", auth, users.password);
app.post("/pin", auth, users.pin);
app.post("/otpsecret", auth, users.otpsecret);
diff --git a/lib/mqtt.ts b/lib/mqtt.ts
index 636dc3e1..4212989d 100644
--- a/lib/mqtt.ts
+++ b/lib/mqtt.ts
@@ -1,5 +1,4 @@
import mqtt from "mqtt";
import config from "$config";
-export let mqtt1 = mqtt.connect("mqtt://ln.coinos.io", config.mqtt1);
-export let mqtt2 = mqtt.connect("mqtt://mqtt.coinos.io", config.mqtt2);
+export default mqtt.connect("mqtt://mqtt.coinos.io", config.mqtt);
diff --git a/lib/notifications.ts b/lib/notifications.ts
index af47a6d7..023fdeff 100644
--- a/lib/notifications.ts
+++ b/lib/notifications.ts
@@ -1,38 +1,77 @@
+import { emit } from "$lib/sockets";
+import { err, warn } from "$lib/logging";
+import { db } from "$lib/db";
+import mqtt from "$lib/mqtt";
import config from "$config";
+import { mail, templates } from "$lib/mail";
+import { f, fmt, link, fiat, t } from "$lib/utils";
import webpush from "web-push";
if (config.vapid) {
webpush.setVapidDetails(
- config.vapid.url,
- config.vapid.publicKey,
- config.vapid.privateKey,
+ `mailto:${config.support}`,
+ config.vapid.pk,
+ config.vapid.sk,
);
}
-export const notify = async (user, payload) => {
- try {
- let { subscriptions } = user;
+export const notify = async (p, user, withdrawal) => {
+ emit(user.id, "payment", p);
+ let { username } = user;
+ let { paymentReceived } = t(user);
- if (subscriptions) {
- let i = 0;
- for (let subscription of subscriptions) {
- i++;
- try {
- await webpush.sendNotification(subscription, payload);
- } catch (e) {
- if (
- e.body &&
- (e.body.includes("expired") || e.body.includes("No such"))
- ) {
- user.subscriptions.splice(i, 1);
- await user.save();
- } else {
- err("problem sending notification", e);
- }
- }
- }
+ try {
+ if (user.verified && user.notify) {
+ mail(user, paymentReceived, templates.paymentReceived, {
+ ...t(user),
+ username,
+ payment: {
+ amount: fmt(p.amount),
+ link: link(p.id),
+ tip: p.tip ? fmt(p.tip) : undefined,
+ fiat: f(fiat(p.amount, p.rate), p.currency),
+ fiatTip: p.tip ? f(fiat(p.tip, p.rate), p.currency) : undefined,
+ memo: p.memo,
+ items:
+ p.items &&
+ p.items.map((i) => {
+ return {
+ quantity: i.quantity,
+ name: i.name,
+ total: i.quantity * i.price,
+ totalFiat: f(i.quantity * i.price, p.currency),
+ };
+ }),
+ },
+ withdrawal,
+ });
}
} catch (e) {
- console.log("problem notifying", e);
+ err("problem emailing", e.message);
+ }
+
+ let subscriptions = await db.sMembers(`${user.id}:subscriptions`);
+
+ let payload = {
+ title: paymentReceived,
+ body: `${fmt(p.amount)} ${f(fiat(p.amount, p.rate), p.currency)}`,
+ url: `/payment/${p.id}`,
+ };
+
+ for (let s of subscriptions) {
+ webpush
+ .sendNotification(JSON.parse(s), JSON.stringify(payload))
+ .catch((e) => {
+ warn("sub failed", e.message);
+ db.sRem(`${user.id}:subscriptions`, s);
+ });
+ }
+
+ if (config.mqtt) {
+ if (!mqtt.connected) await mqtt.reconnect();
+ mqtt.publish(
+ username,
+ `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}:${p.memo}:${p.items}`,
+ );
}
};
diff --git a/lib/payments.ts b/lib/payments.ts
index cc55e5ce..aeb16aa3 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -6,10 +6,10 @@ import { notify } from "$lib/notifications";
import { db, g, s } from "$lib/db";
import { l, err, warn } from "$lib/logging";
import {
- f,
btc,
fail,
- fiat,
+ fmt,
+ link,
getInvoice,
getPayment,
getUser,
@@ -21,18 +21,14 @@ import {
} from "$lib/utils";
import { callWebhook } from "$lib/webhooks";
import got from "got";
-import { mqtt1, mqtt2 } from "$lib/mqtt";
-import { mail, templates } from "$lib/mail";
import api from "$lib/api";
import { bech32 } from "bech32";
import rpc from "@coinos/rpc";
-
import ln from "$lib/ln";
let bc = rpc(config.bitcoin);
let lq = rpc(config.liquid);
-let { URL } = process.env;
let dust = 547;
export let types = {
@@ -228,49 +224,18 @@ export let credit = async ({
.incrBy(`${balanceKey}:${aid || uid}`, amount)
.exec();
- emit(uid, "payment", p);
- notify(user, `Received ${amount} sats`);
- callWebhook(inv, p);
- l(username, "received", type, amount);
-
- let items;
if (inv.items && inv.items.length) {
formatReceipt(inv.items, inv.currency);
p.items = inv.items;
}
- if (config.mqtt1) {
- if (!mqtt1.connected) await mqtt1.reconnect();
- mqtt1.publish(
- username,
- `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}:${p.memo}:${items}`,
- );
- }
-
- if (config.mqtt2) {
- if (!mqtt2.connected) await mqtt2.reconnect();
- mqtt2.publish(
- username,
- `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}:${p.memo}:${items}`,
- );
- }
-
- try {
- await completePayment(p, user);
- } catch (e) {
- console.log(e);
- }
+ await completePayment(inv, p, user);
return p;
};
-let fmt = (sats) =>
- "⚡️" +
- new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(sats);
-
-export let completePayment = async (p, user) => {
+export let completePayment = async (inv, p, user) => {
let { id, autowithdraw, threshold, reserve, destination, username } = user;
- let link = (id) => `${URL}/payment/${id}`;
if (p.confirmed) {
let withdrawal;
if (autowithdraw) {
@@ -292,33 +257,12 @@ export let completePayment = async (p, user) => {
}
}
- let { paymentReceived } = t(user);
- if (user.verified && user.notify) {
- mail(user, paymentReceived, templates.paymentReceived, {
- ...t(user),
- username,
- payment: {
- amount: fmt(p.amount),
- link: link(p.id),
- tip: p.tip ? fmt(p.tip) : undefined,
- fiat: f(fiat(p.amount, p.rate), p.currency),
- fiatTip: p.tip ? f(fiat(p.tip, p.rate), p.currency) : undefined,
- memo: p.memo,
- items:
- p.items &&
- p.items.map((i) => {
- return {
- quantity: i.quantity,
- name: i.name,
- total: i.quantity * i.price,
- totalFiat: f(i.quantity * i.price, p.currency),
- };
- }),
- },
- withdrawal,
- });
- }
+ notify(p, user, withdrawal);
}
+
+ l(username, "received", p.type, p.amount);
+ emit(user.id, "payment", p);
+ callWebhook(inv, p);
};
let pay = async ({ aid = undefined, amount, to, user }) => {
diff --git a/lib/utils.ts b/lib/utils.ts
index b7f55ff1..fa10fb81 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -5,6 +5,8 @@ import { g, s } from "$lib/db";
import config from "$config";
import { getPublicKey } from "nostr";
+ let { URL } = process.env;
+
export let fail = (msg) => {
throw new Error(msg);
};
@@ -180,3 +182,9 @@ export let time = (() => {
console.timeLog("", ++count, s);
};
})();
+
+export let fmt = (sats) =>
+ "⚡️" +
+ new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(sats);
+
+export let link = (id) => `${URL}/payment/${id}`;
diff --git a/lib/whitelist.ts b/lib/whitelist.ts
index 54d3aafc..b0975d2c 100644
--- a/lib/whitelist.ts
+++ b/lib/whitelist.ts
@@ -33,6 +33,7 @@ export default [
"profile",
"prompt",
"pubkey",
+ "push",
"reserve",
"salt",
"seed",
diff --git a/package.json b/package.json
index 50c6a0f4..9fc9519e 100644
--- a/package.json
+++ b/package.json
@@ -44,7 +44,7 @@
"redis": "^4.6.10",
"sharp": "^0.33.5",
"uuid": "8",
- "web-push": "^3.4.4",
+ "web-push": "^3.6.7",
"ws": "^7.4.6"
},
"devDependencies": {
diff --git a/routes/payments.ts b/routes/payments.ts
index 33f66e8d..f69123ac 100644
--- a/routes/payments.ts
+++ b/routes/payments.ts
@@ -24,7 +24,7 @@ import {
sendLightning,
sendOnchain,
} from "$lib/payments";
-import { mqtt1, mqtt2 } from "$lib/mqtt";
+import mqtt from "$lib/mqtt";
import got from "got";
import api from "$lib/api";
import rpc from "@coinos/rpc";
@@ -334,9 +334,8 @@ export default {
.incrBy(`balance:${p.aid || p.uid}`, p.amount)
.exec();
- emit(p.uid, "payment", p);
let user = await g(`user:${p.uid}`);
- await completePayment(p, user);
+ await completePayment(invoice, p, user);
}
}
res.send({});
@@ -407,12 +406,7 @@ export default {
let { username } = user;
- mqtt1.publish(
- username,
- `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
- );
-
- mqtt2.publish(
+ mqtt.publish(
username,
`pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
);
diff --git a/routes/users.ts b/routes/users.ts
index 5f6f0747..deeca4ea 100644
--- a/routes/users.ts
+++ b/routes/users.ts
@@ -256,6 +256,7 @@ export default {
"prompt",
"profile",
"pubkey",
+ "push",
"reserve",
"salt",
"seed",
@@ -340,21 +341,28 @@ export default {
}
},
- async subscribe(req, res) {
- let { body, user } = req;
- let { subscriptions } = user;
- let { subscription } = body;
- if (!subscriptions) subscriptions = [];
- if (
- !subscriptions.find(
- (s) => JSON.stringify(s) === JSON.stringify(subscription),
- )
- )
- subscriptions.push(subscription);
- user.subscriptions = subscriptions;
- l("subscribing", user.username);
- await user.save();
- res.sendStatus(201);
+ async subscription(req, res) {
+ try {
+ let { subscription } = req.body;
+ let { id } = req.user;
+ await db.sAdd(`${id}:subscriptions`, JSON.stringify(subscription));
+ res.send(subscription);
+ } catch (e) {
+ console.log(e);
+ bail(res, e.message);
+ }
+ },
+
+ async deleteSubscription(req, res) {
+ try {
+ let { subscription } = req.body;
+ let { id } = req.user;
+ await db.sRem(`${id}:subscriptions`, JSON.stringify(subscription));
+ res.send(subscription);
+ } catch (e) {
+ console.log(e);
+ bail(res, e.message);
+ }
},
async password(req, res) {