aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docker-compose.yml13
-rw-r--r--index.js30
-rw-r--r--lib/nostr.js1
-rw-r--r--lib/passport.js5
-rw-r--r--lib/register.js15
-rw-r--r--lib/sockets.js26
-rw-r--r--lib/utils.js21
-rw-r--r--lib/whitelist.js1
-rw-r--r--package.json2
-rw-r--r--routes/invoices.js49
-rw-r--r--routes/lightning/receive.js161
-rw-r--r--routes/users.js26
12 files changed, 135 insertions, 215 deletions
diff --git a/docker-compose.yml b/docker-compose.yml
index 95516fe0..83214b92 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -7,7 +7,6 @@ services:
- rd
- bc
- cl
- build: .
command: "pnpm dev"
environment:
NODE_OPTIONS: "--es-module-specifier-resolution=node --experimental-modules --loader ./loader.js"
@@ -32,8 +31,16 @@ services:
volumes:
- ./data/lightning:/root/.lightning
restart: always
+ clb:
+ image: elementsproject/lightningd:latest
+ container_name: clb
+ environment:
+ LIGHTNINGD_NETWORK: regtest
+ volumes:
+ - ./data/lightningb:/root/.lightning
+ restart: always
rd:
- image: redis
+ image: eqalpha/keydb
container_name: rd
volumes:
- ./data/redis:/data
@@ -45,6 +52,6 @@ services:
user: 100:100
volumes:
- "./data/nostr/data:/usr/src/app/db"
- - "./data/nostr/data.toml:/usr/src/app/config.toml"
+ - "./data/nostr/config.toml:/usr/src/app/config.toml"
ports:
- '8080:8080'
diff --git a/index.js b/index.js
index 580435e0..640f7389 100644
--- a/index.js
+++ b/index.js
@@ -1,20 +1,20 @@
-import app from "./app";
-import { l, err } from "./lib/logging";
+import app from "./app.js";
+import { l, err } from "$lib/logging";
-import "./lib/redis";
-import "./lib/sockets";
-import "./lib/passport";
-import "./lib/upload";
-import "./lib/rates";
-import "./lib/notifications";
-import "./lib/mail";
-import "./lib/nostr";
+import "$lib/redis";
+import "$lib/sockets";
+import "$lib/passport";
+import "$lib/upload";
+import "$lib/rates";
+import "$lib/notifications";
+import "$lib/mail";
+import "$lib/nostr";
-import "./routes/locations";
-import "./routes/invoices";
-import "./routes/payments";
-import "./routes/users";
-import "./routes/requests";
+import "$routes/locations";
+import "$routes/invoices";
+import "$routes/payments";
+import "$routes/users";
+import "$routes/requests";
let host = process.env.HOST || "0.0.0.0";
let port = process.env.PORT || 3119;
diff --git a/lib/nostr.js b/lib/nostr.js
index ad089bb5..08ed8b61 100644
--- a/lib/nostr.js
+++ b/lib/nostr.js
@@ -142,7 +142,6 @@ pool.on("event", async (relay, sub, ev) => {
};
await redis.set(`user:${pubkey}`, JSON.stringify(user));
- await redis.set(`user:${user.username}`, JSON.stringify(user));
coinos.send(ev);
} else if (sub.includes("follows")) {
let pubkey = sub.split(":")[0];
diff --git a/lib/passport.js b/lib/passport.js
index b559b2f9..25dc0103 100644
--- a/lib/passport.js
+++ b/lib/passport.js
@@ -34,8 +34,9 @@ fastifyPassport.use(
},
async (payload, next) => {
try {
- let { uuid } = payload;
- let user = await g(`user:${uuid}`);
+ let { id } = payload;
+ let user = await g(`user:${id}`);
+ user.haspin = !!user.pin;
next(null, user);
} catch (e) {
console.log("payload", payload);
diff --git a/lib/register.js b/lib/register.js
index 9f18c15e..8ac5355f 100644
--- a/lib/register.js
+++ b/lib/register.js
@@ -14,8 +14,8 @@ export default async (user, ip, requireChallenge) => {
if (!username) throw new Error("Username required");
username = username.replace(/ /g, "");
- let uuid = v4();
- user.uuid = uuid;
+ let id = v4();
+ user.id = id;
let exists = await g(`user:${username}`);
if (exists) throw new Error(`Username ${username} taken`);
@@ -24,8 +24,6 @@ export default async (user, ip, requireChallenge) => {
user.password = await bcrypt.hash(password, 1);
}
- console.log("BOO");
-
if (!config.ipxapi || ip.startsWith("127") || ip.startsWith("192")) {
user.currency = "CAD";
} else {
@@ -53,14 +51,9 @@ export default async (user, ip, requireChallenge) => {
user.ip = ip;
}
- console.log("user uuid", user.uuid, user.username);
+ await s(`user:${id}`, user);
+ await s(`user:${user.username}`, id);
- try {
- await s(`user:${user.uuid}`, user);
- await s(`user:${user.username}`, user);
- } catch (e) {
- console.log("HUH", e);
- }
l("new user", user.username);
return user;
};
diff --git a/lib/sockets.js b/lib/sockets.js
index 0f343937..c94c29a0 100644
--- a/lib/sockets.js
+++ b/lib/sockets.js
@@ -8,7 +8,7 @@ import { differenceInSeconds, subSeconds } from "date-fns";
import fastifyWs from "@fastify/websocket";
import whitelist from "./whitelist";
import { l, err, warn } from "$lib/logging";
-import { getUser } from "$lib/utils";
+import { g } from "$lib/redis";
const code = 1000;
@@ -92,24 +92,24 @@ app.register(async function(app) {
if (!token || token === "null" || !jwt.decode(token))
return ws.close(code, `bad token ${token}`);
- let { uuid } = jwt.decode(token);
- if (!uuid) throw new Error("Invalid JWT token");
- console.log("UUID", uuid)
- let user = await getUser(uuid);
+ let { id: user_id } = jwt.decode(token);
+ if (!user_id) throw new Error("Invalid JWT token");
+ let user = await g(`user:${user_id}`);
if (!user) throw new Error("User not found");
+ user.haspin = !!user.pin;
- if (!store.sockets[uuid]) store.sockets[uuid] = {};
- const existing = Object.keys(store.sockets[uuid]);
+ if (!store.sockets[user_id]) store.sockets[user_id] = {};
+ const existing = Object.keys(store.sockets[user_id]);
if (existing.length > 4) {
- const p = existing.find(sid => sid !== id);
- store.sockets[uuid][p].send(JSON.stringify({ type: "logout" }));
+ const p = existing.find(sid => sid !== user_id);
+ store.sockets[user_id][p].send(JSON.stringify({ type: "logout" }));
setTimeout(() => {
- if (store.sockets[uuid][p])
- store.sockets[uuid][p].close(code, "too many sockets");
+ if (store.sockets[user_id][p])
+ store.sockets[user_id][p].close(code, "too many sockets");
}, 5000);
}
- store.sockets[uuid][id] = ws;
- names[id] = uuid;
+ store.sockets[user_id][id] = ws;
+ names[id] = user_id;
ws.user = user;
ws.send(
diff --git a/lib/utils.js b/lib/utils.js
index 559f8531..b556c46a 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -1,7 +1,6 @@
import store from "$lib/store";
import { g } from "$lib/redis";
import config from "$config";
-import ln from "$lib/ln";
export const nada = () => {};
@@ -18,15 +17,6 @@ export const requirePin = async ({ body, user }) => {
throw new Error("Invalid pin");
};
-export const getUser = async (uuid) => {
- let user = g(`user:${uuid}`);
- if (!user) throw new Error("user not found");
-
- user.haspin = !!user.pin;
-
- return user;
-};
-
export const prod = process.env.NODE_ENV === "production";
export const fail = msg => {
throw new Error(msg);
@@ -35,14 +25,5 @@ export const fail = msg => {
export const SATS = 100000000;
export const toSats = n => Math.round(n * SATS);
-export const derivePayRequest = async ({ amount, memo, tip }) => {
- if (!tip) tip = 0;
- let value = amount + tip;
-
- if (!memo) memo = "coinos";
- return (
- await ln.invoice(value ? `${value}sat` : "any", new Date(), memo, 360)
- ).bolt11;
-};
-
export const uniq = (a, k) => [...new Map(a.map(x => [k(x), x])).values()];
+export const pick = (O, K) => K.reduce((o, k) => ((o[k] = O[k]), o), {});
diff --git a/lib/whitelist.js b/lib/whitelist.js
index 523e9d37..49cc9af3 100644
--- a/lib/whitelist.js
+++ b/lib/whitelist.js
@@ -25,7 +25,6 @@ export default [
"symbol",
"theme",
"twofa",
- "uuid",
"unit",
"username",
"verified"
diff --git a/package.json b/package.json
index 408df40e..2c1b7258 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"version": "0.0.1",
"private": true,
"scripts": {
- "start": "bun index.js",
+ "start": "node index.js",
"dev": "nodemon index.js"
},
"dependencies": {
diff --git a/routes/invoices.js b/routes/invoices.js
index ddd840ec..f7ab78cf 100644
--- a/routes/invoices.js
+++ b/routes/invoices.js
@@ -1,76 +1,67 @@
import app from "$app";
import store from "$lib/store";
import { optionalAuth } from "$lib/passport";
-import { err, l } from "$lib/logging";
-import { SATS, derivePayRequest } from "$lib/utils";
+import { l } from "$lib/logging";
import { emit } from "$lib/sockets";
-import { v4 } from "uuid";
import { rd, g, s } from "$lib/redis";
+import ln from "$lib/ln";
app.get("/invoice", async ({ query: { id } }, res) => {
let invoice = await g(`invoice:${id}`);
invoice.user = await g(`user:${invoice.user_id}`);
+ console.log("INV", invoice)
res.send(invoice);
});
app.post(
"/invoice",
optionalAuth,
- async (
- {
- body: { type = "bech32", liquidAddress, id, invoice, user, tx },
- user: me
- },
- res
- ) => {
- let {
- blindkey,
- currency,
- tip,
- amount,
- rate,
- network,
- request_id
- } = invoice;
- let text;
+ async ({ body: { invoice, user }, user: me }, res) => {
+ let { currency, tip, amount, rate, request_id } = invoice;
if (amount < 0) throw new Error("amount out of range");
if (tip > 5 * amount || tip < 0) throw new Error("tip amount out of range");
tip = tip || 0;
if (!user) user = me;
- if (!user) throw new Error("user not provided");
+ let user_id = await g(`user:${user.username}`);
+ user = await g(`user:${user_id}`);
- user = await g(`user:${user.id}`);
+ if (!user) throw new Error("user not provided");
if (!currency) currency = user.currency;
if (!rate) rate = store.rates[currency];
if (amount < 0) throw new Error("invalid amount");
+ let { payment_hash: id, bolt11: text } = await ln.invoice(
+ amount ? `${amount + tip}sat` : "any",
+ new Date(),
+ "",
+ 3600
+ );
+
invoice = {
...invoice,
- id: v4(),
+ id,
amount,
currency,
rate,
tip,
- unconfidential,
- user_id: user.id
+ user_id,
+ text
};
- invoice.text = await derivePayRequest(invoice);
-
l(
"creating invoice",
user.username,
- network,
amount,
tip,
currency,
`${text.substr(0, 8)}..${text.substr(-6)}`
);
- await rd.lPush(`${id}:invoices`, invoice.id);
+ await s(`invoice:${id}`, invoice);
+ await rd.lPush(`${user.id}:invoices`, id);
if (request_id) {
let request = await g(`request:${request_id}`);
diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js
index a778271b..a37dc716 100644
--- a/routes/lightning/receive.js
+++ b/routes/lightning/receive.js
@@ -6,132 +6,79 @@ import { callWebhook } from "$lib/webhooks";
import { computeConversionFee } from "./conversionFee";
import ln from "$lib/ln";
import { l, err, warn } from "$lib/logging";
+import { g, s, rd } from "$lib/redis";
-const handlePayment = async msg => {
- try {
- l("incoming lightning payment", msg.received);
-
- if (!msg.secret) return;
- let account, total, user;
-
- const invoice = await db.Invoice.findOne({
- where: {
- text: msg.request
- }
- });
+let handle = async () => {
+ let {
+ payment_hash,
+ bolt11: request,
+ pay_index,
+ status,
+ msatoshi_received,
+ payment_preimage: secret
+ } = await ln.waitanyinvoice(await g("pay_index"));
- if (!invoice)
- return warn("received lightning with no invoice", msg.request);
+ await s("pay_index", pay_index);
- await db.transaction(async transaction => {
- const { text: hash, currency, memo, rate, tip, user_id } = invoice;
- const amount = parseInt(msg.received) - tip;
- if (amount < 0) throw new Error("amount out of range");
+ let received = Math.round(msatoshi_received / 1000);
- account = await db.Account.findOne({
- where: {
- user_id,
- asset: config.liquid.btcasset,
- pubkey: null
- },
- include: {
- model: db.User,
- as: "user"
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
+ try {
+ l("incoming lightning payment", received);
- ({ user } = account);
+ if (!secret) return;
+ let account, total, user;
- let preimage = msg.secret;
+ let invoice = await g(`invoice:${payment_hash}`);
+ if (!invoice) return warn("received lightning with no invoice", request);
- let payment = await db.Payment.create(
- {
- account_id: account.id,
- user_id,
- hash,
- memo,
- amount,
- currency,
- preimage,
- rate,
- received: true,
- confirmed: true,
- network: "lightning",
- tip,
- invoice_id: invoice.id
- },
- { transaction }
- );
+ let { text: hash, currency, memo, rate, tip, user_id } = invoice;
+ let amount = parseInt(received) - tip;
+ if (amount < 0) throw new Error("amount out of range");
- total = amount + tip;
+ let payment = {
+ user_id,
+ hash,
+ memo,
+ amount,
+ currency,
+ secret,
+ rate,
+ tip,
+ invoice_id: invoice.id
+ };
- invoice.status = "paid";
- await invoice.save({ transaction });
+ total = amount + tip;
- await invoice.increment({ received: total }, { transaction });
- await account.increment({ balance: total }, { transaction });
- // get the # of fee credits you would need to pay off this amount of bitcoin
- await account.increment(
- { lightning_credits: computeConversionFee(total) },
- { transaction }
- );
- await account.reload({ transaction });
- await invoice.reload({ transaction });
- await payment.save({ transaction });
- store.payments.push(msg.request);
+ invoice.status = "paid";
+ invoice.received += total;
+ await s(`invoice:${payment_hash}`, invoice);
- payment = payment.get({ plain: true });
- payment.account = account.get({ plain: true });
- payment.invoice = invoice.get({ plain: true });
+ let t = async () => {
+ await rd.watch(`user:${user_id}`);
+ user = await g(`user:${user_id}`);
+ user.balance += total;
+ let m = await rd.multi();
+ await s(`user:${user.id}`, user);
+ if (!(await m.exec())) t();
+ };
+ await t();
- callWebhook(invoice, payment);
+ payment.invoice = invoice;
+ callWebhook(invoice, payment);
- emit(user.username, "payment", payment);
- emit(user.username, "account", payment.account);
- notify(user, `Received ${total} SAT`);
+ emit(user.username, "payment", payment);
+ notify(user, `Received ${total} SAT`);
- l(
- "lightning payment received",
- user.username,
- payment.amount,
- payment.tip
- );
- });
+ l("lightning payment received", user.username, payment.amount, payment.tip);
} catch (e) {
console.log(e);
err("problem receiving lightning payment", e.message);
}
};
-const poll = async ln => {
- try {
- const wait = async i => {
- const {
- bolt11: request,
- pay_index,
- status,
- msatoshi_received,
- payment_preimage: secret
- } = await ln.waitanyinvoice(i);
-
- let settled = status === "paid";
- let received = parseInt(msatoshi_received / 1000);
-
- await handlePayment({
- request,
- received,
- secret
- });
- await wait(pay_index);
- };
-
- const { invoices } = await ln.listinvoices();
- await wait(Math.max(...invoices.map(i => i.pay_index).filter(n => n)));
- } catch (e) {
- err("problem waiting for c-lightning invoice", e.message);
- }
+let listen = async () => {
+ await handle();
+ listen();
};
-poll(ln);
+listen();
diff --git a/routes/users.js b/routes/users.js
index 8f0b9106..97ff9dd4 100644
--- a/routes/users.js
+++ b/routes/users.js
@@ -3,7 +3,7 @@ import { g, s } from "$lib/redis";
import config from "$config";
import store from "$lib/store";
import { auth, optionalAuth } from "$lib/passport";
-import { getUser, nada, uniq, wait } from "$lib/utils";
+import { nada, pick, uniq, wait } from "$lib/utils";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { authenticator } from "otplib";
@@ -14,11 +14,9 @@ import register from "$lib/register";
import { requirePin } from "$lib/utils";
import got from "got";
-const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {});
-
app.get("/me", auth, async (req, res) => {
try {
- res.send(pick(req.user, ...whitelist));
+ res.send(pick(req.user, whitelist));
} catch (e) {
console.log("problem fetching user", e);
res.code(500).send(e.message);
@@ -32,6 +30,9 @@ app.get("/users/:key", async ({ params: { key } }, res) => {
}
let user = await g(`user:${key}`);
+ if (typeof user === "string") {
+ user = await g(`user:${user}`);
+ }
if (!user && key.length === 64) {
user = {
@@ -41,7 +42,6 @@ app.get("/users/:key", async ({ params: { key } }, res) => {
};
}
- console.log(user)
if (!user) return res.code(500).send("User not found");
let whitelist = [
@@ -53,10 +53,10 @@ app.get("/users/:key", async ({ params: { key } }, res) => {
"pubkey",
"display",
"prompt",
- "uuid"
+ "id"
];
- res.send(pick(user, ...whitelist));
+ res.send(pick(user, whitelist));
} catch (e) {
console.log(e);
res.code(500).send(e.message);
@@ -78,7 +78,7 @@ app.post("/register", async (req, res) => {
user = await register(user, ip, false);
l("registered new user", username);
- res.send(pick(user, ...whitelist));
+ res.send(pick(user, whitelist));
} catch (e) {
res.code(500).send(e.message);
}
@@ -308,7 +308,8 @@ let login = async (req, res) => {
return;
}
- let user = await getUser(username);
+ let id = await g(`user:${username}`);
+ let user = await g(`user:${id}`);
if (
!user ||
@@ -332,10 +333,11 @@ let login = async (req, res) => {
req.headers["x-forwarded-for"] || req.socket.remoteAddress
);
- let payload = { username, uuid: user.uuid };
+ let payload = { username, id: user.id };
let token = jwt.sign(payload, config.jwt);
res.cookie("token", token, { expires: new Date(Date.now() + 432000000) });
- user = pick(user, ...whitelist);
+ user = pick(user, whitelist);
+ console.log("SENDING", user, token)
res.send({ user, token });
} catch (e) {
console.log(e);
@@ -413,7 +415,7 @@ app.post("/otpsecret", auth, async function(req, res) {
});
app.get("/contacts", auth, async function({ user: { uuid } }, res) {
- let payments = await g(`${uuid}:payments`) || [];
+ let payments = (await g(`${uuid}:payments`)) || [];
res.send(
Promise.all(
[...new Set(payments.map(p => p.with_id))].map(id => g(`user:${id}`))