aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2023-01-16 23:40:25 -0800
committerAdam Soltys <[email protected]>2023-01-16 23:40:25 -0800
commit91e847126c832efa25ab0bf8d6f3b7e537378fa7 (patch)
tree4a42b6e3bec6eb0f640ed44533b54e6d3d6a5e0e /lib
parent6bafea9bffe24d2227d269812a73a9a513d92ddf (diff)
downloadcoinos-server-91e847126c832efa25ab0bf8d6f3b7e537378fa7.tar.gz
coinos-server-91e847126c832efa25ab0bf8d6f3b7e537378fa7.zip
shuffle things around
Diffstat (limited to 'lib')
-rw-r--r--lib/auth.js37
-rw-r--r--lib/binance.js33
-rw-r--r--lib/db.js14
-rw-r--r--lib/lightning.js0
-rw-r--r--lib/locations.js20
-rw-r--r--lib/mail.js29
-rw-r--r--lib/nostr.js80
-rw-r--r--lib/passport.js79
-rw-r--r--lib/rates.js87
-rw-r--r--lib/redis.js15
-rw-r--r--lib/register.js2
-rw-r--r--lib/send.js201
-rw-r--r--lib/sendInternal.js134
-rw-r--r--lib/sockets.js93
-rw-r--r--lib/store.js1
-rw-r--r--lib/upload.js36
-rw-r--r--lib/utils.js2
17 files changed, 265 insertions, 598 deletions
diff --git a/lib/auth.js b/lib/auth.js
new file mode 100644
index 00000000..9720cd83
--- /dev/null
+++ b/lib/auth.js
@@ -0,0 +1,37 @@
+import fastifyPassport from "@fastify/passport";
+import { g } from "$lib/db";
+import config from "$config";
+import jwt from "passport-jwt";
+
+export const optional = {
+ preValidation: fastifyPassport.authenticate(
+ "jwt",
+ { session: false },
+ function(req, res, err, user, info) {
+ req.user = user;
+ }
+ )
+};
+
+export const auth = {
+ preValidation: fastifyPassport.authenticate("jwt", {
+ authInfo: false,
+ session: false
+ })
+};
+
+export const requirePin = async ({ body, user }) => {
+ if (!user || (user.pin && user.pin !== body.pin))
+ throw new Error("Invalid pin");
+};
+
+export const jwtStrategy = new jwt.Strategy(
+ {
+ jwtFromRequest: jwt.ExtractJwt.fromExtractors([
+ jwt.ExtractJwt.fromAuthHeaderAsBearerToken(),
+ req => (req.cookies ? req.cookies.token : null)
+ ]),
+ secretOrKey: config.jwt
+ },
+ async (payload, next) => next(null, await g(`user:${payload.id}`))
+);
diff --git a/lib/binance.js b/lib/binance.js
deleted file mode 100644
index e583d312..00000000
--- a/lib/binance.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import app from "$app";
-import WebSocket from "ws";
-import { err } from "./logging";
-import store from "./store";
-
-export default () => {
- const binance = new WebSocket(
- "wss://stream.binance.com:9443/ws/btcusdt@ticker"
- );
-
- binance.onmessage = async function(event) {
- try {
- let msg = JSON.parse(event.data);
-
- if (!store.fx) return;
-
- Object.keys(store.fx).map(symbol => {
- store.rates[symbol] = msg.c * store.fx[symbol];
- });
-
- store.last = msg.c;
- } catch (e) {
- err("binance message error", e.message);
- }
- };
-
- binance.onerror = async function(error) {
- console.log(error);
- err("binance socket error", error.message);
- };
-
- return binance;
-};
diff --git a/lib/db.js b/lib/db.js
new file mode 100644
index 00000000..76653c81
--- /dev/null
+++ b/lib/db.js
@@ -0,0 +1,14 @@
+import { createClient } from "redis";
+
+export const db = createClient({
+ url: "redis://rd"
+});
+
+db.on("error", err => console.log("db error", err));
+
+await db.connect();
+
+export default db;
+
+export let g = async k => JSON.parse(await db.get(k));
+export let s = (k, v) => db.set(k, JSON.stringify(v));
diff --git a/lib/lightning.js b/lib/lightning.js
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/lib/lightning.js
diff --git a/lib/locations.js b/lib/locations.js
new file mode 100644
index 00000000..bd2cfb5e
--- /dev/null
+++ b/lib/locations.js
@@ -0,0 +1,20 @@
+import got from "got";
+import { s } from "$lib/db";
+
+export let getLocations = async () => {
+ try {
+ let locations = await got(
+ "https://api.btcmap.org/v2/elements?updated_since=2022-09-19"
+ ).json();
+
+ locations = locations.filter(
+ l =>
+ l["osm_json"].tags && l["osm_json"].tags["payment:coinos"] === "yes"
+ )
+
+
+ await s("locations", locations);
+ } catch (e) {}
+
+ setTimeout(getLocations, 60000);
+};
diff --git a/lib/mail.js b/lib/mail.js
deleted file mode 100644
index 966ce578..00000000
--- a/lib/mail.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import app from "$app";
-import config from "$config";
-
-app.post("/email", async (req, res) => {
- try {
- try {
- var postmark = await import("postmark");
-
- var client = new postmark.ServerClient(config.postmark);
-
- await client.sendEmail({
- Subject: req.body.subject || "Email Signup",
- HtmlBody: JSON.stringify(req.body),
- TextBody: JSON.stringify(req.body),
- MessageStream: "outbound"
- });
-
- res.send({ ok: true });
- } catch (e) {
- console.log("problem sending email", e);
- res.code(500).send(e.message);
- }
- } catch (e) {
- console.log(e);
- res.code(500).send(e.message);
- }
-});
diff --git a/lib/nostr.js b/lib/nostr.js
index 08ed8b61..93370587 100644
--- a/lib/nostr.js
+++ b/lib/nostr.js
@@ -3,16 +3,15 @@ import config from "$config";
import { Relay, RelayPool } from "nostr";
import { broadcast } from "$lib/sockets";
import store from "$lib/store";
-import redis from "$lib/redis";
+import { g, s, db } from "$lib/db";
import { nada, wait } from "$lib/utils";
export let pool;
try {
pool = RelayPool(config.relays);
-} catch(e) {
- console.log(e)
-}
-
+} catch (e) {
+ console.log(e);
+}
pool.on("open", relay => {
if (relay.url.includes(config.relay)) coinos = relay;
@@ -32,7 +31,7 @@ export let q = async (
let start = Date.now();
let seen = [];
let rejected;
- query.since = await redis.get(`since:${sub}`);
+ query.since = await g(`since:${sub}`);
if (now() - query.since < since) return r();
let done = { [sub]: [] };
@@ -56,7 +55,7 @@ export let q = async (
clearTimeout(this.timer);
delete timeouts[sub];
pool.unsubscribe(sub);
- await redis.set(`since:${sub}`, now());
+ await s(`since:${sub}`, now());
},
extend(ev) {
if (ev) {
@@ -93,7 +92,7 @@ export let q = async (
let seen = [];
let getUser = async pubkey =>
- JSON.parse(await redis.get(`user:${pubkey}`)) || {
+ JSON.parse(await g(`user:${pubkey}`)) || {
username: pubkey.substr(0, 6),
pubkey,
anon: true
@@ -141,37 +140,38 @@ pool.on("event", async (relay, sub, ev) => {
updated: ev.created_at
};
- await redis.set(`user:${pubkey}`, JSON.stringify(user));
+ await s(`user:${pubkey}`, JSON.stringify(user));
coinos.send(ev);
} else if (sub.includes("follows")) {
let pubkey = sub.split(":")[0];
- await redis.set(`${pubkey}:follows`, JSON.stringify(ev.tags));
+ await s(`${pubkey}:follows`, JSON.stringify(ev.tags));
for (let f of ev.tags) {
let [_, followPubkey] = f;
- await redis.sAdd(`${followPubkey}:followers`, pubkey);
+ await db.sAdd(`${followPubkey}:followers`, pubkey);
}
coinos.send(ev);
} else if (sub.includes("followers")) {
let followed = sub.split(":")[0];
let { pubkey } = ev;
- await redis.sAdd(`${followed}:followers`, pubkey);
+ await db.sAdd(`${followed}:followers`, pubkey);
coinos.send(ev);
} else if (sub.includes("notes")) {
let pubkey = sub.split(":")[0];
- await redis.sAdd(pubkey, ev.id);
- await redis.set(`ev:${ev.id}`, JSON.stringify(ev));
+ await db.sAdd(pubkey, ev.id);
+ await s(`ev:${ev.id}`, JSON.stringify(ev));
}
} catch (e) {
console.log(e);
}
});
-app.get("/event/:id", async (req, res) => {
+export default {
+async event (req, res) {
try {
- let event = JSON.parse(await redis.get(`ev:${req.params.id}`));
+ let event = JSON.parse(await g(`ev:${req.params.id}`));
let { pubkey } = event;
- event.user = JSON.parse(await redis.get(`user:${pubkey}`)) || {
+ event.user = JSON.parse(await g(`user:${pubkey}`)) || {
username: pubkey.substr(0, 6),
pubkey,
anon: true,
@@ -183,13 +183,13 @@ app.get("/event/:id", async (req, res) => {
} catch (e) {
res.code(500).send("Problem fetching event");
}
-});
+},
-app.get("/:pubkey/notes", async (req, res) => {
+async notes (req, res) {
try {
let { pubkey } = req.params;
- let user = JSON.parse(await redis.get(`user:${pubkey}`));
+ let user = JSON.parse(await g(`user:${pubkey}`));
if (!user) {
user = await db.User.findOne({
@@ -215,12 +215,12 @@ app.get("/:pubkey/notes", async (req, res) => {
{ since: 0 }
).catch(nada);
- await redis.set(`user:${pubkey}`, JSON.stringify(user));
+ await g(`user:${pubkey}`, JSON.stringify(user));
- let ids = await redis.sMembers(pubkey);
+ let ids = await db.sMembers(pubkey);
let events = ids.length
- ? (await redis.mGet(ids.map(k => "ev:" + k))).map(JSON.parse)
+ ? (await db.mGet(ids.map(k => "ev:" + k))).map(JSON.parse)
: [];
res.send(events.map(e => ({ ...e, user })));
@@ -228,15 +228,15 @@ app.get("/:pubkey/notes", async (req, res) => {
console.log(e);
res.code(500).send("problem fetching user events");
}
-});
+},
-app.post("/event", async (req, res) => {
+async broadcast (req, res) {
let { event } = req.body;
pool.send(["EVENT", event]);
res.send(event);
-});
+},
-app.get("/:pubkey/follows", async (req, res) => {
+async follows (req, res) {
try {
let { pubkey } = req.params;
let { tagsonly } = req.query;
@@ -252,7 +252,7 @@ app.get("/:pubkey/follows", async (req, res) => {
{ timeout: 60000, eager: 60000 }
).catch(nada);
- let tags = JSON.parse(await redis.get(`${pubkey}:follows`)) || [];
+ let tags = JSON.parse(await g(`${pubkey}:follows`)) || [];
if (tagsonly) return res.send(tags);
let follows = [];
@@ -265,7 +265,7 @@ app.get("/:pubkey/follows", async (req, res) => {
authors: [pubkey]
}).catch(nada);
- let user = JSON.parse(await redis.get(`user:${pubkey}`));
+ let user = JSON.parse(await g(`user:${pubkey}`));
if (!user)
user = {
@@ -287,14 +287,14 @@ app.get("/:pubkey/follows", async (req, res) => {
}
});
-app.get("/:pubkey/followers", async (req, res) => {
+async (req, res) followers {
try {
let { pubkey } = req.params;
let pubkeys = [
...new Set([
...(await got(`${config.nostr}/followers?pubkey=${pubkey}`).json()),
- ...(await redis.sMembers(`${pubkey}:followers`))
+ ...(await db.sMembers(`${pubkey}:followers`))
])
];
@@ -307,7 +307,7 @@ app.get("/:pubkey/followers", async (req, res) => {
).catch(nada);
for (let pubkey of pubkeys) {
- let user = JSON.parse(await redis.get(`user:${pubkey}`));
+ let user = JSON.parse(await g(`user:${pubkey}`));
if (!user || !user.updated) {
q(`${pubkey}:profile:f2`, {
limit: 1,
@@ -315,7 +315,7 @@ app.get("/:pubkey/followers", async (req, res) => {
authors: [pubkey]
}).catch(nada);
- user = JSON.parse(await redis.get(`user:${pubkey}`));
+ user = JSON.parse(await g(`user:${pubkey}`));
}
if (!user)
@@ -336,11 +336,15 @@ app.get("/:pubkey/followers", async (req, res) => {
console.log(e);
res.code(500).send(e && e.message);
}
-});
+},
-app.get('/nostr.json', async (req, res) => {
+async identities (req, res) {
res.send({
- names: { adam: "566c166f3adab0c8fba5da015b0b3bcc8eb3696b455f2a1d43bfbd97059646a8", asoltys: "566c166f3adab0c8fba5da015b0b3bcc8eb3696b455f2a1d43bfbd97059646a8" }
+ names: {
+ adam: "566c166f3adab0c8fba5da015b0b3bcc8eb3696b455f2a1d43bfbd97059646a8",
+ asoltys:
+ "566c166f3adab0c8fba5da015b0b3bcc8eb3696b455f2a1d43bfbd97059646a8"
+ }
});
-});
-
+}
+}
diff --git a/lib/passport.js b/lib/passport.js
deleted file mode 100644
index 25dc0103..00000000
--- a/lib/passport.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import app from "$app";
-import jwt from "passport-jwt";
-import fastifyPassport from "@fastify/passport";
-import fastifySecureSession from "@fastify/secure-session";
-import config from "$config";
-import fs from "fs";
-import path from "path";
-import { err } from "$lib/logging";
-import { g } from "$lib/redis";
-
-app.register(fastifySecureSession, {
- key: Buffer.from(config.jwt, "hex")
-});
-
-app.register(fastifyPassport.initialize());
-app.register(fastifyPassport.secureSession());
-
-function cookieExtractor(req) {
- if (req && req.cookies) {
- return req.cookies["token"];
- }
- return null;
-}
-
-fastifyPassport.use(
- "jwt",
- new jwt.Strategy(
- {
- jwtFromRequest: jwt.ExtractJwt.fromExtractors([
- jwt.ExtractJwt.fromAuthHeaderAsBearerToken(),
- cookieExtractor
- ]),
- secretOrKey: config.jwt
- },
- async (payload, next) => {
- try {
- let { id } = payload;
- let user = await g(`user:${id}`);
- user.haspin = !!user.pin;
- next(null, user);
- } catch (e) {
- console.log("payload", payload);
- if (!e.message.includes("locked")) {
- console.log(e);
- err("problem logging in", e);
- }
- next(null, { username: "guest" });
- }
- }
- )
-);
-
-export const optionalAuth = {
- preValidation: fastifyPassport.authenticate(
- "jwt",
- { session: false },
- function(req, res, err, user, info) {
- req.user = user;
- }
- )
-};
-
-export const adminAuth = {
- preValidation: fastifyPassport.authenticate(
- "jwt",
- { session: false },
- function(req, res, err, user, info) {
- if (!(user && user.admin)) return res.code(401).send("Unauthorized");
- req.user = user;
- }
- )
-};
-
-export const auth = {
- preValidation: fastifyPassport.authenticate("jwt", {
- authInfo: false,
- session: false
- })
-};
diff --git a/lib/rates.js b/lib/rates.js
index 8064a117..c7d6918c 100644
--- a/lib/rates.js
+++ b/lib/rates.js
@@ -1,69 +1,60 @@
-import app from "$app";
import got from "got";
import config from "$config";
-import fs from "fs";
-import binance from "./binance";
import { broadcast } from "./sockets";
import store from "./store";
import { err, warn } from "$lib/logging";
+import { g, s } from "$lib/db";
+import WebSocket from "ws";
-let b = binance();
-setInterval(() => {
- b.terminate();
- b = binance();
-}, 360000);
+let ws;
+let connect = () => {
+ if (ws) ws.terminate();
+ ws = new WebSocket("wss://stream.binance.com:9443/ws/btcusdt@ticker");
-let fetchRates = async () => {
- let date, fx;
+ ws.onmessage = async function(event) {
+ try {
+ let msg = JSON.parse(event.data);
- try {
- ({ date, fx } = JSON.parse(fs.readFileSync("data/fx")));
- } catch (e) {
- warn("Failed to read fx file");
- }
+ if (!store.fx) return;
- let today = JSON.stringify(new Date())
- .replace(/T.*/, "")
- .replace(/"/, "");
+ Object.keys(store.fx).map(symbol => {
+ store.rates[symbol] = msg.c * store.fx[symbol];
+ });
+
+ store.last = msg.c;
+ } catch (e) {
+ err("binance message error", e.message);
+ }
+ };
+
+ ws.onerror = async function(error) {
+ err("binance socket error", error.message);
+ };
+
+ setTimeout(connect, 360000);
+ return ws;
+};
- if (date !== today) {
+export let sendRates = () => broadcast("rate", store.last);
+export let getRates = async () => {
+ connect();
+
+ let date, fx;
+ let rates = await g("rates");
+ if (rates) ({ date, fx } = rates);
+
+ if (Date.now() - date > 24 * 60 * 60 * 1000) {
try {
let { rates: fx } = await got(
`https://api.exchangerate.host/latest?base=USD&v={(new Date()).toISOString().split('T')[0]}`
).json();
- fs.writeFileSync("data/fx", JSON.stringify({ date, fx }));
+ await s("rates", { date, fx });
} catch (e) {
- try {
- let { rates: fx } = await got(
- `http://data.fixer.io/api/latest?access_key=${config.fixer}`
- ).json();
-
- let USD = fx["USD"];
- Object.keys(fx).map(k => {
- fx[k] = parseFloat((fx[k] / fx["USD"]).toFixed(6));
- });
-
- fs.writeFileSync("data/fx", JSON.stringify({ date, fx }));
- } catch (e) {
- err("error fetching rates", e.message);
- }
+ err("error fetching rates", e.message);
}
}
store.fx = fx;
- setTimeout(fetchRates, 7200000);
+ setTimeout(getRates, 7200000);
};
-fetchRates();
-
-app.get("/rate", async (req, res, next) => {
- res.send(store.last);
-});
-
-app.get("/rates", async (req, res, next) => {
- res.send(store.rates);
-});
-
-setInterval(() => {
- broadcast("rate", store.last);
-}, 1000);
diff --git a/lib/redis.js b/lib/redis.js
deleted file mode 100644
index 6aec2af7..00000000
--- a/lib/redis.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { createClient } from "redis";
-
-const redis = createClient({
- url: "redis://rd"
-});
-
-redis.on("error", err => console.log("redis error", err));
-
-await redis.connect();
-
-export default redis;
-
-export let g = async k => JSON.parse(await redis.get(k));
-export let s = (k, v) => redis.set(k, JSON.stringify(v));
-export let rd = redis;
diff --git a/lib/register.js b/lib/register.js
index 8ac5355f..63dccb38 100644
--- a/lib/register.js
+++ b/lib/register.js
@@ -4,7 +4,7 @@ import { authenticator } from "otplib";
import bcrypt from "bcrypt";
import { l } from "$lib/logging";
import { v4 } from "uuid";
-import { s, g } from "$lib/redis";
+import { s, g } from "$lib/db";
import { got } from "got";
export default async (user, ip, requireChallenge) => {
diff --git a/lib/send.js b/lib/send.js
deleted file mode 100644
index f378cfc1..00000000
--- a/lib/send.js
+++ /dev/null
@@ -1,201 +0,0 @@
-import config from "$config";
-import { emit } from "$lib/sockets";
-import store from "./store";
-import {
- computeConversionFee,
- conversionFeeReceiver
-} from "../routes/lightning/conversionFee";
-import ln from "$lib/ln";
-import { l, warn, err } from "$lib/logging";
-
-export default async (amount, memo, hash, user) => {
- l("attempting lightning payment", user.username, amount, hash);
-
- // decode payreq with cl
- if (!amount || payreq.satoshis > amount) amount = payreq.satoshis;
- amount = parseInt(amount);
-
- if (store.seen.includes(hash)) {
- warn("attempted to pay a paid invoice", user.username);
- throw new Error("Invoice has been paid, can't pay again");
- }
-
- let error, fee, total, preimage, payment;
-
- let conversionFee = computeConversionFee(amount);
- let conversionFeeDeduction;
- await db.transaction(async transaction => {
- l("starting lightning send transaction");
- let account;
- if (user.account.asset === config.liquid.btcasset)
- account = await db.Account.findOne({
- where: {
- id: user.account.id,
- pubkey: null
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
- else
- account = await db.Account.findOne({
- where: {
- user_id: user.id,
- asset: config.liquid.btcasset,
- pubkey: null,
- balance: {
- [Op.gte]: amount
- }
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
-
- conversionFeeDeduction = Math.min(account.lightning_credits, conversionFee);
- total = amount + conversionFee - conversionFeeDeduction;
-
- if (!account || account.balance < total) {
- warn(
- "user attempting to send more than they have",
- user.id,
- amount,
- conversionFee
- );
- throw new Error("Insufficient funds");
- }
-
- await account.decrement(
- { balance: total, lightning_credits: conversionFeeDeduction },
- { transaction }
- );
- await account.reload({ transaction });
-
- l("debited account", account.id, total);
- let tokens;
- if (!payreq.satoshis) tokens = amount;
-
- let m;
- let { msatoshi, msatoshi_sent, payment_preimage } = await ln.pay(
- hash,
- tokens ? `${tokens}sats` : undefined
- );
- preimage = payment_preimage;
- fee = parseInt((msatoshi_sent - msatoshi) / 1000);
-
- if (store.seen.includes(preimage)) {
- warn("duplicate payment detected", preimage);
- throw new Error("Duplicate payment detected");
- }
-
- store.seen.push(preimage);
- store.seen.push(hash);
- });
-
- await db.transaction(async transaction => {
- l("starting lightning fee transaction");
-
- let account;
- if (user.account.asset === config.liquid.btcasset)
- account = await db.Account.findOne({
- where: {
- id: user.account.id,
- pubkey: null
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
- else
- account = await db.Account.findOne({
- where: {
- user_id: user.id,
- asset: config.liquid.btcasset,
- pubkey: null,
- balance: {
- [Op.gte]: fee
- }
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
-
- l("deducting fee", fee);
- await account.decrement({ balance: fee }, { transaction });
- await account.reload({ transaction });
-
- if (conversionFeeDeduction) {
- conversionFee -= conversionFeeDeduction;
- }
-
- l("conversion fee", conversionFee);
-
- let receiverAccount = await db.Account.findOne({
- where: {
- "$user.username$": conversionFeeReceiver
- },
- include: [
- {
- model: db.User,
- as: "user"
- }
- ],
- lock: transaction.LOCK.UPDATE,
- transaction
- });
-
- let fee_payment;
- let fee_payment_id = null;
- if (conversionFee) {
- await receiverAccount.increment(
- { balance: conversionFee },
- { transaction }
- );
- await receiverAccount.reload({ transaction });
- fee_payment = await db.Payment.create(
- {
- amount: conversionFee,
- fee: 0,
- memo: "Lightning conversion fee",
- account_id: receiverAccount.id,
- user_id: receiverAccount.user_id,
- rate: store.rates[receiverAccount.user.currency],
- currency: receiverAccount.user.currency,
- confirmed: true,
- received: true,
- network: "COINOS"
- },
- { transaction }
- );
- fee_payment_id = fee_payment.id;
- }
-
- payment = await db.Payment.create(
- {
- amount: -total,
- account_id: account.id,
- user_id: user.id,
- hash,
- memo,
- preimage,
- rate: store.rates[user.currency],
- currency: user.currency,
- confirmed: true,
- network: "lightning",
- fee,
- fee_payment_id
- },
- { transaction }
- );
-
- l("created payment record", payment.id);
-
- payment = payment.get({ plain: true });
- payment.account = account.get({ plain: true });
- payment.fee_payment = fee_payment && fee_payment.get({ plain: true });
-
- emit(user.username, "account", payment.account);
- emit(user.username, "payment", payment);
-
- l("sent lightning", user.username, amount, total, fee);
- });
-
- return payment;
-};
diff --git a/lib/sendInternal.js b/lib/sendInternal.js
index f801c9cd..0ba0f0b4 100644
--- a/lib/sendInternal.js
+++ b/lib/sendInternal.js
@@ -7,12 +7,12 @@ import { notify } from "$lib/notifications";
import { l, err } from "$lib/logging";
import { fail } from "$lib/utils";
import ln from "$lib/ln";
-import { g, s, rd } from "$lib/redis";
+import { g, s, db } from "$lib/db";
const { HOSTNAME: hostname } = process.env;
export default async (
- { amount, address, payreq, unconfidential, memo, username, tip },
+ { amount, hash, memo, tip },
user
) => {
amount = parseInt(amount);
@@ -20,97 +20,77 @@ export default async (
if (!amount || amount < 0) fail("Amount must be greater than zero");
let fee = 0;
- let recipient;
+ let invoice = await g(`invoice:${hash}`);
let t = async () => {
- await rd.watch(`user:${user_id}`);
- user = await g(`user:${user_id}`);
- if (user.balance < amount) throw new Error("Insufficient funds");
-
+ await db.watch(`user:${user.id}`);
+ user = await g(`user:${user.id}`);
+ if (user.balance < amount) fail("Insufficient funds");
user.balance -= amount;
- let m = await rd.multi();
+ let m = await db.multi();
await s(`user:${user.id}`, user);
- if (username && (recipient = await g(`user:${username}`))) {
- let { payment_hash: h } = await ln.decode(payreq);
- let invoice = payreq && (await g(`invoice:${h}`));
+ let p1 = {
+ hash,
+ amount: -amount,
+ memo,
+ uid: user.id,
+ rate: store.rates[user.currency],
+ currency: user.currency,
+ type: "internal",
+ created: Date.now()
+ };
+
+ await s(`payment:${hash}`, p1);
+ await db.lPush(`${user.id}:payments`, hash);
+
+ l("sent internal", user.username, -p1.amount);
+ emit(user.id, "payment", p1);
- let t = async () => {
- await rd.watch(`user:${recipient.id}`);
- recipient = await g(`user:${recipient.id}`);
+ let { uid } = invoice;
+ if (uid !== user.id) {
+ let recipient = await g(`user:${uid}`);
+
+ let u = async () => {
+ await db.watch(`user:${uid}`);
+ recipient = await g(`user:${uid}`);
recipient.balance += amount;
- let m = await rd.multi();
- await s(`user:${recipient.id}`, recipient);
- if (!(await m.exec())) t();
+ let m = await db.multi();
+ await s(`user:${uid}`, recipient);
+ if (!(await m.exec())) u();
};
- await t();
-
- if (!(await m.exec())) t();
- }
- await t();
+ await u();
- let p2 = {
- id: v4(),
- amount,
- user_id: recipient.id,
- rate: store.rates[recipient.currency],
- currency: recipient.currency,
- memo,
- type: "internal",
- with_id: user.id,
- created_at: Date.now()
- };
+ let p2 = {
+ hash,
+ amount,
+ uid: recipient.id,
+ rate: store.rates[recipient.currency],
+ currency: recipient.currency,
+ memo,
+ type: "internal",
+ with_id: user.id,
+ created: Date.now()
+ };
- if (invoice) {
let { tip } = invoice;
- params.invoice_id = invoice.id;
if (tip) {
- params.tip = tip;
- params.amount -= tip;
+ p2.tip = tip;
+ p2.amount -= tip;
}
invoice.received += amount;
- await s(`invoice:${h}`, invoice);
- }
-
- if (invoice) p2.invoice = invoice;
- await s(`payment:${p2.id}`, p2);
- await rd.lPush(`${recipient.id}:payments`, p2.id);
+ await s(`invoice:${hash}`, invoice);
+ await s(`payment:${id}`, p2);
+ await db.lPush(`${recipient.id}:payments`, id);
- emit(recipient.username, "payment", p2);
-
- l("received internal", recipient.username, amount);
- notify(recipient, `Received ${amount} sats`);
- callWebhook(invoice, p2);
- };
+ l("received internal", recipient.username, amount);
+ emit(recipient.username, "payment", p2);
+ notify(recipient, `Received ${amount} sats`);
+ callWebhook(invoice, p2);
+ }
- let p1 = {
- id: v4(),
- amount: -amount,
- memo,
- user_id: user.id,
- rate: store.rates[user.currency],
- currency: user.currency,
- with_id: recipient && recipient.id,
- hash: `#${v4().substr(0, 6)} ${
- username ? `Payment to ${username}` : "Internal Transfer"
- }`,
- type: "internal",
- created_at: Date.now()
+ if (!(await m.exec())) t();
};
-
- if (!username) {
- l("creating redeemable payment");
- params.redeemcode = v4();
- params.hash = `${hostname}/redeem/${params.redeemcode}`;
- }
-
- await s(`payment:${p1.id}`, p1);
- await rd.lPush(`${user.id}:payments`, p1.id);
-
- l("sent internal", user.username, -p1.amount);
-
- emit(user.username, "payment", p1);
-
- return p1;
+ await t();
};
diff --git a/lib/sockets.js b/lib/sockets.js
index c94c29a0..7f655570 100644
--- a/lib/sockets.js
+++ b/lib/sockets.js
@@ -1,49 +1,42 @@
-import store from "$lib/store";
-import config from "$config";
import app from "$app";
+import config from "$config";
+import store from "$lib/store";
import jwt from "jsonwebtoken";
import { v4 } from "uuid";
-import WebSocket from "ws";
-import { differenceInSeconds, subSeconds } from "date-fns";
-import fastifyWs from "@fastify/websocket";
import whitelist from "./whitelist";
import { l, err, warn } from "$lib/logging";
-import { g } from "$lib/redis";
+import { g } from "$lib/db";
+import { pick } from "$lib/utils";
const code = 1000;
-
-const wss = new WebSocket.Server({ clientTracking: false, noServer: true });
-
-const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {});
const all = {};
const subscriptions = [];
const lastSeen = {};
+const users = {};
-export const emit = (uuid, type, data) => {
- if (data.uuid !== undefined) data = pick(data, ...whitelist);
-
+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];
- if (s.invoice.id === data.invoice_id) {
+ if (s.hash === data.hash) {
s.ws.send(JSON.stringify({ type, data }));
}
}
}
- if (!store.sockets[uuid]) return;
- for (let id in store.sockets[uuid]) {
- const ws = store.sockets[uuid][id];
+ if (!store.sockets[uid]) return;
+ for (let id in store.sockets[uid]) {
+ const ws = store.sockets[uid][id];
ws.send(JSON.stringify({ type, data }));
}
};
export const broadcast = (type, data) => {
const sent = [];
- for (const uuid in store.sockets) {
- for (const id in store.sockets[uuid]) {
- const ws = store.sockets[uuid][id];
+ for (const uid in store.sockets) {
+ for (const id in store.sockets[uid]) {
+ const ws = store.sockets[uid][id];
ws.send(JSON.stringify({ type, data }));
sent.push(ws.id);
}
@@ -54,14 +47,10 @@ export const broadcast = (type, data) => {
});
};
-function subtractSeconds(numOfSeconds, date = new Date()) {
- date.setSeconds(date.getSeconds() - numOfSeconds);
- return date;
-}
+export async function socketServer(app) {
+ let seconds = (n, d = new Date()) => d.setSeconds(d.getSeconds() - n) && d;
-app.register(fastifyWs);
-app.register(async function(app) {
app.get("/ws", { websocket: true }, ({ socket: ws }, req) => {
const id = v4();
ws.id = id;
@@ -85,41 +74,42 @@ app.register(async function(app) {
case "login":
let token = data;
- if (lastSeen[token] > subtractSeconds(5)) return;
+ if (lastSeen[token] > seconds(5)) return;
lastSeen[token] = new Date();
try {
if (!token || token === "null" || !jwt.decode(token))
return ws.close(code, `bad token ${token}`);
- let { id: user_id } = jwt.decode(token);
- if (!user_id) throw new Error("Invalid JWT token");
- let user = await g(`user:${user_id}`);
+ let { id: uid } = jwt.decode(token);
+ if (!uid) throw new Error("Invalid JWT token");
+ let user = await g(`user:${uid}`);
if (!user) throw new Error("User not found");
- user.haspin = !!user.pin;
- if (!store.sockets[user_id]) store.sockets[user_id] = {};
- const existing = Object.keys(store.sockets[user_id]);
+ if (!store.sockets[uid]) store.sockets[uid] = {};
+ const existing = Object.keys(store.sockets[uid]);
if (existing.length > 4) {
- const p = existing.find(sid => sid !== user_id);
- store.sockets[user_id][p].send(JSON.stringify({ type: "logout" }));
+ const p = existing.find(sid => sid !== uid);
+ store.sockets[uid][p].send(
+ JSON.stringify({ type: "logout" })
+ );
setTimeout(() => {
- if (store.sockets[user_id][p])
- store.sockets[user_id][p].close(code, "too many sockets");
+ if (store.sockets[uid][p])
+ store.sockets[uid][p].close(code, "too many sockets");
}, 5000);
}
- store.sockets[user_id][id] = ws;
- names[id] = user_id;
+ store.sockets[uid][id] = ws;
+ users[id] = uid;
ws.user = user;
ws.send(
- JSON.stringify({ type: "login", data: pick(user, ...whitelist) })
+ JSON.stringify({ type: "login", data: pick(user, whitelist) })
);
ws.send(
JSON.stringify({ type: "version", data: config.clientVersion })
);
} catch (e) {
- console.log(e)
+ console.log(e);
err("caught socket error", e.message);
if (e.message.includes("not found"))
ws.send(JSON.stringify({ type: "logout" }));
@@ -146,9 +136,9 @@ app.register(async function(app) {
ws.on("close", function(code, reason) {
try {
- const uuid = names[id];
- if (store.sockets[uuid] && store.sockets[uuid][id]) {
- delete store.sockets[uuid][id];
+ const uid = users[id];
+ if (store.sockets[uid] && store.sockets[uid][id]) {
+ delete store.sockets[uid][id];
}
if (all[id]) delete all[id];
@@ -157,16 +147,15 @@ app.register(async function(app) {
}
});
});
-});
+}
-const names = {};
-setInterval(() => {
- for (const uuid in store.sockets) {
- for (const id in store.sockets[uuid]) {
- let ws = store.sockets[uuid][id];
+export let sendHeartbeat = () => {
+ for (const uid in store.sockets) {
+ for (const id in store.sockets[uid]) {
+ let ws = store.sockets[uid][id];
ws.beats++;
- if (ws.beats > 10) ws.close(code, `${uuid} ${id} lost heartbeat`);
+ if (ws.beats > 10) ws.close(code, `${uid} ${id} lost heartbeat`);
}
}
-}, 2000);
+}
diff --git a/lib/store.js b/lib/store.js
index 6f0c1c97..bf71219e 100644
--- a/lib/store.js
+++ b/lib/store.js
@@ -2,7 +2,6 @@ export default {
addresses: {},
challenge: {},
change: [],
- exceptions: [],
issuances: {},
logins: {},
nodes: [],
diff --git a/lib/upload.js b/lib/upload.js
index 95dfe21a..b37e0bf2 100644
--- a/lib/upload.js
+++ b/lib/upload.js
@@ -1,7 +1,3 @@
-import app from "$app";
-import { auth } from "./passport";
-import fastifyMultipart from "@fastify/multipart";
-import fastifyStatic from "@fastify/static";
import { fileTypeFromStream } from "file-type";
import pump from "pump";
import Clone from "readable-stream-clone";
@@ -9,21 +5,13 @@ import fs from "fs";
import path from "path";
import sharp from "sharp";
import { pipeline } from "stream";
+import { g, s } from "$lib/db";
-app.register(fastifyMultipart);
-
-app.register(fastifyStatic, {
- root: path.join("/app/uploads"),
- prefix: "/public/"
-});
-
-app.post("/upload/:type", auth, async function(req, res) {
- try {
- let { type } = req.params;
+export default async ({ params: { type }, file, user }, res) => {
if (!["banner", "profile"].includes(type))
throw new Error("invalid upload");
- let data = await req.file();
+ let data = await file();
let s1 = new Clone(data.file);
let s2 = new Clone(data.file);
@@ -32,7 +20,7 @@ app.post("/upload/:type", auth, async function(req, res) {
if (format !== "image" && !["jpg", "jpeg", "png"].includes(ext))
throw new Error("unsupported file type");
- let name = `${req.user.uuid}-${type}.webp`;
+ let name = `${user.id}-${type}.webp`;
let path = `/app/uploads/${name}`;
let size = type === "banner" ? 1920 : 240;
@@ -43,12 +31,14 @@ app.post("/upload/:type", auth, async function(req, res) {
await new Promise(r => pipeline(s2, t, fs.createWriteStream(path), r));
- req.user[type] = true;
- await req.user.save();
+ let r = async () => {
+ await db.watch(`user:${user.id}`);
+ user = await g(`user:${user.id}`);
+ user[type] = true;
+ let m = await db.multi();
+ await s(`user:${user.id}`, user);
+ };
+ await r();
res.send(name);
- } catch (e) {
- console.log(e);
- return res.code(500).send(e);
- }
-});
+}
diff --git a/lib/utils.js b/lib/utils.js
index b556c46a..e53bf649 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -1,5 +1,5 @@
import store from "$lib/store";
-import { g } from "$lib/redis";
+import { g } from "$lib/db";
import config from "$config";
export const nada = () => {};