diff options
| author | Adam Soltys <[email protected]> | 2023-01-09 01:50:34 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2023-01-09 01:50:34 -0800 |
| commit | ded2c69ce89438326523a512b570be6eee4b23f0 (patch) | |
| tree | 71f96ea4349d665365c26aba7eb18ac8f63f9c99 | |
| parent | dce07aa93b3c2f47aca0c24cea6adde9ec37f1a7 (diff) | |
| download | coinos-server-ded2c69ce89438326523a512b570be6eee4b23f0.tar.gz coinos-server-ded2c69ce89438326523a512b570be6eee4b23f0.zip | |
use data folder for container data
| -rw-r--r-- | .env | 17 | ||||
| -rw-r--r-- | .gitignore | 25 | ||||
| -rw-r--r-- | .pnpm-debug.log | 22 | ||||
| -rw-r--r-- | docker-compose.yml | 10 | ||||
| -rw-r--r-- | index.js | 1 | ||||
| -rw-r--r-- | lib/mail.js | 5 | ||||
| -rw-r--r-- | routes/lnurl.js | 575 |
7 files changed, 46 insertions, 609 deletions
@@ -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 @@ -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' @@ -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); - } -}); |
