aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2023-01-09 01:50:34 -0800
committerAdam Soltys <[email protected]>2023-01-09 01:50:34 -0800
commitded2c69ce89438326523a512b570be6eee4b23f0 (patch)
tree71f96ea4349d665365c26aba7eb18ac8f63f9c99
parentdce07aa93b3c2f47aca0c24cea6adde9ec37f1a7 (diff)
downloadcoinos-server-ded2c69ce89438326523a512b570be6eee4b23f0.tar.gz
coinos-server-ded2c69ce89438326523a512b570be6eee4b23f0.zip
use data folder for container data
-rw-r--r--.env17
-rw-r--r--.gitignore25
-rw-r--r--.pnpm-debug.log22
-rw-r--r--docker-compose.yml10
-rw-r--r--index.js1
-rw-r--r--lib/mail.js5
-rw-r--r--routes/lnurl.js575
7 files changed, 46 insertions, 609 deletions
diff --git a/.env b/.env
new file mode 100644
index 00000000..9fedb2dc
--- /dev/null
+++ b/.env
@@ -0,0 +1,17 @@
+NGINX_PORT=8080
+APP_SERVER_PORT=3220
+BITCOIN_PEER_PORT=18442
+BITCOIN_NODE_PORT=18443
+LIQUID_PEER_PORT=18444
+LIQUID_NODE_PORT=7040
+BITCOIN_ELECTRS_RPC_PORT=51401
+BITCOIN_ELECTRS_PORT=3002
+LIQUID_ELECTRS_RPC_PORT=60401
+LIQUID_ELECTRS_PORT=3012
+LIQUID_BLOCK_PORT=18606
+LIQUID_TX_PORT=18607
+LIQUID_ESPLORA_URL=http://localhost:5005
+LIQUID_ESPLORA_PORT=5005
+LIQUID_ELECTRS_URL=http://localhost:3012
+ROOT_PASSWORD=password
+HOST=dev.coinos.io
diff --git a/.gitignore b/.gitignore
index 3b6aa213..af41b69f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,26 +1,5 @@
node_modules/
-.env
-yarn-error.log
-.pnp*
-.yarn*
-tls.*
-leveldb
-swaps
-exceptions
-fx
+.pnpm-store
uploads
-verified
data
-lnurl-server.sqlite3
-watcher.log
-config
-mysql
-build.json
-build.sh
-watcher.sh
-logs
-docker-compose.mainnet.yml
-assets.json
-lm.yml
-lt.yml
-caddy
+config.js
diff --git a/.pnpm-debug.log b/.pnpm-debug.log
new file mode 100644
index 00000000..ad5f9d74
--- /dev/null
+++ b/.pnpm-debug.log
@@ -0,0 +1,22 @@
+{
+ "0 debug pnpm:scope": {
+ "selected": 1
+ },
+ "1 error pnpm": {
+ "code": "ELIFECYCLE",
+ "errno": "ENOENT",
+ "syscall": "spawn",
+ "file": "sh",
+ "pkgid": "[email protected]",
+ "stage": "start",
+ "script": "nodemon index.js",
+ "pkgname": "coinos",
+ "err": {
+ "name": "pnpm",
+ "message": "[email protected] start: `nodemon index.js`\nspawn ENOENT",
+ "code": "ELIFECYCLE",
+ "stack": "pnpm: [email protected] start: `nodemon index.js`\nspawn ENOENT\n at ChildProcess.<anonymous> (/usr/local/lib/node_modules/pnpm/dist/pnpm.cjs:92977:22)\n at ChildProcess.emit (node:events:394:28)\n at maybeClose (node:internal/child_process:1067:16)\n at Process.ChildProcess._handle.onexit (node:internal/child_process:301:5)"
+ }
+ },
+ "2 warn pnpm:global": " Local package.json exists, but node_modules missing, did you mean to install?"
+} \ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index b4a73394..72c3cec1 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -20,7 +20,7 @@ services:
image: asoltys/bitcoin
container_name: bc
volumes:
- - ./config/bitcoin:/home/bitcoin/.bitcoin
+ - ./data/bitcoin:/home/bitcoin/.bitcoin
restart: always
cl:
image: elementsproject/lightningd:latest
@@ -28,13 +28,13 @@ services:
environment:
LIGHTNINGD_NETWORK: regtest
volumes:
- - ./config/lightning:/root/.lightning
+ - ./data/lightning:/root/.lightning
restart: always
rd:
image: redis
container_name: rd
volumes:
- - ./config/redis:/data
+ - ./data/redis:/data
restart: always
nostr:
container_name: nostr
@@ -42,7 +42,7 @@ services:
restart: always
user: 100:100
volumes:
- - "./config/nostr/data:/usr/src/app/db"
- - "./config/nostr/config.toml:/usr/src/app/config.toml"
+ - "./data/nostr/data:/usr/src/app/db"
+ - "./data/nostr/data.toml:/usr/src/app/config.toml"
ports:
- '8080:8080'
diff --git a/index.js b/index.js
index b5ec0dca..bec89372 100644
--- a/index.js
+++ b/index.js
@@ -15,7 +15,6 @@ import "./routes/invoices";
import "./routes/payments";
import "./routes/users";
import "./routes/requests";
-import "./routes/lnurl";
let host = process.env.HOST || "0.0.0.0";
let port = process.env.PORT || 3119;
diff --git a/lib/mail.js b/lib/mail.js
index a146d128..966ce578 100644
--- a/lib/mail.js
+++ b/lib/mail.js
@@ -1,13 +1,8 @@
import app from "$app";
import config from "$config";
-import persist from "./persist";
-const emails = persist("data/emails.json");
app.post("/email", async (req, res) => {
try {
- let file = persist("data/emails.json");
- file.emails = [...file.emails, req.body];
-
try {
var postmark = await import("postmark");
diff --git a/routes/lnurl.js b/routes/lnurl.js
deleted file mode 100644
index e9a0b167..00000000
--- a/routes/lnurl.js
+++ /dev/null
@@ -1,575 +0,0 @@
-import app from "$app";
-import config from "$config";
-import store from "$lib/store";
-import { optionalAuth, auth } from "$lib/passport";
-import { l, err, warn } from "$lib/logging";
-
-import ln from "$lib/ln";
-import persist from "$lib/persist";
-import { emit } from "$lib/sockets";
-
-import lnurl from "lnurl";
-import jwt from "jsonwebtoken";
-import qs from "query-string";
-
-import {
- computeConversionFee,
- conversionFeeReceiver
-} from "./lightning/conversionFee.js";
-
-import send from "$lib/send";
-import sendInternal from "$lib/sendInternal";
-import register from "$lib/register";
-
-const logins = persist("data/logins.json");
-const recipients = persist("data/recipients.json");
-const lnurlPayments = persist("data/payments.json");
-const withdrawals = persist("data/withdrawals.json");
-
-const { HOSTNAME: hostname } = process.env;
-
-export const lnurlServer = lnurl.createServer(config.lnurl);
-
-app.get("/url", async (req, res, next) => {
- try {
- const code = await db.Code.findOne({
- where: {
- code: req.query.code
- }
- });
-
- if (code) {
- res.send(code.text);
- } else {
- throw new Error("code not found");
- }
- } catch (e) {
- err("couldn't find url", e.message);
- }
-});
-
-app.get("/withdraw", auth, async (req, res, next) => {
- const { min, max } = req.query;
- try {
- const result = await lnurlServer.generateNewUrl("withdrawRequest", {
- minWithdrawable: min * 1000,
- maxWithdrawable: max * 1000,
- defaultDescription: "coinos voucher"
- });
-
- withdrawals[result.secret] = req.user;
-
- res.send(result);
- } catch (e) {
- err("problem generating withdrawl url", e.message);
- res.code(500).send(e.message);
- }
-});
-
-app.post("/code", async (req, res, next) => {
- try {
- const { encoded } = req.body.lnurl;
- const code = await db.Code.findOrCreate({
- where: {
- code: `lnurl:${encoded.substr(-32)}`,
- text: encoded
- }
- });
-
- res.send(code);
- } catch (e) {
- err(e.errors);
- res.code(500).send(e.message);
- }
-});
-
-app.post("/withdraw", auth, async (req, res, next) => {
- const { user } = req;
- const {
- amount: value,
- params: { callback, k1 }
- } = req.body;
-
- let invoice;
- let label = new Date();
- let desc = label;
-
- invoice = await ln.invoice(`${value}sats`, label, desc);
-
- const { payment_request: pr } = invoice;
- const url = `${callback}?k1=${k1}&pr=${pr}`;
-
- await db.Invoice.create({
- user_id: user.id,
- text: pr,
- rate: store.rates[user.currency],
- currency: user.currency,
- amount: value,
- tip: 0,
- network: "bitcoin"
- });
-
- try {
- result = (await axios.get(url)).data;
- res.send(result);
- } catch (e) {
- err("failed to withdraw", e.message);
- res.code(500).send(e.message);
- }
-});
-
-app.get("/lnurlp/:username", async (req, res, next) => {
- try {
- const { username } = req.params;
- let user = await db.User.findOne({
- where: {
- username
- }
- });
-
- if (!user) throw new Error("user not found");
-
- let { amount, minSendable, maxSendable } = req.query;
- minSendable = minSendable || 1000;
- maxSendable = maxSendable || 1000000000;
- if (parseInt(amount)) minSendable = maxSendable = amount * 1000;
-
- let result = await lnurlServer.generateNewUrl("payRequest", {
- minSendable,
- maxSendable,
- metadata: JSON.stringify([
- ["text/plain", `paying ${user.username}`],
- ["text/identifier", `${user.username}@coinos.io`]
- ])
- });
-
- recipients[result.secret] = {
- id: user.id,
- currency: user.currency,
- username
- };
-
- l("recipient", user.username, result.secret);
-
- result = await axios.get(result.url);
-
- res.send(result.data);
- } catch (e) {
- err("problem generating payment url", e.message);
- res.code(500).send(e.message);
- }
-});
-
-app.get("/pay/:username", async (req, res, next) => {
- const { username } = req.params;
- let user = await db.User.findOne({
- where: {
- username
- }
- });
-
- let { amount, minSendable, maxSendable } = req.query;
- minSendable = minSendable || 1000;
- maxSendable = maxSendable || 1000000000;
- if (parseInt(amount)) minSendable = maxSendable = amount * 1000;
-
- try {
- const result = await lnurlServer.generateNewUrl("payRequest", {
- minSendable,
- maxSendable,
- metadata: JSON.stringify([
- ["text/plain", `paying ${user.username}`],
- ["text/identifier", `${user.username}@coinos.io`]
- ])
- });
-
- recipients[result.secret] = {
- id: user.id,
- currency: user.currency,
- username
- };
-
- l("recipient", user.username, result.secret);
- res.send(result);
- } catch (e) {
- err("problem generating payment url", e.message);
- res.code(500).send(e.message);
- }
-});
-
-app.post("/pay", auth, async (req, res, next) => {
- const { user } = req;
-
- const {
- amount,
- comment,
- params: { callback }
- } = req.body;
-
- let url = `${callback}${callback.includes("?") ? "&" : "?"}amount=${amount *
- 1000}${comment ? "&comment=" + comment : ""}`;
-
- try {
- const parts = callback.split("/");
- const secret = parts[parts.length - 1];
- lnurlPayments[secret] = user.id;
-
- if (recipients[secret]) {
- let data = await sendInternal(
- {
- amount,
- memo: comment,
- username: recipients[secret].username
- },
- user
- );
-
- res.send(data);
- } else {
- const { data } = await axios.get(url);
- res.send(await send(amount, "", data.pr, user));
- }
- } catch (e) {
- console.log(e);
- err("failed to send payment", e.message);
- res.code(500).send(e.message);
- }
-});
-
-app.get("/login", optionalAuth, async (req, res, next) => {
- try {
- const result = await lnurlServer.generateNewUrl("login");
-
- if (req.user) {
- logins[result.secret] = req.user.username;
- }
-
- res.send(result);
- } catch (e) {
- err("problem generating login url", e.message);
- res.code(500).send(e.message);
- }
-});
-
-app.get("/encode", async (req, res, next) => {
- let { domain, name } = req.query;
- let url = `https://${domain}/.well-known/lnurlp/${name}`;
- res.send(lnurl.encode(url));
-});
-
-app.get("/decode", async (req, res, next) => {
- const { text } = req.query;
-
- try {
- const url = lnurl.decode(text);
- let spl = url.split("?");
- if (spl.length > 1 && qs.parse(spl[1]).tag === "login") {
- return res.send({
- tag: "login",
- k1: qs.parse(spl[1]).k1,
- callback: url,
- domain: url
- .split("://")[1]
- .split("/")[0]
- .split("@")
- .slice(-1)[0]
- .split(":")[0]
- });
- }
-
- const { data: params } = await axios.get(url);
-
- res.send(params);
- } catch (e) {
- err("problem decoding lnurl", e.message);
- res.code(500).send(e.message);
- }
-});
-
-lnurlServer.on("payRequest:action:processed", async function(event) {
- const { secret, params, result } = event;
- const { id, invoice } = result;
- const recipient = recipients[secret];
- let payreq = bolt11.decode(invoice);
-
- try {
- let i = await db.Invoice.create({
- user_id: recipient.id,
- text: invoice,
- rate: store.rates[recipient.currency],
- currency: recipient.currency,
- amount: payreq.satoshis,
- tip: 0,
- network: "lightning"
- });
-
- l("invoice created", i.text, i.amount);
- } catch (e) {
- err("problem finding lnurl invoice", e.message);
- }
-});
-
-lnurlServer.bindToHook(
- "middleware:signedLnurl:afterCheckSignature",
- async (req, res, next) => {
- try {
- let user;
- const { amount: msats, key, tag, pr, k1 } = req.query;
-
- if (msats) {
- let amount = Math.round(msats / 1000);
- const parts = req.originalUrl.split("/");
- const secret = parts[parts.length - 1].split("?")[0];
- let user_id = lnurlPayments[secret];
-
- if (user_id) {
- let account = await db.Account.findOne({
- where: {
- user_id,
- asset: config.liquid.btcasset,
- pubkey: null
- }
- });
-
- if (account.balance < amount) {
- throw new Error("Insufficient funds");
- }
- }
- }
-
- if (tag === "login") {
- let username = logins[k1];
-
- if (!username) {
- const keyObj = await db.Key.findOne({
- where: { hex: key },
- include: [
- {
- model: db.User,
- as: "user"
- }
- ]
- });
-
- if (keyObj) ({ user } = keyObj);
-
- if (!user) {
- let ip =
- req.headers["x-forwarded-for"] || req.connection.remoteAddress;
-
- let username = `satoshi-${key.substr(0, 12)}`;
- let existing = await db.User.findOne({ where: { username } });
- if (existing) user = existing;
- else {
- user = await register(
- {
- username,
- password: key
- },
- ip
- );
- }
- }
-
- ({ username } = user);
- }
-
- logins[key] = { k1, username };
- }
-
- if (pr) {
- user = withdrawals[k1];
- if (!user) {
- if (next) next(new Error("withdrawal not found"));
- return;
- }
-
- try {
- let amount;
-
- let { msatoshi } = await ln.decodepay(pr);
- amount = parseInt(msatoshi / 1000);
- let conversionFee = computeConversionFee(amount);
-
- await db.transaction(async transaction => {
- let account = await db.Account.findOne({
- where: {
- user_id: user.id,
- asset: config.liquid.btcasset
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
-
- // account that receives conversion fees
- let receiverAccount = await db.Account.findOne({
- where: {
- "$user.username$": conversionFeeReceiver
- },
- include: [
- {
- model: db.User,
- as: "user"
- }
- ],
- lock: transaction.LOCK.UPDATE,
- transaction
- });
-
- let conversionFeeDeduction = Math.min(
- account.lightning_credits,
- conversionFee
- );
- if (conversionFeeDeduction) {
- await account.decrement(
- { lightning_credits: conversionFeeDeduction },
- { transaction }
- );
- await account.reload({ transaction });
- conversionFee -= conversionFeeDeduction;
- }
-
- if (account.balance < amount) {
- throw new Error("Insufficient funds");
- } else if (account.balance < amount + conversionFee) {
- throw new Error("Insufficient funds for conversion fee");
- }
-
- let fee_payment_id = null;
- if (conversionFee) {
- await receiverAccount.increment(
- { balance: conversionFee },
- { transaction }
- );
- await receiverAccount.reload({ transaction });
- let fee_payment = await db.Payment.create(
- {
- amount: conversionFee,
- fee: 0,
- memo: "Bitcoin 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;
- }
-
- let payment = await db.Payment.create(
- {
- amount: -amount,
- account_id: account.id,
- user_id: user.id,
- hash: pr,
- rate: store.rates[user.currency],
- currency: user.currency,
- confirmed: true,
- network: "lightning",
- fee_payment_id
- },
- { transaction }
- );
-
- setTimeout(async () => {
- try {
- let { payments } = await ln.getPayments({
- limit: 5
- });
-
- let p = payments.find(p => p.request === pr);
- if (p) {
- l("found payment", pr);
- payment.fee = p.fee;
- await account.decrement({ balance: p.fee }, { transaction });
- await account.reload({ transaction });
- await payment.save();
- payment = payment.get({ plain: true });
- payment.account = account.get({ plain: true });
- emit(user.username, "account", account);
- emit(user.username, "payment", payment);
- } else {
- warn("payment not found, fee not set", pr);
- }
- } catch (e) {
- err("problem trying to get ln withdrawal payment", e);
- }
- }, 1000);
-
- await account.decrement(
- { balance: amount + conversionFee },
- { transaction }
- );
- await account.reload({ transaction });
-
- let p = payment.get({ plain: true });
- p.account = account.get({ plain: true });
-
- emit(user.username, "account", p.account);
- emit(user.username, "payment", p);
- });
- } catch (e) {
- err("failed to process withdrawal", e.message);
- return next(e);
- }
- }
-
- if (next) next();
- } catch (e) {
- err("unhandled lnurerr", e.message);
- }
- }
-);
-
-lnurlServer.bindToHook("login", async key => {
- try {
- if (!key) throw new Error("login key not defined");
-
- const exists = await db.Key.findOne({
- where: { hex: key },
- include: [{ model: db.User, as: "user" }]
- });
-
- let user;
- if (logins[key] && logins[key] !== "undefined") {
- const { username } = logins[key];
- user = await db.User.findOne({
- where: { username }
- });
-
- if (user) {
- const [k, created] = await db.Key.findOrCreate({
- where: {
- user_id: user.id,
- hex: key
- }
- });
-
- if (created) {
- l("added key", username, k);
- emit(username, "key", k);
- }
- } else {
- l("user not found");
- user = await register({
- username: key.substr(0, 8),
- password: key
- });
- }
- } else if (exists) ({ user } = exists);
-
- if (user && user.username) {
- const payload = { username: user.username };
- const token = jwt.sign(payload, config.jwt);
- const ws = store.sessions[logins[key].k1];
- if (ws && ws.send)
- ws.send(JSON.stringify({ type: "token", data: { token, key } }));
- }
- } catch (e) {
- err("problem with login hook", e.message);
- }
-});