├── sampledata ├── uploads │ └── .gitkeep ├── db │ └── keydb.conf ├── bitcoin │ ├── transaction.sh │ └── bitcoin.conf ├── lightning │ └── config ├── lightningb │ └── config ├── index.js.map ├── liquid │ ├── elements.conf │ └── transaction.sh ├── index.ts ├── index.js └── nostr │ └── config.toml ├── server ├── strsrv ├── bun.lockb ├── mklittlefs ├── .pino-prettyrc ├── lib ├── api.ts ├── mqtt.ts ├── api.js.map ├── locales │ ├── index.ts │ ├── zh.json │ ├── ja.json │ ├── ko.json │ ├── ar.json │ ├── th.json │ ├── index.js.map │ ├── fa.json │ ├── hi.json │ ├── bn.json │ ├── tr.json │ ├── pl.json │ ├── es.json │ ├── en.json │ ├── it.json │ ├── nl.json │ ├── el.json │ ├── pt.json │ ├── ru.json │ ├── fr.json │ └── de.json ├── mqtt.js.map ├── ln.js.map ├── migrate.ts ├── store.ts ├── logging.ts ├── store.js.map ├── whitelist.js.map ├── whitelist.ts ├── strfry.ts ├── webhooks.ts ├── logging.js.map ├── webhooks.js.map ├── notifications.js.map ├── mail.js.map ├── upload.ts ├── mail.ts ├── locations.js.map ├── auth.js.map ├── square.ts ├── auth.ts ├── types.ts ├── lightning.js.map ├── app.js.map ├── locations.ts ├── register.js.map ├── upload.js.map ├── rates.ts ├── rates.js.map ├── ecash.js.map ├── ln.ts ├── register.ts ├── ecash.ts ├── db.js.map ├── lightning.ts ├── invoices.js.map ├── notifications.ts ├── app.ts ├── countries.ts ├── countries.js.map ├── db.ts ├── sockets.ts ├── invoices.ts ├── sockets.js.map ├── migrate.js.map ├── utils.ts └── utils.js.map ├── db └── sessions │ ├── 018ba16a867ca3d0fb336166c6b23030 │ ├── 01b1707f36168090b3ef7bfc9c287258 │ ├── 05391af90e82d6150020655d39a91c5c │ ├── 06006f553d879f5c4ea47a9f9416214d │ ├── 079f53fe074034a99612d7a177164583 │ ├── 13a3926f9666796b1ba2dfcfc994b6e5 │ ├── 1480e2cedef56b55b88a49cbac820405 │ ├── 1bf3fcb9a7bf53846fc4366bed50ef56 │ ├── 1e5d52e35ef0775f7b0895eb85c3ddb2 │ ├── 2029240f6d1128be89ddc32729463129 │ ├── 20da11dfa350e6649c793f4abee5e4d6 │ ├── 23a5d9ada293a7aad167c32adba114c8 │ ├── 2488fa553df6d1d32776f24353dcd612 │ ├── 24a4f3873dccf1154d87962d852a6d27 │ ├── 29092978e2db5541caa6c017d31ddecf │ ├── 2d6fb62afb924bc25b24f75b77384eb8 │ ├── 30265bad14719343d5bf4a9aaa9b6136 │ ├── 31dc2773af847a4ecc0504a2d73e2d0c │ ├── 320cab8936c941bbbf3c13e2c0c98efc │ ├── 3402ee84f9347096d6f9978675c69da5 │ ├── 383715f31603f0645edf9103617c420f │ ├── 445cfa1e1c6adfb2f06f8114c94ea5bb │ ├── 4650da5546ff1aa66d0f67fb952048bd │ ├── 46f0b79fab8befe200e93b33decfc566 │ ├── 472da5a78c1fda47f66d9db46909cd47 │ ├── 481f0c82690e84ca81378ce54c432f1c │ ├── 4a181973aaf3482b0348d5d47ac77c4e │ ├── 5e2d2f8968f00018882d853e95f68501 │ ├── 60582aab67d954ce3d5e81bb11e46066 │ ├── 6404930090ea34f9bf427e39372a5830 │ ├── 6afcd0f7520ea63caeec8821e0110774 │ ├── 6fb0aeb8c6e0261b1b47357aee62cc8b │ ├── 73cbc9b310fa1504c71bb602f8409610 │ ├── 743f667065516a89ed84f7396ecb9113 │ ├── 778f83fc6203bd5b819e9aa6ba56f02b │ ├── 86e39adeb51274d138a18fb612454644 │ ├── 8e23a8b87ce97d21e53ec7c564958c00 │ ├── 90508310ae890d85b94fedcbea72fac3 │ ├── 910a7588b7564a5bd06fab8ddfb9b397 │ ├── 954622d75ae49e5ddfcf9cb23725dd80 │ ├── 966e7f52e42f940ec1f0d3a44e6d6e8c │ ├── 97b49e057cee1e78c8a71ec52d9ec902 │ ├── 9805fafc09a0dbe4775a187894b41032 │ ├── 9b339d9137ea691ec09446509237ed9c │ ├── 9cf3c14c07fb4c5def965ced8488d97a │ ├── 9ef717a0828dd5c4683a551cc8b3c345 │ ├── a211b3b15ed0b6ab2dcaae7cd0f57315 │ ├── a24eb3ead1364d18b6fcfcb4c48359f1 │ ├── aca49ca7642fed5b28419b117069e84b │ ├── ad1e69e12afc7e034c8f0d87f78ce143 │ ├── b0a195d3f8bf441b46bf7b4d6529aeb5 │ ├── b1a53183a5bdc5b4733f3b750cf589cf │ ├── b22ed4263ad7622d861263171c09519e │ ├── b59b1dc26eca1e31bebd1e625a76fecf │ ├── b758163a5c2657a697ce7629ed37af91 │ ├── bc3b6c9bead3b0bd7174308c760bb5c8 │ ├── bda9429577c23bce5a84456b0b24bf90 │ ├── c4221802bb0c7c0d46749ea92a53722b │ ├── c571095176b45046f4b460b0cea40c38 │ ├── c68e55a257cd6aa5a72cc505baf76458 │ ├── c7b86accdb1742563b75fddb053df008 │ ├── c827c714eeb91cf6532a47caa4233eca │ ├── c9a6c4adcb7c3a8a7908399b7305610d │ ├── cc1377152860eaf886d92b66ea263993 │ ├── d0c8f4cac368dfbbd36c26905140cd9b │ ├── d2f5bf56683a66e4975917410f69ec0b │ ├── d3ae4acc292123415db3bf0abb0c3a2c │ ├── d9c843d1e9de2d344e44a2587104b6b7 │ ├── d9e2fc19986340eeb610e7e3b4c0006c │ ├── dc4f9e18bd79a2027fb7f224e060c12c │ ├── dfcb1a49a640e82fd06ba5cddbd3dd8a │ ├── e1005c91c26e7b75ab8b56447819e64e │ ├── e43362425e142ce46f6b127c2cb9655b │ ├── e53f706db281a03f7becd104a1bfbbb7 │ ├── e5ad138cb31619bea1cbf27b2cd08317 │ ├── e6a3e7c2e22df783638f9eb2ec9dbb4c │ ├── e8d71bd01783250570c8cf22b822e799 │ ├── f0b118f5ef7e6c8611093366c2558238 │ ├── f3ce4321fb8ecf13a37fba279323e535 │ ├── f9a8e4ce97931b414a647dea05826a25 │ ├── fa0c53bcef6cdf97d55fcecebbd25581 │ └── fe05cd7c887c0bda34c990338e5f09fe ├── nodemon.json ├── Dockerfile ├── biome.json ├── routes ├── locations.ts ├── locations.js.map ├── rates.ts ├── rates.js.map ├── shopify.js.map ├── info.ts ├── invoices.js.map ├── info.js.map ├── shopify.ts ├── email.js.map ├── email.ts ├── items.ts ├── items.js.map ├── square.ts ├── invoices.ts ├── ecash.js.map ├── lnurl.js.map └── ecash.ts ├── .dockerignore ├── .gitignore ├── setup.sh ├── Dockerfile.bun ├── templates └── payments │ ├── verify.html │ ├── reset.html │ └── received.html ├── config.js.map ├── tsconfig.json ├── mint.ts ├── config.ts.sample ├── package.json ├── compose.yml.sample ├── README.md └── index.js.map /sampledata/uploads/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sampledata/db/keydb.conf: -------------------------------------------------------------------------------- 1 | loglevel debug 2 | -------------------------------------------------------------------------------- /server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/server -------------------------------------------------------------------------------- /strsrv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/strsrv -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/bun.lockb -------------------------------------------------------------------------------- /mklittlefs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/mklittlefs -------------------------------------------------------------------------------- /.pino-prettyrc: -------------------------------------------------------------------------------- 1 | { 2 | "translateTime": "mm-dd HH:MM:ss", 3 | "ignore": "hostname,pid" 4 | } 5 | -------------------------------------------------------------------------------- /lib/api.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | bitcoin: "https://mempool.space/api/v1", 3 | liquid: "https://liquid.network/api/v1", 4 | }; 5 | -------------------------------------------------------------------------------- /db/sessions/018ba16a867ca3d0fb336166c6b23030: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/018ba16a867ca3d0fb336166c6b23030 -------------------------------------------------------------------------------- /db/sessions/01b1707f36168090b3ef7bfc9c287258: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/01b1707f36168090b3ef7bfc9c287258 -------------------------------------------------------------------------------- /db/sessions/05391af90e82d6150020655d39a91c5c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/05391af90e82d6150020655d39a91c5c -------------------------------------------------------------------------------- /db/sessions/06006f553d879f5c4ea47a9f9416214d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/06006f553d879f5c4ea47a9f9416214d -------------------------------------------------------------------------------- /db/sessions/079f53fe074034a99612d7a177164583: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/079f53fe074034a99612d7a177164583 -------------------------------------------------------------------------------- /db/sessions/13a3926f9666796b1ba2dfcfc994b6e5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/13a3926f9666796b1ba2dfcfc994b6e5 -------------------------------------------------------------------------------- /db/sessions/1480e2cedef56b55b88a49cbac820405: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/1480e2cedef56b55b88a49cbac820405 -------------------------------------------------------------------------------- /db/sessions/1bf3fcb9a7bf53846fc4366bed50ef56: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/1bf3fcb9a7bf53846fc4366bed50ef56 -------------------------------------------------------------------------------- /db/sessions/1e5d52e35ef0775f7b0895eb85c3ddb2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/1e5d52e35ef0775f7b0895eb85c3ddb2 -------------------------------------------------------------------------------- /db/sessions/2029240f6d1128be89ddc32729463129: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/2029240f6d1128be89ddc32729463129 -------------------------------------------------------------------------------- /db/sessions/20da11dfa350e6649c793f4abee5e4d6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/20da11dfa350e6649c793f4abee5e4d6 -------------------------------------------------------------------------------- /db/sessions/23a5d9ada293a7aad167c32adba114c8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/23a5d9ada293a7aad167c32adba114c8 -------------------------------------------------------------------------------- /db/sessions/2488fa553df6d1d32776f24353dcd612: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/2488fa553df6d1d32776f24353dcd612 -------------------------------------------------------------------------------- /db/sessions/24a4f3873dccf1154d87962d852a6d27: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/24a4f3873dccf1154d87962d852a6d27 -------------------------------------------------------------------------------- /db/sessions/29092978e2db5541caa6c017d31ddecf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/29092978e2db5541caa6c017d31ddecf -------------------------------------------------------------------------------- /db/sessions/2d6fb62afb924bc25b24f75b77384eb8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/2d6fb62afb924bc25b24f75b77384eb8 -------------------------------------------------------------------------------- /db/sessions/30265bad14719343d5bf4a9aaa9b6136: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/30265bad14719343d5bf4a9aaa9b6136 -------------------------------------------------------------------------------- /db/sessions/31dc2773af847a4ecc0504a2d73e2d0c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/31dc2773af847a4ecc0504a2d73e2d0c -------------------------------------------------------------------------------- /db/sessions/320cab8936c941bbbf3c13e2c0c98efc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/320cab8936c941bbbf3c13e2c0c98efc -------------------------------------------------------------------------------- /db/sessions/3402ee84f9347096d6f9978675c69da5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/3402ee84f9347096d6f9978675c69da5 -------------------------------------------------------------------------------- /db/sessions/383715f31603f0645edf9103617c420f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/383715f31603f0645edf9103617c420f -------------------------------------------------------------------------------- /db/sessions/445cfa1e1c6adfb2f06f8114c94ea5bb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/445cfa1e1c6adfb2f06f8114c94ea5bb -------------------------------------------------------------------------------- /db/sessions/4650da5546ff1aa66d0f67fb952048bd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/4650da5546ff1aa66d0f67fb952048bd -------------------------------------------------------------------------------- /db/sessions/46f0b79fab8befe200e93b33decfc566: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/46f0b79fab8befe200e93b33decfc566 -------------------------------------------------------------------------------- /db/sessions/472da5a78c1fda47f66d9db46909cd47: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/472da5a78c1fda47f66d9db46909cd47 -------------------------------------------------------------------------------- /db/sessions/481f0c82690e84ca81378ce54c432f1c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/481f0c82690e84ca81378ce54c432f1c -------------------------------------------------------------------------------- /db/sessions/4a181973aaf3482b0348d5d47ac77c4e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/4a181973aaf3482b0348d5d47ac77c4e -------------------------------------------------------------------------------- /db/sessions/5e2d2f8968f00018882d853e95f68501: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/5e2d2f8968f00018882d853e95f68501 -------------------------------------------------------------------------------- /db/sessions/60582aab67d954ce3d5e81bb11e46066: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/60582aab67d954ce3d5e81bb11e46066 -------------------------------------------------------------------------------- /db/sessions/6404930090ea34f9bf427e39372a5830: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/6404930090ea34f9bf427e39372a5830 -------------------------------------------------------------------------------- /db/sessions/6afcd0f7520ea63caeec8821e0110774: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/6afcd0f7520ea63caeec8821e0110774 -------------------------------------------------------------------------------- /db/sessions/6fb0aeb8c6e0261b1b47357aee62cc8b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/6fb0aeb8c6e0261b1b47357aee62cc8b -------------------------------------------------------------------------------- /db/sessions/73cbc9b310fa1504c71bb602f8409610: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/73cbc9b310fa1504c71bb602f8409610 -------------------------------------------------------------------------------- /db/sessions/743f667065516a89ed84f7396ecb9113: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/743f667065516a89ed84f7396ecb9113 -------------------------------------------------------------------------------- /db/sessions/778f83fc6203bd5b819e9aa6ba56f02b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/778f83fc6203bd5b819e9aa6ba56f02b -------------------------------------------------------------------------------- /db/sessions/86e39adeb51274d138a18fb612454644: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/86e39adeb51274d138a18fb612454644 -------------------------------------------------------------------------------- /db/sessions/8e23a8b87ce97d21e53ec7c564958c00: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/8e23a8b87ce97d21e53ec7c564958c00 -------------------------------------------------------------------------------- /db/sessions/90508310ae890d85b94fedcbea72fac3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/90508310ae890d85b94fedcbea72fac3 -------------------------------------------------------------------------------- /db/sessions/910a7588b7564a5bd06fab8ddfb9b397: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/910a7588b7564a5bd06fab8ddfb9b397 -------------------------------------------------------------------------------- /db/sessions/954622d75ae49e5ddfcf9cb23725dd80: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/954622d75ae49e5ddfcf9cb23725dd80 -------------------------------------------------------------------------------- /db/sessions/966e7f52e42f940ec1f0d3a44e6d6e8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/966e7f52e42f940ec1f0d3a44e6d6e8c -------------------------------------------------------------------------------- /db/sessions/97b49e057cee1e78c8a71ec52d9ec902: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/97b49e057cee1e78c8a71ec52d9ec902 -------------------------------------------------------------------------------- /db/sessions/9805fafc09a0dbe4775a187894b41032: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/9805fafc09a0dbe4775a187894b41032 -------------------------------------------------------------------------------- /db/sessions/9b339d9137ea691ec09446509237ed9c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/9b339d9137ea691ec09446509237ed9c -------------------------------------------------------------------------------- /db/sessions/9cf3c14c07fb4c5def965ced8488d97a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/9cf3c14c07fb4c5def965ced8488d97a -------------------------------------------------------------------------------- /db/sessions/9ef717a0828dd5c4683a551cc8b3c345: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/9ef717a0828dd5c4683a551cc8b3c345 -------------------------------------------------------------------------------- /db/sessions/a211b3b15ed0b6ab2dcaae7cd0f57315: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/a211b3b15ed0b6ab2dcaae7cd0f57315 -------------------------------------------------------------------------------- /db/sessions/a24eb3ead1364d18b6fcfcb4c48359f1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/a24eb3ead1364d18b6fcfcb4c48359f1 -------------------------------------------------------------------------------- /db/sessions/aca49ca7642fed5b28419b117069e84b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/aca49ca7642fed5b28419b117069e84b -------------------------------------------------------------------------------- /db/sessions/ad1e69e12afc7e034c8f0d87f78ce143: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/ad1e69e12afc7e034c8f0d87f78ce143 -------------------------------------------------------------------------------- /db/sessions/b0a195d3f8bf441b46bf7b4d6529aeb5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/b0a195d3f8bf441b46bf7b4d6529aeb5 -------------------------------------------------------------------------------- /db/sessions/b1a53183a5bdc5b4733f3b750cf589cf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/b1a53183a5bdc5b4733f3b750cf589cf -------------------------------------------------------------------------------- /db/sessions/b22ed4263ad7622d861263171c09519e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/b22ed4263ad7622d861263171c09519e -------------------------------------------------------------------------------- /db/sessions/b59b1dc26eca1e31bebd1e625a76fecf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/b59b1dc26eca1e31bebd1e625a76fecf -------------------------------------------------------------------------------- /db/sessions/b758163a5c2657a697ce7629ed37af91: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/b758163a5c2657a697ce7629ed37af91 -------------------------------------------------------------------------------- /db/sessions/bc3b6c9bead3b0bd7174308c760bb5c8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/bc3b6c9bead3b0bd7174308c760bb5c8 -------------------------------------------------------------------------------- /db/sessions/bda9429577c23bce5a84456b0b24bf90: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/bda9429577c23bce5a84456b0b24bf90 -------------------------------------------------------------------------------- /db/sessions/c4221802bb0c7c0d46749ea92a53722b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/c4221802bb0c7c0d46749ea92a53722b -------------------------------------------------------------------------------- /db/sessions/c571095176b45046f4b460b0cea40c38: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/c571095176b45046f4b460b0cea40c38 -------------------------------------------------------------------------------- /db/sessions/c68e55a257cd6aa5a72cc505baf76458: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/c68e55a257cd6aa5a72cc505baf76458 -------------------------------------------------------------------------------- /db/sessions/c7b86accdb1742563b75fddb053df008: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/c7b86accdb1742563b75fddb053df008 -------------------------------------------------------------------------------- /db/sessions/c827c714eeb91cf6532a47caa4233eca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/c827c714eeb91cf6532a47caa4233eca -------------------------------------------------------------------------------- /db/sessions/c9a6c4adcb7c3a8a7908399b7305610d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/c9a6c4adcb7c3a8a7908399b7305610d -------------------------------------------------------------------------------- /db/sessions/cc1377152860eaf886d92b66ea263993: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/cc1377152860eaf886d92b66ea263993 -------------------------------------------------------------------------------- /db/sessions/d0c8f4cac368dfbbd36c26905140cd9b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/d0c8f4cac368dfbbd36c26905140cd9b -------------------------------------------------------------------------------- /db/sessions/d2f5bf56683a66e4975917410f69ec0b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/d2f5bf56683a66e4975917410f69ec0b -------------------------------------------------------------------------------- /db/sessions/d3ae4acc292123415db3bf0abb0c3a2c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/d3ae4acc292123415db3bf0abb0c3a2c -------------------------------------------------------------------------------- /db/sessions/d9c843d1e9de2d344e44a2587104b6b7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/d9c843d1e9de2d344e44a2587104b6b7 -------------------------------------------------------------------------------- /db/sessions/d9e2fc19986340eeb610e7e3b4c0006c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/d9e2fc19986340eeb610e7e3b4c0006c -------------------------------------------------------------------------------- /db/sessions/dc4f9e18bd79a2027fb7f224e060c12c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/dc4f9e18bd79a2027fb7f224e060c12c -------------------------------------------------------------------------------- /db/sessions/dfcb1a49a640e82fd06ba5cddbd3dd8a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/dfcb1a49a640e82fd06ba5cddbd3dd8a -------------------------------------------------------------------------------- /db/sessions/e1005c91c26e7b75ab8b56447819e64e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/e1005c91c26e7b75ab8b56447819e64e -------------------------------------------------------------------------------- /db/sessions/e43362425e142ce46f6b127c2cb9655b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/e43362425e142ce46f6b127c2cb9655b -------------------------------------------------------------------------------- /db/sessions/e53f706db281a03f7becd104a1bfbbb7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/e53f706db281a03f7becd104a1bfbbb7 -------------------------------------------------------------------------------- /db/sessions/e5ad138cb31619bea1cbf27b2cd08317: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/e5ad138cb31619bea1cbf27b2cd08317 -------------------------------------------------------------------------------- /db/sessions/e6a3e7c2e22df783638f9eb2ec9dbb4c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/e6a3e7c2e22df783638f9eb2ec9dbb4c -------------------------------------------------------------------------------- /db/sessions/e8d71bd01783250570c8cf22b822e799: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/e8d71bd01783250570c8cf22b822e799 -------------------------------------------------------------------------------- /db/sessions/f0b118f5ef7e6c8611093366c2558238: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/f0b118f5ef7e6c8611093366c2558238 -------------------------------------------------------------------------------- /db/sessions/f3ce4321fb8ecf13a37fba279323e535: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/f3ce4321fb8ecf13a37fba279323e535 -------------------------------------------------------------------------------- /db/sessions/f9a8e4ce97931b414a647dea05826a25: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/f9a8e4ce97931b414a647dea05826a25 -------------------------------------------------------------------------------- /db/sessions/fa0c53bcef6cdf97d55fcecebbd25581: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/fa0c53bcef6cdf97d55fcecebbd25581 -------------------------------------------------------------------------------- /db/sessions/fe05cd7c887c0bda34c990338e5f09fe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coinos/coinos-server/HEAD/db/sessions/fe05cd7c887c0bda34c990338e5f09fe -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["lib", "routes", "index.ts"], 3 | "ext": ".ts,.js", 4 | "ignore": [], 5 | "exec": "bun ./index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /lib/mqtt.ts: -------------------------------------------------------------------------------- 1 | import mqtt from "mqtt"; 2 | import config from "$config"; 3 | 4 | export default mqtt.connect("mqtt://mqtt.coinos.io", config.mqtt); 5 | -------------------------------------------------------------------------------- /lib/api.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"api.js","sourceRoot":"","sources":["api.ts"],"names":[],"mappings":";;AAAA,kBAAe;IACb,OAAO,EAAE,8BAA8B;IACvC,MAAM,EAAE,+BAA+B;CACxC,CAAC"} -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM oven/bun 2 | 3 | ARG NODE_ENV=production 4 | 5 | COPY package.json /home/bun/app 6 | 7 | RUN NODE_ENV=development bun i 8 | 9 | CMD ["bun", "run", "start"] 10 | -------------------------------------------------------------------------------- /sampledata/bitcoin/transaction.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | wget -O- --post-data='{"txid": "'$1'", "wallet": "'$2'", "type": "bitcoin"}' --header='Content-Type:application/json' 'http://app:3119/confirm' 3 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatter": { 3 | "indentStyle": "space" 4 | }, 5 | "vcs": { 6 | "enabled": true, 7 | "clientKind": "git", 8 | "useIgnoreFile": false 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /routes/locations.ts: -------------------------------------------------------------------------------- 1 | import { g } from "$lib/db"; 2 | 3 | export default { 4 | async list(_, res) { 5 | const locations = await g("locations"); 6 | res.send({ locations }); 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env 3 | yarn-error.log 4 | .pnp* 5 | .yarn* 6 | tls.* 7 | leveldb 8 | swaps 9 | exceptions 10 | fx 11 | uploads 12 | verified 13 | data 14 | lnurl-server.sqlite3 15 | watcher.log 16 | mysql 17 | config 18 | -------------------------------------------------------------------------------- /lib/locales/index.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | export default "ar,bn,de,el,en,es,fa,fr,hi,it,ja,ko,nl,pl,pt,ru,th,tr,zh".split(",").reduce((a, l) => { 3 | a[l] = JSON.parse(fs.readFileSync(`lib/locales/${l}.json`)); 4 | return a; 5 | }, {}); 6 | -------------------------------------------------------------------------------- /lib/locales/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "你好", 3 | "youReceived": "您收到了一笔付款", 4 | "withdrawalTriggered": "这引发了撤回", 5 | "problemWithdrawing": "发送至您的自动提款地址时出现问题", 6 | "paymentReceived": "已收到付款", 7 | "balanceAdjustment": "余额调整", 8 | "insufficientFunds": "资金不足" 9 | } 10 | -------------------------------------------------------------------------------- /lib/mqtt.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"mqtt.js","sourceRoot":"","sources":["mqtt.ts"],"names":[],"mappings":";;;;;;AAAA,8CAAwB;AACxB,oDAA6B;AAElB,QAAA,KAAK,GAAG,cAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,iBAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,QAAA,KAAK,GAAG,cAAI,CAAC,OAAO,CAAC,uBAAuB,EAAE,iBAAM,CAAC,KAAK,CAAC,CAAC"} -------------------------------------------------------------------------------- /sampledata/lightning/config: -------------------------------------------------------------------------------- 1 | alias=coinos 2 | rgb=006000 3 | bitcoin-rpcconnect=bc 4 | bitcoin-rpcport=18443 5 | bitcoin-rpcuser=admin1 6 | bitcoin-rpcpassword=123 7 | network=regtest 8 | addr=cl:9735 9 | clnrest-port=3010 10 | clnrest-protocol=http 11 | clnrest-host=0.0.0.0 12 | 13 | -------------------------------------------------------------------------------- /sampledata/lightningb/config: -------------------------------------------------------------------------------- 1 | alias=coinos 2 | rgb=006000 3 | bitcoin-rpcconnect=bc 4 | bitcoin-rpcport=18443 5 | bitcoin-rpcuser=admin1 6 | bitcoin-rpcpassword=123 7 | network=regtest 8 | addr=clb:9735 9 | clnrest-port=3010 10 | clnrest-protocol=http 11 | clnrest-host=0.0.0.0 12 | 13 | -------------------------------------------------------------------------------- /lib/locales/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "こんにちは", 3 | "youReceived": "の支払いを受けました", 4 | "withdrawalTriggered": "これが撤退のきっかけとなった", 5 | "problemWithdrawing": "自動引き出しアドレスへの送信中に問題が発生しました", 6 | "paymentReceived": "お支払い頂いた", 7 | "balanceAdjustment": "バランス調整", 8 | "insufficientFunds": "十分な資金" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "balanceAdjustment": "균형 조정", 3 | "paymentReceived": "지불받은", 4 | "hello": "이름 *", 5 | "youReceived": "당신은 지불을 받았습니다", 6 | "withdrawalTriggered": "이 방아쇠 인출", 7 | "problemWithdrawing": "Autowithdrawal 주소로 보내는 문제가 있었습니다", 8 | "insufficientFunds": "충분한 자금" 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .pnpm* 3 | data/ 4 | config.ts 5 | compose.yml 6 | .env 7 | *.flitevox 8 | mimic 9 | watcher.sh 10 | .aws 11 | *.js 12 | routes/**/*.js 13 | lib/**/*.js 14 | watcher.log 15 | .history/ 16 | res 17 | req 18 | nobal.sh 19 | littlefs.img 20 | printer 21 | failedlogins.txt 22 | -------------------------------------------------------------------------------- /lib/ln.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"ln.js","sourceRoot":"","sources":["ln.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oDAA6B;AAC7B,IAAI,EAAE,CAAC;AAED,IAAA,eAAe,GAAK,CAAC,uEAAa,mBAAmB,MAAC,CAAC,CAAC,OAAO,gBAAhD,CAAiD;AACtE,EAAE,GAAG,IAAI,eAAe,CAAC,iBAAM,CAAC,SAAS,CAAC,CAAC;AAE3C,kBAAe,EAAE,CAAC"} -------------------------------------------------------------------------------- /lib/migrate.ts: -------------------------------------------------------------------------------- 1 | import { g } from "$lib/db"; 2 | 3 | async function migrate(id) { 4 | const k = id?.replace(/\s/g, "").toLowerCase(); 5 | let user = await g(`user:${k}`); 6 | if (typeof user === "string") user = await g(`user:${user}`); 7 | return user; 8 | } 9 | 10 | export default migrate; 11 | -------------------------------------------------------------------------------- /lib/locales/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "balanceAdjustment": "تسوية الرصيد", 3 | "paymentReceived": "المدفوعات الواردة", 4 | "hello": "مرحبا", 5 | "youReceived": "لقد حصلت على دفعة", 6 | "withdrawalTriggered": "هذا أدى إلى سحب", 7 | "problemWithdrawing": "كان هناك مشكلة إرسال إلى عنوان سحب السيارة", 8 | "insufficientFunds": "عدم كفاية الأموال" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/th.json: -------------------------------------------------------------------------------- 1 | { 2 | "balanceAdjustment": "ปรับสมดุล", 3 | "paymentReceived": "ได้รับค่าจ้าง", 4 | "hello": "สวัสดี", 5 | "youReceived": "คุณได้รับเงิน", 6 | "withdrawalTriggered": "นี่กระตุ้นให้เกิดการถอน", 7 | "problemWithdrawing": "เกิดปัญหาในการส่งที่อยู่โดยอัตโนมัติของคุณ", 8 | "insufficientFunds": "เงินทุนไม่เพียงพอ" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;;;;AAAA,0CAAoB;AACpB,kBAAe,+BAA+B,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,UAAC,CAAC,EAAE,CAAC;IAChE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAE,CAAC,YAAY,CAAC,sBAAe,CAAC,UAAO,CAAC,CAAC,CAAC;IAC9D,OAAO,CAAC,CAAC;AACb,CAAC,EAAE,EAAE,CAAC,CAAC"} -------------------------------------------------------------------------------- /routes/locations.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"locations.js","sourceRoot":"","sources":["locations.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8BAA4B;AAE5B,kBAAe;IACP,IAAI,YAAC,GAAG,EAAE,GAAG;;;;;4BACD,qBAAM,IAAA,MAAC,EAAC,WAAW,CAAC,EAAA;;wBAAhC,SAAS,GAAG,SAAoB;wBACpC,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,WAAA,EAAE,CAAC,CAAC;;;;;KACzB;CACF,CAAC"} -------------------------------------------------------------------------------- /lib/locales/fa.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "سلام", 3 | "youReceived": "شما پرداختی دریافت کردید", 4 | "withdrawalTriggered": "این باعث خروج از", 5 | "problemWithdrawing": "مشکلی در ارسال به آدرس برداشت خودکار شما وجود داشت", 6 | "paymentReceived": "پرداخت دریافت شد", 7 | "balanceAdjustment": "تعدیل تعادل تعادل", 8 | "insufficientFunds": "صندوق های بی کفایت" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/hi.json: -------------------------------------------------------------------------------- 1 | { 2 | "balanceAdjustment": "संतुलन समायोजन", 3 | "paymentReceived": "भुगतान प्राप्त", 4 | "hello": "नमस्ते", 5 | "youReceived": "आपको भुगतान प्राप्त हुआ", 6 | "withdrawalTriggered": "यह ट्रिगर की वापसी", 7 | "problemWithdrawing": "वहाँ एक समस्या अपने autowithdrawal पते पर भेज रहा था", 8 | "insufficientFunds": "अपर्याप्त निधि" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/bn.json: -------------------------------------------------------------------------------- 1 | { 2 | "balanceAdjustment": "মাপে পরিবর্তন", 3 | "paymentReceived": "মূল্য প্রদান করা হয়েছে", 4 | "hello": "হ্যালো", 5 | "youReceived": "আপনি পেমেন্ট পেয়েছিলাম", 6 | "withdrawalTriggered": "এটা প্রত্যাহারের সূত্রপাত ঘটিয়েছে", 7 | "problemWithdrawing": "আপনার অটো-রান ঠিকানায় প্রেরিত হয়েছে", 8 | "insufficientFunds": "অপর্যাপ্ত অর্থ" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "balanceAdjustment": "Denge ayarı", 3 | "paymentReceived": "Ödeme alındı", 4 | "hello": "Merhaba", 5 | "youReceived": "Bir ödeme aldın", 6 | "withdrawalTriggered": "Bu, bir geri çekilmeyi tetikledi", 7 | "problemWithdrawing": "AutoWithdrawal adresinize gönderilen bir sorun vardı", 8 | "insufficientFunds": "Yetersiz fonlarda" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/pl.json: -------------------------------------------------------------------------------- 1 | { 2 | "balanceAdjustment": "Korekta salda", 3 | "paymentReceived": "Otrzymane płatności", 4 | "hello": "Halo", 5 | "youReceived": "Otrzymałeś zapłatę", 6 | "withdrawalTriggered": "Wywołało to wycofanie", 7 | "problemWithdrawing": "Był problem z wysłaniem na twój adres autowycofania", 8 | "insufficientFunds": "Niewystarczające fundusze" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Hola", 3 | "youReceived": "Recibiste un pago de", 4 | "withdrawalTriggered": "Esto provocó la retirada de", 5 | "problemWithdrawing": "Hubo un problema al enviar a su dirección de retiro automático", 6 | "paymentReceived": "Pago recibido", 7 | "balanceAdjustment": "Ajuste del equilibrio", 8 | "insufficientFunds": "Fondos insuficientes" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "balanceAdjustment": "Balance adjustment", 3 | "paymentReceived": "Payment received", 4 | "hello": "Hello", 5 | "youReceived": "You received a payment of", 6 | "withdrawalTriggered": "This triggered a withdrawal of", 7 | "problemWithdrawing": "There was a problem sending to your autowithdrawal address", 8 | "insufficientFunds": "Insufficient funds" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "balanceAdjustment": "Regolazione del saldo", 3 | "paymentReceived": "Pagamento ricevuto", 4 | "hello": "Ciao", 5 | "youReceived": "Hai ricevuto un pagamento", 6 | "withdrawalTriggered": "Questo ha innescato un ritiro", 7 | "problemWithdrawing": "C'è stato un problema di inviare al tuo indirizzo di autoritiro", 8 | "insufficientFunds": "Fondi insufficienti" 9 | } 10 | -------------------------------------------------------------------------------- /sampledata/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";;AAAA,kBAAe;IACb,IAAI,EAAE,IAAI;IACV,KAAK,EAAE,iBAAiB;IACxB,MAAM,EAAE,+CAA+C;IACvD,QAAQ,EAAE,sCAAsC;IAChD,GAAG,EAAE,kEAAkE;IACvE,OAAO,EAAE;QACP,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,QAAQ;QAClB,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,KAAK;KACZ;IACD,SAAS,EAAE,6CAA6C;CACzD,CAAC"} -------------------------------------------------------------------------------- /lib/locales/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "balanceAdjustment": "Balansaanpassing", 3 | "paymentReceived": "Ontvangen betaling", 4 | "hello": "Hallo", 5 | "youReceived": "U ontving een betaling van", 6 | "withdrawalTriggered": "Dit leidde tot een intrekking van", 7 | "problemWithdrawing": "Er was een probleem met het verzenden naar uw automatisch intrekadres", 8 | "insufficientFunds": "Onvoldoende middelen" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/el.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Γειά σου", 3 | "youReceived": "Λάβατε μια πληρωμή του", 4 | "withdrawalTriggered": "Αυτό προκάλεσε την απόσυρση του", 5 | "problemWithdrawing": "Παρουσιάστηκε πρόβλημα κατά την αποστολή στη διεύθυνση αυτόματης απόσυρσής σας", 6 | "paymentReceived": "Η πληρωμή ελήφθη", 7 | "balanceAdjustment": "Προσαρμογή υπολοίπου", 8 | "insufficientFunds": "Ανεπαρκή κεφάλαια" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Olá", 3 | "youReceived": "Você recebeu um pagamento de", 4 | "withdrawalTriggered": "Isto desencadeou uma retirada de", 5 | "problemWithdrawing": "Ocorreu um problema ao enviar para seu endereço de retirada automática", 6 | "paymentReceived": "Pagamento recebido", 7 | "balanceAdjustment": "Ajustamento do saldo", 8 | "insufficientFunds": "Fundos insuficientes" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Привет", 3 | "youReceived": "Вы получили выплату в размере", 4 | "withdrawalTriggered": "Это послужило толчком к выводу", 5 | "problemWithdrawing": "Возникла проблема с отправкой на ваш адрес автоматического вывода средств.", 6 | "paymentReceived": "Платеж получен", 7 | "balanceAdjustment": "Корректировка баланса", 8 | "insufficientFunds": "Недостаточно средств" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Bonjour", 3 | "youReceived": "Vous avez reçu un paiement de", 4 | "withdrawalTriggered": "Cela a déclenché un retrait de", 5 | "problemWithdrawing": "Un problème est survenu lors de l'envoi à votre adresse de retrait automatique", 6 | "paymentReceived": "Paiement reçu", 7 | "balanceAdjustment": "Ajustement du solde", 8 | "insufficientFunds": "Insuffisance des fonds" 9 | } 10 | -------------------------------------------------------------------------------- /lib/locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "Hallo", 3 | "youReceived": "Sie haben eine Zahlung von erhalten", 4 | "withdrawalTriggered": "Dies löste einen Rückzug von aus", 5 | "problemWithdrawing": "Beim Senden an Ihre automatische Auszahlungsadresse ist ein Problem aufgetreten", 6 | "paymentReceived": "Zahlung erhalten", 7 | "balanceAdjustment": "Bilanzanpassung", 8 | "insufficientFunds": "Unzureichende Mittel" 9 | } 10 | -------------------------------------------------------------------------------- /routes/rates.ts: -------------------------------------------------------------------------------- 1 | import { g } from "$lib/db"; 2 | import { rate } from "$lib/rates"; 3 | 4 | export default { 5 | async fx(_, res) { 6 | const { fx } = await g("fx"); 7 | res.send({ fx }); 8 | }, 9 | 10 | async last(_, res) { 11 | res.send(rate || (await g("rate"))); 12 | }, 13 | 14 | async index(_, res) { 15 | const { date, fx, ...rates } = await g("rates"); 16 | res.send(rates); 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /lib/store.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | addresses: {}, 3 | challenge: {}, 4 | change: [], 5 | issuances: {}, 6 | logins: {}, 7 | nodes: [], 8 | seen: [], 9 | sessions: {}, 10 | sockets: {}, 11 | unaccounted: [], 12 | networks: [], 13 | rates: { USD: 20000 }, 14 | fx: {}, 15 | assets: {}, 16 | bcAddressIndex: 0, 17 | lqAddressIndex: 0, 18 | last: {}, 19 | payments: [], 20 | fetching: {}, 21 | timeouts: {}, 22 | }; 23 | -------------------------------------------------------------------------------- /sampledata/bitcoin/bitcoin.conf: -------------------------------------------------------------------------------- 1 | regtest=1 2 | testnet=0 3 | dnsseed=0 4 | upnp=0 5 | 6 | [regtest] 7 | fallbackfee=0.00002 8 | server=1 9 | txindex=1 10 | 11 | rpcuser=admin1 12 | rpcpassword=123 13 | rpcallowip=0.0.0.0/0 14 | rpcbind=0.0.0.0 15 | rpcwallet=coinos 16 | 17 | wallet=coinos 18 | wallet=external 19 | walletnotify=/home/bitcoin/.bitcoin/transaction.sh %s %w 20 | 21 | zmqpubrawblock=tcp://0.0.0.0:18506 22 | zmqpubrawtx=tcp://0.0.0.0:18507 23 | -------------------------------------------------------------------------------- /sampledata/liquid/elements.conf: -------------------------------------------------------------------------------- 1 | chain=liquidregtest 2 | listen=1 3 | txindex=1 4 | fallbackfee=0.00000002 5 | 6 | [liquidregtest] 7 | rpcuser=admin1 8 | rpcpassword=123 9 | rpcbind=0.0.0.0 10 | rpcallowip=0.0.0.0/0 11 | rpcwallet=coinos 12 | validatepegin=0 13 | initialfreecoins=2100000000000000 14 | wallet=coinos 15 | wallet=external 16 | wallet=raretoshi 17 | walletnotify=/home/elements/.elements/transaction.sh %s %w 18 | # con_blocksubsidy=5000000000 19 | -------------------------------------------------------------------------------- /lib/logging.ts: -------------------------------------------------------------------------------- 1 | import pino from "pino"; 2 | 3 | export const l = (...msgs) => pino().info(msgs.join(" ")); 4 | export const warn = (...msgs) => pino().warn(msgs.join(" ")); 5 | export const err = (...msgs) => pino().error(msgs.join(" ")); 6 | 7 | export const line = () => { 8 | const stack = new Error().stack; 9 | const stackLine = stack.split("\n")[1]; 10 | const match = stackLine.match(/at\s+(.*):(\d+):(\d+)/); 11 | return `${match[1]}:${match[2]}`; 12 | }; 13 | -------------------------------------------------------------------------------- /routes/rates.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"rates.js","sourceRoot":"","sources":["rates.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8BAA4B;AAE5B,kBAAe;IACP,IAAI,YAAC,GAAG,EAAE,GAAG;;;;;;wBACjB,KAAA,CAAA,KAAA,GAAG,CAAA,CAAC,IAAI,CAAA;wBAAC,qBAAM,IAAA,MAAC,EAAC,MAAM,CAAC,EAAA;;wBAAxB,cAAS,SAAe,EAAC,CAAC;;;;;KAC3B;IAEK,KAAK,YAAC,GAAG,EAAE,GAAG;;;;;;wBAClB,KAAA,CAAA,KAAA,GAAG,CAAA,CAAC,IAAI,CAAA;wBAAC,qBAAM,IAAA,MAAC,EAAC,OAAO,CAAC,EAAA;;wBAAzB,cAAS,SAAgB,EAAC,CAAC;;;;;KAC5B;CACF,CAAC"} -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | cp config.ts.sample config.ts 2 | cp compose.yml.sample compose.yml 3 | cp -r sampledata data 4 | sudo chown 100:100 data/nostr/data 5 | docker compose up -d 6 | docker run -it -v $(pwd):/home/bun/app --entrypoint bun asoltys/coinos-server i 7 | docker exec -it bc bitcoin-cli createwallet coinos 8 | docker exec -it bc bitcoin-cli rescanblockchain 9 | docker exec -it bc bitcoin-cli generatetoaddress 500 $(docker exec -it bc bitcoin-cli getnewaddress "" "p2sh-segwit") 10 | docker exec -it lq elements-cli createwallet coinos 11 | -------------------------------------------------------------------------------- /lib/store.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"store.js","sourceRoot":"","sources":["store.ts"],"names":[],"mappings":";;AAAA,kBAAe;IACb,SAAS,EAAE,EAAE;IACb,SAAS,EAAE,EAAE;IACb,MAAM,EAAE,EAAE;IACV,SAAS,EAAE,EAAE;IACb,MAAM,EAAE,EAAE;IACV,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,EAAE;IACR,QAAQ,EAAE,EAAE;IACZ,OAAO,EAAE,EAAE;IACX,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,EAAE;IACZ,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE;IACrB,EAAE,EAAE,EAAE;IACN,MAAM,EAAE,EAAE;IACV,cAAc,EAAE,CAAC;IACjB,cAAc,EAAE,CAAC;IACjB,IAAI,EAAE,EAAE;IACR,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;CACb,CAAC"} -------------------------------------------------------------------------------- /sampledata/index.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | port: 3119, 3 | nostr: "ws://nostr:8080", 4 | ipxapi: "4275|PqKjKaxIlzooToria2m4XFLzvHihSpsZMX4hvgxu", 5 | postmark: "f64dffd2-84ea-47c7-ba9f-95e053a2d0ae", 6 | jwt: "00d0ab4f7738a83feb37f661526512063c41e49278b7c32cba87314269a5788b", 7 | bitcoin: { 8 | host: "bitcoin", 9 | wallet: "coinos", 10 | username: "admin1", 11 | password: "123", 12 | network: "regtest", 13 | port: 18443, 14 | }, 15 | lightning: "/app/config/lightning/regtest/lightning-rpc", 16 | }; 17 | -------------------------------------------------------------------------------- /lib/whitelist.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"whitelist.js","sourceRoot":"","sources":["whitelist.ts"],"names":[],"mappings":";;AAAA,kBAAe;IACb,SAAS;IACT,OAAO;IACP,MAAM;IACN,cAAc;IACd,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,YAAY;IACZ,UAAU;IACV,aAAa;IACb,SAAS;IACT,OAAO;IACP,MAAM;IACN,WAAW;IACX,SAAS;IACT,QAAQ;IACR,YAAY;IACZ,SAAS;IACT,IAAI;IACJ,OAAO;IACP,MAAM;IACN,UAAU;IACV,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,MAAM;IACN,QAAQ;IACR,MAAM;IACN,KAAK;IACL,UAAU;IACV,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,MAAM;IACN,MAAM;IACN,cAAc;IACd,cAAc;IACd,OAAO;IACP,WAAW;IACX,OAAO;IACP,UAAU;IACV,UAAU;CACX,CAAC"} -------------------------------------------------------------------------------- /Dockerfile.bun: -------------------------------------------------------------------------------- 1 | FROM node:21 2 | 3 | ARG NODE_ENV=production 4 | ENV NODE_ENV $NODE_ENV 5 | 6 | RUN apt update 7 | RUN apt install gcc make pkg-config automake libpcre2-dev libtool git ffmpeg curl zip -y 8 | RUN curl -fsSL https://bun.sh/install | bash 9 | 10 | COPY . /app 11 | WORKDIR /app 12 | 13 | RUN NODE_ENV=development NODE_OPTIONS="" /root/.bun/bin/bun i 14 | WORKDIR /app/node_modules/bcrypt 15 | RUN npx node-pre-gyp install --fallback-to-build 16 | WORKDIR /app/node_modules/sharp 17 | RUN npm run install 18 | WORKDIR /app 19 | 20 | CMD ["node", "index.js"] 21 | -------------------------------------------------------------------------------- /templates/payments/verify.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 |
Hello {{username}},
12 | 13 |
14 | Please visit {{link}} to verify your email. 15 |
16 | 17 |
Thanks!
18 | 19 |
The Coinos Team
20 | 21 | 22 | -------------------------------------------------------------------------------- /config.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"config.js","sourceRoot":"","sources":["config.ts"],"names":[],"mappings":";;AAAA,kBAAe;IACb,OAAO,EAAE,iBAAiB;IAC1B,OAAO,EAAE,kBAAkB;IAC3B,QAAQ,EAAE,iEAAiE;IAC3E,EAAE,EAAE,YAAY;IAChB,KAAK,EAAE,iBAAiB;IACxB,MAAM,EAAE;QACN,iBAAiB;QACjB,+BAA+B;QAC/B,cAAc;QACd,oBAAoB;QACpB,sBAAsB;QACtB,4BAA4B;KAC7B;IACD,GAAG,EAAE,kEAAkE;IACvE,OAAO,EAAE;QACP,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,QAAQ;QAClB,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,KAAK;KACZ;IACD,MAAM,EAAE;QACN,IAAI,EAAE,IAAI;QACV,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,QAAQ;QAClB,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,IAAI;QACV,GAAG,EAAE,kEAAkE;KACxE;IACD,SAAS,EAAE,oDAAoD;CAChE,CAAC"} -------------------------------------------------------------------------------- /sampledata/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.default = { 4 | port: 3119, 5 | nostr: "ws://nostr:8080", 6 | ipxapi: "4275|PqKjKaxIlzooToria2m4XFLzvHihSpsZMX4hvgxu", 7 | postmark: "f64dffd2-84ea-47c7-ba9f-95e053a2d0ae", 8 | jwt: "00d0ab4f7738a83feb37f661526512063c41e49278b7c32cba87314269a5788b", 9 | bitcoin: { 10 | host: "bitcoin", 11 | wallet: "coinos", 12 | username: "admin1", 13 | password: "123", 14 | network: "regtest", 15 | port: 18443, 16 | }, 17 | lightning: "/app/config/lightning/regtest/lightning-rpc", 18 | }; 19 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /templates/payments/reset.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 |
Hello {{username}},
12 | 13 |
14 | Somebody requested to reset the password for your account. If it wasn't you, please ignore this email. Otherwise, please visit this link to reset your password: 15 |
16 | 17 |
18 | {{link}} 19 |
20 | 21 |
Thanks!
22 | 23 |
The Coinos Team
24 | 25 | 26 | -------------------------------------------------------------------------------- /lib/whitelist.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | "about", 3 | "admin", 4 | "anon", 5 | "autowithdraw", 6 | "balance", 7 | "banner", 8 | "currencies", 9 | "currency", 10 | "destination", 11 | "display", 12 | "email", 13 | "fiat", 14 | "fresh", 15 | "followers", 16 | "follows", 17 | "haspin", 18 | "hasprinter", 19 | "hidepay", 20 | "id", 21 | "index", 22 | "keys", 23 | "language", 24 | "linked", 25 | "locked", 26 | "locktime", 27 | "memoPrompt", 28 | "nip5", 29 | "notify", 30 | "npub", 31 | "nsec", 32 | "nwc", 33 | "payments", 34 | "picture", 35 | "profile", 36 | "prompt", 37 | "pubkey", 38 | "push", 39 | "reserve", 40 | "seed", 41 | "shopifyStore", 42 | "shopifyToken", 43 | "theme", 44 | "threshold", 45 | "tip", 46 | "twofa", 47 | "username", 48 | "verified", 49 | ]; 50 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Enable latest features 4 | "lib": ["ESNext"], 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "moduleDetection": "force", 8 | "jsx": "react-jsx", 9 | "allowJs": true, 10 | "moduleResolution": "nodenext", 11 | "allowImportingTsExtensions": true, 12 | 13 | // Best practices 14 | "strict": false, 15 | "skipLibCheck": true, 16 | "noFallthroughCasesInSwitch": true, 17 | 18 | // Some stricter flags 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noAssignInExpressions": false, 22 | 23 | "baseUrl": ".", 24 | "paths": { 25 | "$lib/*": ["./lib/*.ts"], 26 | "$routes/*": ["./routes/*.ts"], 27 | "$config": ["./config.ts"], 28 | "@cashu/cashu-ts": ["node_modules/@cashu/cashu-ts/dist/lib/es6/index.js"] 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/strfry.ts: -------------------------------------------------------------------------------- 1 | import * as net from "net"; 2 | import type { Event } from "nostr-tools"; 3 | 4 | const controlSocketPath = "/home/bun/app/data/sockets/strsrv.sock"; 5 | 6 | export const scan = (f: object): Promise => 7 | new Promise((resolve, reject) => { 8 | const result = []; 9 | const client = net 10 | .createConnection(controlSocketPath) 11 | .on("connect", () => { 12 | client.write(`${JSON.stringify(f)}\n`); 13 | }) 14 | .on("data", (data) => { 15 | result.push(data); 16 | }) 17 | .on("end", () => { 18 | resolve( 19 | Buffer.concat(result) 20 | .toString("utf8") 21 | .split("\n") 22 | .filter((l) => l) 23 | .map((l) => JSON.parse(l) as Event), 24 | ); 25 | }) 26 | .on("error", (e) => { 27 | reject(e.message); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /lib/webhooks.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import got from "got"; 3 | import { l, err } from "$lib/logging"; 4 | 5 | export const callWebhook = async (invoice, payment) => { 6 | try { 7 | if (!invoice || !payment) return; 8 | 9 | const { address, received, text, webhook, secret } = invoice; 10 | 11 | if (webhook) { 12 | const { amount, confirmed, hash, memo } = payment; 13 | 14 | l("calling webhook", webhook, amount, hash, address, text); 15 | const res = await got.post(webhook, { 16 | json: { 17 | address, 18 | amount, 19 | confirmed, 20 | hash, 21 | memo, 22 | received, 23 | text, 24 | secret, 25 | }, 26 | https: { rejectUnauthorized: false }, 27 | }); 28 | return res; 29 | } 30 | } catch (e) { 31 | err("problem calling webhook", e.message); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /lib/logging.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"logging.js","sourceRoot":"","sources":["logging.ts"],"names":[],"mappings":";;;;;;AAAA,8CAAwB;AAEjB,IAAI,CAAC,GAAG;IAAC,cAAO;SAAP,UAAO,EAAP,qBAAO,EAAP,IAAO;QAAP,yBAAO;;IAAK,OAAA,IAAA,cAAI,GAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAA3B,CAA2B,CAAC;AAA7C,QAAA,CAAC,KAA4C;AACjD,IAAI,IAAI,GAAG;IAAC,cAAO;SAAP,UAAO,EAAP,qBAAO,EAAP,IAAO;QAAP,yBAAO;;IAAK,OAAA,IAAA,cAAI,GAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAA3B,CAA2B,CAAC;AAAhD,QAAA,IAAI,QAA4C;AACpD,IAAI,GAAG,GAAG;IAAC,cAAO;SAAP,UAAO,EAAP,qBAAO,EAAP,IAAO;QAAP,yBAAO;;IAAK,OAAA,IAAA,cAAI,GAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAA5B,CAA4B,CAAC;AAAhD,QAAA,GAAG,OAA6C;AAEpD,IAAI,IAAI,GAAG;IAChB,IAAI,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC,KAAK,CAAC;IAC9B,IAAI,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACrD,OAAO,UAAG,KAAK,CAAC,CAAC,CAAC,cAAI,KAAK,CAAC,CAAC,CAAC,CAAE,CAAC;AACnC,CAAC,CAAC;AALS,QAAA,IAAI,QAKb"} -------------------------------------------------------------------------------- /routes/shopify.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"shopify.js","sourceRoot":"","sources":["shopify.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4CAAsB;AACtB,8BAA4B;AAC5B,wCAAmC;AAEnC,IAAI,KAAK,GAAG,kLAQV,CAAC;AAEH,mBAAe,+EAAO,EAAkC,EAAE,GAAG;;QAA7B,IAAI,eAAA,EAAc,EAAE,eAAA;;;oBAC1C,qBAAM,IAAA,MAAC,EAAC,kBAAW,IAAI,CAAE,CAAC,EAAA;;gBAA9B,CAAC,GAAG,SAA0B;qBAC9B,CAAA,OAAO,CAAC,KAAK,QAAQ,CAAA,EAArB,wBAAqB;gBAAM,qBAAM,IAAA,MAAC,EAAC,kBAAW,CAAC,CAAE,CAAC,EAAA;;gBAA3B,CAAC,GAAG,SAAuB,CAAC;;oBAC5C,qBAAM,IAAA,MAAC,EAAC,eAAQ,CAAC,CAAC,GAAG,CAAE,CAAC,EAAA;;gBAA/B,IAAI,GAAG,SAAwB;;;;gBAGzB,qBAAM,aAAG;yBACd,IAAI,CACH,kBAAW,IAAI,CAAC,YAAY,kDAA+C,EAC3E;wBACE,OAAO,EAAE;4BACP,cAAc,EAAE,kBAAkB;4BAClC,wBAAwB,EAAE,IAAI,CAAC,YAAY;yBAC5C;wBACD,IAAI,EAAE;4BACJ,KAAK,OAAA;4BACL,SAAS,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,8BAAuB,EAAE,CAAE,EAAE,EAAE;yBAC1D;qBACF,CACF;yBACA,IAAI,EAAE,EAAA;;gBAdL,CAAC,GAAG,SAcC;gBAET,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;;;gBAEZ,IAAA,aAAG,EAAC,uCAAuC,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;;;KAE3D,EAAC"} -------------------------------------------------------------------------------- /lib/webhooks.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["webhooks.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,4CAAsB;AACtB,wCAAsC;AAE/B,IAAM,WAAW,GAAG,UAAO,OAAO,EAAE,OAAO;;;;;;gBAE9C,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO;oBAAE,sBAAO;gBAE3B,OAAO,GAAsC,OAAO,QAA7C,EAAE,QAAQ,GAA4B,OAAO,SAAnC,EAAE,IAAI,GAAsB,OAAO,KAA7B,EAAE,OAAO,GAAa,OAAO,QAApB,EAAE,MAAM,GAAK,OAAO,OAAZ,CAAa;qBAEvD,OAAO,EAAP,wBAAO;gBACH,MAAM,GAA4B,OAAO,OAAnC,EAAE,SAAS,GAAiB,OAAO,UAAxB,EAAE,IAAI,GAAW,OAAO,KAAlB,EAAE,IAAI,GAAK,OAAO,KAAZ,CAAa;gBAEhD,IAAA,WAAC,EAAC,iBAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;gBACjD,qBAAM,aAAG,CAAC,IAAI,CAAC,OAAO,EAAE;wBAChC,IAAI,EAAE;4BACJ,OAAO,SAAA;4BACP,MAAM,QAAA;4BACN,SAAS,WAAA;4BACT,IAAI,MAAA;4BACJ,IAAI,MAAA;4BACJ,QAAQ,UAAA;4BACR,IAAI,MAAA;4BACJ,MAAM,QAAA;yBACP;wBACC,KAAK,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE;qBACvC,CAAC,EAAA;;gBAZE,GAAG,GAAG,SAYR;gBACF,sBAAO,GAAG,EAAC;;;;gBAGb,IAAA,aAAG,EAAC,yBAAyB,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;;;KAE7C,CAAC;AA5BW,QAAA,WAAW,eA4BtB"} -------------------------------------------------------------------------------- /mint.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import { createClient } from "redis"; 3 | import { 4 | CashuMint, 5 | CashuWallet, 6 | getEncodedTokenV4, 7 | MintQuoteState, 8 | } from "@cashu/cashu-ts"; 9 | 10 | const mintUrl = "http://mint:3338"; // the mint URL 11 | const mint = new CashuMint(mintUrl); 12 | const wallet = new CashuWallet(mint); 13 | let amt = 100000; 14 | const mintQuote = await wallet.createMintQuote(amt); 15 | console.log(mintQuote.request); 16 | 17 | let interval = setInterval(async () => { 18 | const mintQuoteChecked = await wallet.checkMintQuote(mintQuote.quote); 19 | if (mintQuoteChecked.state == MintQuoteState.PAID) { 20 | const { proofs } = await wallet.mintTokens(amt, mintQuote.quote); 21 | 22 | const token = getEncodedTokenV4({ 23 | token: [{ mint: mintUrl, proofs }], 24 | }); 25 | 26 | let db = createClient({ 27 | url: config.db, 28 | }); 29 | 30 | await db.connect(); 31 | await db.set("cash", token); 32 | 33 | clearInterval(interval); 34 | } 35 | }, 1000); 36 | -------------------------------------------------------------------------------- /lib/notifications.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"notifications.js","sourceRoot":"","sources":["notifications.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oDAA6B;AAC7B,sDAA+B;AAE/B,IAAI,iBAAM,CAAC,KAAK,EAAE,CAAC;IACjB,kBAAO,CAAC,eAAe,CACrB,iBAAM,CAAC,KAAK,CAAC,GAAG,EAChB,iBAAM,CAAC,KAAK,CAAC,SAAS,EACtB,iBAAM,CAAC,KAAK,CAAC,UAAU,CACxB,CAAC;AACJ,CAAC;AAEM,IAAM,MAAM,GAAG,UAAO,IAAI,EAAE,OAAO;;;;;;gBAEhC,aAAa,GAAK,IAAI,cAAT,CAAU;qBAEzB,aAAa,EAAb,wBAAa;gBACX,CAAC,GAAG,CAAC,CAAC;sBAC4B,EAAb,+BAAa;;;qBAAb,CAAA,2BAAa,CAAA;gBAA7B,YAAY;gBACnB,CAAC,EAAE,CAAC;;;;gBAEF,qBAAM,kBAAO,CAAC,gBAAgB,CAAC,YAAY,EAAE,OAAO,CAAC,EAAA;;gBAArD,SAAqD,CAAC;;;;qBAGpD,CAAA,GAAC,CAAC,IAAI;oBACN,CAAC,GAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,GAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAA,EAD1D,wBAC0D;gBAE1D,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBAChC,qBAAM,IAAI,CAAC,IAAI,EAAE,EAAA;;gBAAjB,SAAiB,CAAC;;;gBAElB,GAAG,CAAC,8BAA8B,EAAE,GAAC,CAAC,CAAC;;;;gBAZpB,IAAa,CAAA;;;;;gBAkBxC,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,GAAC,CAAC,CAAC;;;;;KAEvC,CAAC;AA1BW,QAAA,MAAM,UA0BjB"} -------------------------------------------------------------------------------- /routes/info.ts: -------------------------------------------------------------------------------- 1 | import { archive } from "$lib/db"; 2 | import { db, g } from "$lib/db"; 3 | import ln from "$lib/ln"; 4 | import { getDecodedToken } from "@cashu/cashu-ts"; 5 | 6 | export default { 7 | async balances(_, res) { 8 | let total = 0; 9 | 10 | // for await (const k of db.scanIterator({ MATCH: "balance:*" })) { 11 | // total += parseInt(await db.get(k)); 12 | // } 13 | // 14 | // for await (const k of archive.scanIterator({ MATCH: "balance:*" })) { 15 | // total += parseInt(await archive.get(k)); 16 | // } 17 | 18 | const funds = await ln.listfunds(); 19 | const lnchannel = parseInt( 20 | funds.channels.reduce((a, b) => a + b.channel_sat, 0), 21 | ); 22 | const lnwallet = parseInt(funds.outputs.reduce((a, b) => a + b.value, 0)); 23 | 24 | const cash = getDecodedToken(await g("cash")).proofs.reduce( 25 | (a, b) => a + b.amount, 26 | 0, 27 | ); 28 | 29 | const info = { 30 | cash, 31 | lnchannel, 32 | lnwallet, 33 | }; 34 | 35 | res.send(info); 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /sampledata/liquid/transaction.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use warnings; 4 | use IO::Socket::INET; 5 | 6 | # Auto-flush on socket 7 | $| = 1; 8 | 9 | # Creating a socket, connecting to the server 10 | my $socket = new IO::Socket::INET ( 11 | PeerHost => 'app', 12 | PeerPort => '3119', 13 | Proto => 'tcp', 14 | ) or die "ERROR in Socket Creation : $!\n"; 15 | 16 | my $txid = $ARGV[0] // die "txid not provided"; 17 | my $wallet = $ARGV[1] // die "wallet not provided"; 18 | 19 | # Define HTTP request 20 | my $json = "{\"txid\": \"$txid\", \"wallet\": \"$wallet\", \"type\": \"liquid\"}"; 21 | my $request = "POST /confirm HTTP/1.1\r\n"; 22 | $request .= "Host: app\r\n"; 23 | $request .= "Content-Type: application/json\r\n"; 24 | $request .= "Content-Length: " . length($json) . "\r\n"; 25 | $request .= "Connection: close\r\n"; 26 | $request .= "\r\n"; 27 | $request .= $json; 28 | 29 | # Send request to server 30 | print $socket "$request"; 31 | shutdown($socket, 1); 32 | 33 | # Read the response 34 | while (my $line = <$socket>) { 35 | print $line; 36 | } 37 | 38 | # Close the socket 39 | $socket->close(); 40 | -------------------------------------------------------------------------------- /lib/mail.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"mail.js","sourceRoot":"","sources":["mail.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wCAA4C;AAC5C,oDAA6B;AAC7B,kDAAgD;AAChD,kDAAuD;AACvD,0DAAoC;AACpC,0CAAoB;AAGpB,IAAI,OAAO,GAAG,OAAO,CAAC;AAEX,QAAA,SAAS,GAAG;IACrB,WAAW,EAAE,gCAAgC;IAC7C,eAAe,EAAE,kCAAkC;IACnD,aAAa,EAAE,+BAA+B;CAC/C,CAAC;AAEK,IAAI,IAAI,GAAG,UAAO,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM;;;;;;gBAEpD,IAAA,WAAC,EAAC,cAAc,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAC1C,IAAI,CAAC,IAAI,CAAC,KAAK;oBAAE,sBAAO;gBAEpB,MAAM,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC3C,IAAI,GAAG,oBAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;gBAE1C,MAAM,GAAG,IAAI,sBAAS,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;gBAEpD,qBAAM,MAAM,CAAC,IAAI,CACf,IAAI,6BAAgB,CAAC;wBACnB,WAAW,EAAE;4BACX,WAAW,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;yBAC1B;wBACD,OAAO,EAAE;4BACP,IAAI,EAAE;gCACJ,IAAI,EAAE,EAAE,OAAO,SAAA,EAAE,IAAI,EAAE,IAAI,EAAE;6BAC9B;4BACD,OAAO,EAAE,EAAE,OAAO,SAAA,EAAE,IAAI,EAAE,OAAO,EAAE;yBACpC;wBACD,MAAM,EAAE,uBAAc,iBAAM,CAAC,OAAO,MAAG;qBACxC,CAAC,CACH,EAAA;;gBAbD,SAaC,CAAC;;;;gBAEF,IAAA,aAAG,EAAC,sBAAsB,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;;;KAE1C,CAAC;AA3BS,QAAA,IAAI,QA2Bb"} -------------------------------------------------------------------------------- /lib/upload.ts: -------------------------------------------------------------------------------- 1 | import { createHash } from "crypto"; 2 | import { writeFileSync } from "fs"; 3 | import { err } from "$lib/logging"; 4 | import { bail, fail } from "$lib/utils"; 5 | import { fileTypeFromBuffer } from "file-type"; 6 | 7 | const sharp = require("sharp"); 8 | 9 | export default async (req, res) => { 10 | try { 11 | const { 12 | params: { type }, 13 | } = req; 14 | 15 | const data = await req.file(); 16 | let buf = await data.toBuffer(); 17 | 18 | const [format, ext] = (await fileTypeFromBuffer(buf)).mime.split("/"); 19 | 20 | if (format !== "image" && !["jpg", "jpeg", "png"].includes(ext)) 21 | fail("unsupported file type"); 22 | 23 | const w = type === "banner" ? 1920 : 240; 24 | buf = await sharp(buf, { failOnError: false }) 25 | .rotate() 26 | .resize(w) 27 | .webp() 28 | .toBuffer(); 29 | 30 | const hash = createHash("sha256").update(buf).digest("hex"); 31 | 32 | const filePath = `/home/bun/app/data/uploads/${hash}.webp`; 33 | writeFileSync(filePath, buf); 34 | 35 | res.send({ hash }); 36 | } catch (e) { 37 | err("problem uploading", e.message); 38 | bail(res, e.message); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /routes/invoices.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"invoices.js","sourceRoot":"","sources":["invoices.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8BAA+B;AAC/B,0CAAyC;AACzC,oCAA8C;AAM9C,kBAAe;IACP,GAAG;4DAAC,EAAkB,EAAE,GAAG;;gBAAX,EAAE,eAAA;;;4BACR,qBAAM,IAAA,MAAC,EAAC,kBAAW,EAAE,CAAE,CAAC,EAAA;;wBAAlC,OAAO,GAAG,SAAwB;6BAClC,CAAA,OAAO,OAAO,KAAK,QAAQ,CAAA,EAA3B,wBAA2B;wBAAY,qBAAM,IAAA,MAAC,EAAC,kBAAW,OAAO,CAAE,CAAC,EAAA;;wBAAvC,OAAO,GAAG,SAA6B,CAAC;;;6BAErE,OAAO,EAAP,wBAAO;wBACT,OAAO,OAAO,CAAC,MAAM,CAAC;wBACtB,KAAA,OAAO,CAAA;wBAAQ,KAAA,YAAI,CAAA;wBAAC,qBAAM,IAAA,MAAC,EAAC,eAAQ,OAAO,CAAC,GAAG,CAAE,CAAC,EAAA;;wBAAlD,GAAQ,IAAI,GAAG,kBAAK,SAA8B,EAAE;gCAClD,IAAI;gCACJ,SAAS;gCACT,QAAQ;gCACR,UAAU;gCACV,UAAU;gCACV,QAAQ;6BACT,EAAC,CAAC;wBAEH,OAAO,CAAC,KAAK,KAAb,OAAO,CAAC,KAAK,GAAK,EAAE,EAAC;;;wBAEvB,IAAI,OAAO;4BAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;;4BAC1B,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;;;;;KAC9C;IAEK,MAAM;4DAAC,EAAyC,EAAE,GAAG;;gBAA5C,YAAuB,EAAf,OAAO,aAAA,EAAE,IAAI,UAAA,EAAU,MAAM,UAAA;;;;;wBAEhD,KAAA,CAAA,KAAA,GAAG,CAAA,CAAC,IAAI,CAAA;wBAAC,qBAAM,IAAA,mBAAQ,EAAC,EAAE,OAAO,SAAA,EAAE,IAAI,MAAA,EAAE,MAAM,QAAA,EAAE,CAAC,EAAA;;wBAAlD,cAAS,SAAyC,EAAC,CAAC;;;;wBAEpD,IAAA,YAAI,EAAC,GAAG,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;;;;KAExB;CACF,CAAC"} -------------------------------------------------------------------------------- /routes/info.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"info.js","sourceRoot":"","sources":["info.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8BAAkC;AAClC,+CAAyB;AACzB,+CAAyB;AAEzB,kBAAe;IACP,QAAQ,YAAC,GAAG,EAAE,GAAG;;;;;;;wBAIjB,KAAK,GAAG,CAAC,CAAC;;;;mCAEM,KAAA,cAAA,YAAE,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAA;;;;;wBAAvC,cAAuC;wBAAvC,WAAuC;wBAA5C,CAAC,KAAA,CAAA;wBACd,KAAA,KAAK,CAAA;wBAAI,KAAA,QAAQ,CAAA;wBAAC,qBAAM,YAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAA;;wBAAjC,KAAK,GAAL,KAAS,kBAAS,SAAe,EAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;mCAGjB,KAAA,cAAA,YAAO,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAA;;;;;wBAA5C,cAA4C;wBAA5C,WAA4C;wBAAjD,CAAC,KAAA,CAAA;wBACd,KAAA,KAAK,CAAA;wBAAI,KAAA,QAAQ,CAAA;wBAAC,qBAAM,YAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAA;;wBAAtC,KAAK,GAAL,KAAS,kBAAS,SAAoB,EAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;6BAG5B,qBAAM,YAAE,CAAC,SAAS,EAAE,EAAA;;wBAA5B,KAAK,GAAG,SAAoB;wBAClC,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAC,CAAC,EAAE,CAAC,IAAK,OAAA,CAAC,GAAG,CAAC,CAAC,WAAW,EAAjB,CAAiB,EAAE,CAAC,CAAC,CAAC,CAAC;wBAC5E,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,UAAC,CAAC,EAAE,CAAC,IAAK,OAAA,CAAC,GAAG,CAAC,CAAC,KAAK,EAAX,CAAW,EAAE,CAAC,CAAC,CAAC,CAAC;wBAE9D,IAAI,GAAG;4BACX,SAAS,WAAA;4BACT,QAAQ,UAAA;4BACR,KAAK,OAAA;yBACN,CAAC;wBAEF,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;;;;KAChB;CACF,CAAC"} -------------------------------------------------------------------------------- /routes/shopify.ts: -------------------------------------------------------------------------------- 1 | import got from "got"; 2 | import { g } from "$lib/db"; 3 | import { l, err } from "$lib/logging"; 4 | import { getPayment } from "$lib/utils"; 5 | 6 | const query = `mutation orderMarkAsPaid($input: OrderMarkAsPaidInput!) { 7 | orderMarkAsPaid(input: $input) { 8 | order { id } 9 | userErrors { 10 | field 11 | message 12 | } 13 | } 14 | }`; 15 | 16 | export default async (req, res) => { 17 | const { 18 | body: { hash }, 19 | params: { id }, 20 | } = req; 21 | const p = await getPayment(hash); 22 | const user = await g(`user:${p.uid}`); 23 | 24 | l("marking shopify order paid", id, user.username, p.id, user.shopifyStore); 25 | 26 | try { 27 | const r = await got 28 | .post( 29 | `https://${user.shopifyStore}.myshopify.com/admin/api/2023-07/graphql.json`, 30 | { 31 | headers: { 32 | "Content-Type": "application/json", 33 | "X-Shopify-Access-Token": user.shopifyToken, 34 | }, 35 | json: { 36 | query, 37 | variables: { input: { id: `gid://shopify/Order/${id}` } }, 38 | }, 39 | }, 40 | ) 41 | .json(); 42 | 43 | l("shopify success", r); 44 | 45 | res.send(r); 46 | } catch (e) { 47 | err("problem marking shopify order as paid", e.message); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /lib/mail.ts: -------------------------------------------------------------------------------- 1 | import { l, err, warn } from "$lib/logging"; 2 | import config from "$config"; 3 | import { SESClient } from "@aws-sdk/client-ses"; 4 | import { SendEmailCommand } from "@aws-sdk/client-ses"; 5 | import handlebars from "handlebars"; 6 | import fs from "fs"; 7 | import path from "path"; 8 | 9 | const Charset = "UTF-8"; 10 | 11 | export const templates = { 12 | verifyEmail: "templates/payments/verify.html", 13 | paymentReceived: "templates/payments/received.html", 14 | passwordReset: "templates/payments/reset.html", 15 | }; 16 | 17 | export const mail = async (user, subject, template, params) => { 18 | try { 19 | l("sending mail", user.username, subject); 20 | if (!user.email) return; 21 | 22 | const source = fs.readFileSync(template, "utf8"); 23 | const html = handlebars.compile(source)(params); 24 | 25 | const client = new SESClient({ region: "us-east-2" }); 26 | 27 | await client.send( 28 | new SendEmailCommand({ 29 | Destination: { 30 | ToAddresses: [user.email], 31 | }, 32 | Message: { 33 | Body: { 34 | Html: { Charset, Data: html }, 35 | }, 36 | Subject: { Charset, Data: subject }, 37 | }, 38 | Source: `"Coinos " <${config.support}>`, 39 | }), 40 | ); 41 | } catch (e) { 42 | err("failed to send email", e.message); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /lib/locations.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"locations.js","sourceRoot":"","sources":["locations.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,wCAAmC;AACnC,4CAAsB;AACtB,8BAA+B;AAC/B,oCAA0C;AAEnC,IAAI,YAAY,GAAG;;;;;;;gBAEN,qBAAM,IAAA,aAAG,EACvB,uEAAuE,CACxE,CAAC,IAAI,EAAE,EAAA;;gBAFJ,SAAS,GAAG,SAER;gBAER,SAAS,GAAG,SAAS,CAAC,MAAM,CAC1B,UAAC,CAAC;oBACA,OAAA,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,KAAK;gBAApE,CAAoE,CACvE,CAAC;gBAEF,SAAS,CAAC,GAAG,CAAC,UAAC,CAAC;;oBACR,IAAA,MAAM,IAAR,KAAuB,CAAC,CAAC,UAAU,CAAC,YAA5B,EAAE,GAAG,SAAA,EAAE,GAAG,SAAA,CAAmB;oBAEzC,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;oBAC/D,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjE,CAAC,CAAC,CAAC;;;;2BAEiB,cAAA,cAAA,SAAS,CAAA;;;;;gBAAT,yBAAS;gBAAT,WAAS;gBAAd,CAAC,KAAA,CAAA;gBACV,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;qBACpC,QAAQ,EAAR,wBAAQ;gBACA,qBAAM,IAAA,MAAC,EAAC,eAAQ,QAAQ,CAAE,CAAC,EAAA;;gBAAjC,GAAG,GAAG,SAA2B;gBAC1B,qBAAM,IAAA,MAAC,EAAC,eAAQ,GAAG,CAAE,CAAC,EAAA;;gBAA7B,IAAI,GAAG,SAAsB;gBACjC,IAAI,IAAI;oBAAE,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,IAAA,YAAI,EAAC,IAAI,EAAE,cAAM,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;qBAI3D,qBAAM,IAAA,MAAC,EAAC,WAAW,EAAE,SAAS,CAAC,EAAA;;gBAA/B,SAA+B,CAAC;;;;gBAEhC,IAAA,aAAG,EAAC,4BAA4B,EAAE,GAAC,CAAC,CAAC;;;gBAGvC,UAAU,CAAC,oBAAY,EAAE,KAAK,CAAC,CAAC;;;;KACjC,CAAC;AAjCS,QAAA,YAAY,gBAiCrB"} -------------------------------------------------------------------------------- /lib/auth.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"auth.js","sourceRoot":"","sources":["auth.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+DAAgD;AAEhD,oCAA2C;AAC3C,oDAA6B;AAC7B,8DAA+B;AAElB,QAAA,KAAK,GAAG;IACnB,aAAa,EAAE,kBAAe,CAAC,YAAY,CACzC,KAAK,EACL,EAAE,OAAO,EAAE,KAAK,EAAE,EAClB,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI;QACjC,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3D,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC,CACF;CACF,CAAC;AAEW,QAAA,QAAQ,GAAG;IACtB,aAAa,EAAE,kBAAe,CAAC,YAAY,CACzC,KAAK,EACL,EAAE,OAAO,EAAE,KAAK,EAAE,EAClB,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI;QACjC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC,CACF;CACF,CAAC;AAEW,QAAA,IAAI,GAAG;IAClB,aAAa,EAAE,kBAAe,CAAC,YAAY,CAAC,KAAK,EAAE;QACjD,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,KAAK;KACf,CAAC;CACH,CAAC;AAEK,IAAM,UAAU,GAAG,iEAAO,EAAc;QAAZ,IAAI,UAAA,EAAE,IAAI,UAAA;;QAC3C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC;YAAE,IAAA,YAAI,EAAC,aAAa,CAAC,CAAC;;;KACvE,CAAC;AAFW,QAAA,UAAU,cAErB;AAEW,QAAA,WAAW,GAAG,IAAI,sBAAG,CAAC,QAAQ,CACzC;IACE,cAAc,EAAE,sBAAG,CAAC,UAAU,CAAC,cAAc,CAAC;QAC5C,sBAAG,CAAC,UAAU,CAAC,2BAA2B,EAAE;QAC5C,UAAC,GAAG,IAAK,OAAA,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAxC,CAAwC;KAClD,CAAC;IACF,WAAW,EAAE,iBAAM,CAAC,GAAG;CACxB,EACD,UAAO,OAAO,EAAE,IAAI;;;;oBACP,qBAAM,IAAA,eAAO,EAAC,OAAO,CAAC,EAAE,CAAC,EAAA;;gBAAhC,IAAI,GAAG,SAAyB;gBACpC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;;;;KAClB,CACF,CAAC"} -------------------------------------------------------------------------------- /routes/email.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"email.js","sourceRoot":"","sources":["email.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oCAAkC;AAClC,4CAAsB;AACtB,oDAA6B;AAC7B,wCAAuC;AACvC,kDAAgD;AAChD,kDAAuD;AAEvD,kBAAe;IACP,IAAI;4DAAC,EAAQ,EAAE,GAAG;;gBAAX,IAAI,UAAA;;;;;wBAEP,KAAK,GAAyC,IAAI,MAA7C,EAAE,OAAO,GAAgC,IAAI,QAApC,EAAE,QAAQ,GAAsB,IAAI,SAA1B,EAAS,QAAQ,GAAK,IAAI,MAAT,CAAU;wBAErD,OAAO,GAAG,OAAO,CAAC;wBAEL,MAAM,GAAK,iBAAM,UAAX,CAAY;wBACjB,qBAAM,aAAG;iCACxB,IAAI,CAAC,iDAAiD,EAAE;gCACvD,IAAI,EAAE;oCACJ,MAAM,QAAA;oCACN,QAAQ,UAAA;iCACT;6BACF,CAAC;iCACD,IAAI,EAAE,EAAA;;wBAPH,OAAO,GAAK,CAAA,SAOT,CAAA,QAPI;6BAST,CAAA,OAAO,IAAI,QAAQ,KAAK,iBAAM,CAAC,SAAS,CAAA,EAAxC,wBAAwC;wBAC1C,OAAO,IAAI,CAAC,KAAK,CAAC;wBAElB,IAAA,cAAI,EAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;wBAChC,MAAM,GAAG,IAAI,sBAAS,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;wBACpD,qBAAM,MAAM,CAAC,IAAI,CACf,IAAI,6BAAgB,CAAC;gCACnB,WAAW,EAAE;oCACX,WAAW,EAAE,EAAE;oCACf,WAAW,EAAE,CAAC,iBAAM,CAAC,OAAO,CAAC;iCAC9B;gCACD,OAAO,EAAE;oCACP,IAAI,EAAE;wCACJ,IAAI,EAAE,EAAE,OAAO,SAAA,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE;wCACvD,IAAI,EAAE,EAAE,OAAO,SAAA,EAAE,IAAI,EAAE,OAAO,EAAE;qCACjC;oCACD,OAAO,EAAE;wCACP,OAAO,SAAA;wCACP,IAAI,EACF,IAAI,CAAC,OAAO;4CACZ,iBAAiB,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;qCAC5D;iCACF;gCACD,gBAAgB,EAAE,CAAC,KAAK,CAAC;gCACzB,MAAM,EAAE,iBAAM,CAAC,OAAO;6BACvB,CAAC,CACH,EAAA;;wBArBD,SAqBC,CAAC;wBAEF,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;;;wBAEvB,IAAA,YAAI,EAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;;;;;wBAG9B,OAAO,CAAC,GAAG,CAAC,GAAC,CAAC,CAAC;;;;;;KAElB;CACF,CAAC"} -------------------------------------------------------------------------------- /config.ts.sample: -------------------------------------------------------------------------------- 1 | import { SquareEnvironment } from "square"; 2 | 3 | export default { 4 | archive: "redis://archive", 5 | db: "redis://db", 6 | nostr: "ws://nostr:8080", 7 | relays: [ 8 | "ws://nostr:8080", 9 | "wss://nostr-pub.wellorder.net", 10 | "wss://brb.io", 11 | "wss://nostr.v0l.io", 12 | "wss://relay.nostr.bg", 13 | "wss://nostr.orangepill.dev" 14 | ], 15 | jwt: "00d0ab4f7738a83feb37f661526512063c41e49278b7c32cba87314269a5788b", 16 | bitcoin: { 17 | host: "bc", 18 | wallet: "coinos", 19 | user: "admin1", 20 | password: "123", 21 | network: "regtest", 22 | port: 18443, 23 | }, 24 | liquid: { 25 | host: "lq", 26 | wallet: "coinos", 27 | user: "admin1", 28 | password: "123", 29 | network: "regtest", 30 | port: 7040, 31 | btc: "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225" 32 | }, 33 | lightning: "/home/bun/app/data/lightning/regtest/lightning-rpc", 34 | lightningb: "/home/bun/app/data/lightningb/regtest/lightning-rpc", 35 | fee: 0.001, 36 | adminpass: "pwa", 37 | support: "support@coinos.io", 38 | nostrKey: "nsec1msk3dqy2pzqy2murmm5s5gejfgkjkm2zesnh266gud2wcvhcunzqlu0h90", 39 | nostrKey2: "nsec17ugnsk0qrnakf7xaxzckya4xvx58q063k6q4vq2l577k4uyv9r0qmdcgm5", 40 | mintUrl: "http://mint:3338", 41 | square: { 42 | environment: SquareEnvironment.Sandbox, 43 | appId: "sandbox-sq0adb-9QbyDZeHTPc_RhFSEr_SaQ", 44 | url: "https://connect.squareupsandbox.com/", 45 | scopes: ["MERCHANT_PROFILE_READ", "ORDERS_READ", "PAYMENTS_READ"], 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /lib/square.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import { g, s } from "$lib/db"; 3 | import { fiat } from "$lib/utils"; 4 | import { SquareClient } from "square"; 5 | import { v4 } from "uuid"; 6 | 7 | const { appId, environment } = config.square; 8 | 9 | export const squarePayment = async (p, user) => { 10 | let square = await g(`${user.id}:square`); 11 | if (!(square && user.syncSquare)) return; 12 | 13 | let client = new SquareClient({ 14 | environment, 15 | }); 16 | 17 | const { refreshToken } = square; 18 | 19 | square = await client.oAuth.obtainToken({ 20 | clientId: appId, 21 | grantType: "refresh_token", 22 | refreshToken, 23 | }); 24 | 25 | await s(`${user.id}:square`, square); 26 | 27 | const { accessToken } = square; 28 | 29 | client = new SquareClient({ 30 | token: accessToken, 31 | environment, 32 | }); 33 | 34 | const locs = await client.locations.list({}); 35 | 36 | await client.payments.create({ 37 | sourceId: "EXTERNAL", 38 | idempotencyKey: v4(), 39 | amountMoney: { 40 | amount: BigInt(parseFloat(fiat(p.amount, p.rate).toFixed(2)) * 100), 41 | currency: p.currency, 42 | }, 43 | tipMoney: p.tip 44 | ? { 45 | amount: BigInt(parseFloat(fiat(p.tip, p.rate).toFixed(2)) * 100), 46 | currency: p.currency, 47 | } 48 | : undefined, 49 | autocomplete: true, 50 | externalDetails: { type: "OTHER", source: `Coinos payment ${p.id}` }, 51 | locationId: locs.locations[0].id, 52 | referenceId: v4(), 53 | note: `Coinos payment ${p.id}`, 54 | }); 55 | }; 56 | -------------------------------------------------------------------------------- /templates/payments/received.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | {{#if payment.items}} 12 |
13 | {{#each payment.items}} 14 |
{{this.quantity}} {{this.name}} {{this.totalFiat}}
15 | {{/each}} 16 |
17 | {{/if}} 18 | 19 |
20 | {{payment.fiat}} {{#if payment.tip}} + {{payment.fiatTip}}{{/if}} 21 |
22 | 23 |
24 | {{payment.amount}} {{#if payment.tip}} + {{payment.tip}}{{/if}} 25 |
26 | 27 | {{#if payment.memo}} 28 |
29 | Memo: {{payment.memo}} 30 |
31 | {{/if}} 32 | 33 |
34 | {{payment.link}} 35 |
36 | 37 | {{#if withdrawal.amount}} 38 |
39 | {{withdrawalTriggered}} {{withdrawal.amount}} 40 |
41 | 42 |
43 | {{withdrawal.link}} 44 |
45 | {{else if withdrawal.failed}} 46 |
47 | {{problemWithdrawing}} 48 |
49 | {{/if}} 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /lib/auth.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import { fail, getUser } from "$lib/utils"; 3 | import fastifyPassport from "@fastify/passport"; 4 | import jwt from "passport-jwt"; 5 | 6 | export const admin = { 7 | preValidation: fastifyPassport.authenticate( 8 | "jwt", 9 | { session: false }, 10 | (req, res, err, user, info) => { 11 | if (!user.admin) return res.code(401).send("unauthorized"); 12 | req.user = user; 13 | }, 14 | ), 15 | }; 16 | 17 | export const optional = { 18 | preValidation: fastifyPassport.authenticate( 19 | "jwt", 20 | { session: false }, 21 | (req, res, err, user, info) => { 22 | req.user = user; 23 | }, 24 | ), 25 | }; 26 | 27 | export const auth = { 28 | preValidation: fastifyPassport.authenticate("jwt", { 29 | authInfo: false, 30 | session: false, 31 | }), 32 | }; 33 | 34 | export const requirePin = async ({ body, user }) => { 35 | if (!user || (user.pin && user.pin !== body.pin)) fail("Invalid pin"); 36 | }; 37 | 38 | export const jwtStrategy = new jwt.Strategy( 39 | { 40 | jwtFromRequest: jwt.ExtractJwt.fromExtractors([ 41 | jwt.ExtractJwt.fromAuthHeaderAsBearerToken(), 42 | (req) => (req.cookies ? req.cookies.token : null), 43 | ]), 44 | secretOrKey: config.jwt, 45 | passReqToCallback: true, 46 | }, 47 | async (req, payload, next) => { 48 | const { originalUrl: u, method: m } = req; 49 | 50 | let { id } = payload; 51 | const wl = { GET: ["/invoice", "/payments"], POST: ["/invoice"] }; 52 | 53 | if (id.endsWith("-ro") && wl[m].some((p) => u.startsWith(p))) 54 | id = id.slice(0, -3); 55 | 56 | const user = await getUser(id); 57 | next(null, user); 58 | }, 59 | ); 60 | -------------------------------------------------------------------------------- /lib/types.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | about: string; 3 | admin: string; 4 | anon: string; 5 | autowithdraw: string; 6 | balance: string; 7 | banner: string; 8 | currencies: string; 9 | currency: string; 10 | destination: string; 11 | display: string; 12 | email: string; 13 | fiat: string; 14 | followers: string; 15 | follows: string; 16 | haspin: string; 17 | hasprinter: string; 18 | hidepay: string; 19 | id: string; 20 | index: string; 21 | keys: string; 22 | language: string; 23 | linked: string; 24 | locktime: string; 25 | memoPrompt: string; 26 | nip5: string; 27 | notify: string; 28 | npub: string; 29 | nsec: string; 30 | nwc: string; 31 | payments: string; 32 | picture: string; 33 | profile: string; 34 | prompt: string; 35 | pubkey: string; 36 | push: string; 37 | reserve: string; 38 | seed: string; 39 | shopifyStore: string; 40 | shopifyToken: string; 41 | theme: string; 42 | threshold: string; 43 | twofa: string; 44 | username: string; 45 | verified: string; 46 | } 47 | 48 | export enum PaymentType { 49 | internal = "internal", 50 | bitcoin = "bitcoin", 51 | lightning = "lightning", 52 | fund = "fund", 53 | liquid = "liquid", 54 | ecash = "ecash", 55 | reconcile = "reconcile", 56 | bolt12 = "bolt12", 57 | } 58 | 59 | export interface Payment { 60 | id: string; 61 | aid: string; 62 | amount: number; 63 | fee: number; 64 | hash: string; 65 | hex?: string; 66 | ourfee: number; 67 | memo?: string; 68 | iid: string; 69 | uid: string; 70 | confirmed: boolean; 71 | rate: number; 72 | currency: string; 73 | type: PaymentType; 74 | ref: string; 75 | tip?: number; 76 | created: number; 77 | user?: User; 78 | } 79 | -------------------------------------------------------------------------------- /lib/lightning.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"lightning.js","sourceRoot":"","sources":["lightning.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,gDA0CC;AAnDD,oCAAuC;AAIvC,+CAAyB;AACzB,wCAA4C;AAC5C,8BAAsC;AACtC,0CAA8C;AAE9C,SAAsB,kBAAkB;;;;;;oBACtB,KAAA,CAAA,KAAA,YAAE,CAAA,CAAC,cAAc,CAAA;oBAAE,qBAAM,IAAA,MAAC,EAAC,WAAW,CAAC,EAAA;wBAA7C,qBAAM,cAAkB,CAAC,SAAoB,CAAC,IAAI,CAAC,EAAC,EAAA;;oBAA1D,GAAG,GAAG,SAAoD;oBAE5D,MAAM,GAMJ,GAAG,OANC,EACN,WAAW,GAKT,GAAG,YALM,EACX,SAAS,GAIP,GAAG,UAJI,EACT,MAAM,GAGJ,GAAG,OAHC,EACN,oBAAoB,GAElB,GAAG,qBAFe,EACF,QAAQ,GACxB,GAAG,iBADqB,CACpB;oBAER,qBAAM,IAAA,MAAC,EAAC,WAAW,EAAE,SAAS,CAAC,EAAA;;oBAA/B,SAA+B,CAAC;oBAChC,UAAU,CAAC,kBAAkB,CAAC,CAAC;oBAE3B,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;;;;oBAGrD,IAAI,CAAC,QAAQ;wBAAE,sBAAO;oBAEb,qBAAM,IAAA,MAAC,EAAC,kBAAW,MAAM,CAAE,CAAC,EAAA;;oBAAjC,EAAE,GAAG,SAA4B;oBACvB,qBAAM,IAAA,MAAC,EAAC,kBAAW,EAAE,CAAE,CAAC,EAAA;;oBAAlC,OAAO,GAAG,SAAwB;oBACtC,IAAI,CAAC,OAAO;wBAAE,sBAAO,IAAA,cAAI,EAAC,oCAAoC,EAAE,MAAM,CAAC,EAAC;yBAEpE,CAAA,OAAO,IAAI,OAAO,CAAC,IAAI,CAAA,EAAvB,yBAAuB;;;;yBAEnB,CAAA,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,KAAK,IAAI,CAAA,EAArC,wBAAqC;oBAAE,qBAAM,IAAA,iBAAS,EAAC,GAAG,CAAC,EAAA;;oBAApB,SAAoB,CAAC;;;;;oBAEhE,IAAI,CAAC,GAAC,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC;wBACrC,IAAA,cAAI,EAAC,sBAAsB,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;yBAItC,qBAAM,IAAA,MAAC,EAAC,kBAAW,MAAM,CAAE,CAAC,EAAA;;oBAAhC,CAAC,GAAG,SAA4B;yBAChC,CAAA,OAAO,CAAC,KAAK,QAAQ,CAAA,EAArB,yBAAqB;oBAAM,qBAAM,IAAA,MAAC,EAAC,kBAAW,CAAC,CAAE,CAAC,EAAA;;oBAA3B,CAAC,GAAG,SAAuB,CAAC;;;oBAEvD,IAAI,CAAC;wBAAE,sBAAO,IAAA,cAAI,EAAC,mBAAmB,EAAE,MAAM,CAAC,EAAC;oBAEhD,qBAAM,IAAA,iBAAM,EAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,gBAAK,CAAC,SAAS,CAAC,EAAA;;oBAAvE,SAAuE,CAAC;;;;oBAExE,OAAO,CAAC,GAAG,CAAC,GAAC,CAAC,CAAC;oBACf,IAAA,aAAG,EAAC,qCAAqC,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;;;;CAEzD"} -------------------------------------------------------------------------------- /lib/app.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oDAA6B;AAC7B,oDAA8B;AAC9B,iEAAkD;AAClD,2DAA4C;AAC5C,+DAAgD;AAChD,2EAA2D;AAC3D,uDAAiC;AAEjC,8CAAwB;AACxB,8CAAwB;AAExB,kCAAwC;AAExC,IAAM,GAAG,GAAG,IAAA,iBAAO,EAAC;IAClB,MAAM,EAAE,IAAA,cAAI,GAAE;IACd,qBAAqB,EAAE,IAAI;IAC3B,cAAc,EAAE,GAAG;CACpB,CAAC,CAAC;AAEH,MAAM,GAAG,CAAC,QAAQ,kEAAQ,qBAAqB,QAAG;IAChD,SAAS,EAAE,UAAC,GAAG,EAAE,GAAG,IAAK,OAAA,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAA9B,CAA8B;IACvD,GAAG,EAAE,GAAG;IACR,UAAU,EAAE,IAAI;IAChB,YAAY,EAAE,UAAC,GAAG,IAAK,OAAA,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAA/B,CAA+B;IACtD,oBAAoB,EAAE,UAAC,GAAG,EAAE,OAAO;QACjC,OAAO;YACL,UAAU,EAAE,GAAG;YACf,KAAK,EAAE,mBAAmB;YAC1B,OAAO,EAAE,yCAAyC;SACnD,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,GAAG,CAAC,QAAQ,CAAC,wBAAoB,EAAE;IACjC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,iBAAM,CAAC,GAAG,EAAE,KAAK,CAAC;CACpC,CAAC,CAAC;AAEH,GAAG,CAAC,QAAQ,CAAC,kBAAe,CAAC,UAAU,EAAE,CAAC,CAAC;AAC3C,GAAG,CAAC,QAAQ,CAAC,kBAAe,CAAC,aAAa,EAAE,CAAC,CAAC;AAE9C,kBAAe,CAAC,GAAG,CAAC,KAAK,EAAE,kBAAW,CAAC,CAAC;AAExC,GAAG,CAAC,QAAQ,CAAC,mBAAgB,EAAE,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,SAAA,EAAE,EAAI,CAAC,CAAA,EAAE,EAAE,CAAC,CAAC;AAElE,GAAG,CAAC,QAAQ,CAAC,gBAAa,EAAE;IAC1B,IAAI,EAAE,cAAI,CAAC,IAAI,CAAC,4BAA4B,CAAC;IAC7C,MAAM,EAAE,UAAU;CACnB,CAAC,CAAC;AAEH,MAAM,GAAG,CAAC,QAAQ,CAAC,cAAI,EAAE;IACvB,MAAM,EAAE,IAAI;CACb,CAAC,CAAC;AAEH,GAAG,CAAC,eAAe,CAAC,UAAC,KAAK,EAAE,OAAO,EAAE,KAAK;IACxC,IAAI,KAAK,YAAY,iBAAO,CAAC,UAAU,CAAC,uBAAuB,EAAE,CAAC;QAChE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACxC,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,kBAAkB,CAAC,UAAC,OAAO,EAAE,KAAK;IACpC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AAEH,kBAAe,GAAG,CAAC"} -------------------------------------------------------------------------------- /routes/email.ts: -------------------------------------------------------------------------------- 1 | import { bail } from "$lib/utils"; 2 | import got from "got"; 3 | import config from "$config"; 4 | import { l, warn } from "$lib/logging"; 5 | import { SESClient } from "@aws-sdk/client-ses"; 6 | import { SendEmailCommand } from "@aws-sdk/client-ses"; 7 | 8 | export default { 9 | async send({ body }, res) { 10 | try { 11 | const { email, message, username, token: response } = body; 12 | 13 | const Charset = "UTF-8"; 14 | 15 | const { recaptcha: secret } = config; 16 | const { success } = await got 17 | .post("https://www.google.com/recaptcha/api/siteverify", { 18 | form: { 19 | secret, 20 | response, 21 | }, 22 | }) 23 | .json(); 24 | 25 | if (success || response === config.adminpass) { 26 | body.token = undefined; 27 | 28 | warn("support request from", email); 29 | const client = new SESClient({ region: "us-east-2" }); 30 | await client.send( 31 | new SendEmailCommand({ 32 | Destination: { 33 | CcAddresses: [], 34 | ToAddresses: [config.support], 35 | }, 36 | Message: { 37 | Body: { 38 | Html: { Charset, Data: message.replace(/\n/g, "
") }, 39 | Text: { Charset, Data: message }, 40 | }, 41 | Subject: { 42 | Charset, 43 | Data: 44 | body.subject || 45 | `Support Request${username ? ` From ${username}` : ""}`, 46 | }, 47 | }, 48 | ReplyToAddresses: [email], 49 | Source: config.support, 50 | }), 51 | ); 52 | 53 | res.send({ ok: true }); 54 | } else { 55 | bail(res, "failed captcha"); 56 | } 57 | } catch (e) { 58 | console.log(e); 59 | } 60 | }, 61 | }; 62 | -------------------------------------------------------------------------------- /lib/locations.ts: -------------------------------------------------------------------------------- 1 | import { g, s } from "$lib/db"; 2 | import { err } from "$lib/logging"; 3 | import { fields, getUser } from "$lib/utils"; 4 | import got from "got"; 5 | 6 | const dedup = (array) => 7 | Object.values( 8 | array.reduce((acc, obj) => { 9 | if ( 10 | !acc[obj.id] || 11 | new Date(obj.updated_at) > new Date(acc[obj.id].updated_at) 12 | ) { 13 | acc[obj.id] = obj; 14 | } 15 | return acc; 16 | }, {}), 17 | ); 18 | 19 | export const getLocations = async () => { 20 | try { 21 | const previous = (await g("locations")) || []; 22 | let since = await g("locations:since"); 23 | if (!since) since = "2022-09-19T00:00:00Z"; 24 | if (Date.now() - new Date(since).getTime() < 60000) return; 25 | 26 | let locations: Array = await got( 27 | `https://api.btcmap.org/v2/elements?updated_since=${since}`, 28 | ).json(); 29 | 30 | locations = locations.filter( 31 | (l) => 32 | l.osm_json.tags && 33 | l.osm_json.tags["payment:coinos"] === "yes" && 34 | l.osm_json.tags.name && 35 | !l.deleted_at, 36 | ); 37 | 38 | locations.map((l) => { 39 | const { bounds, lat, lon } = l.osm_json; 40 | 41 | l.osm_json.lat = lat || (bounds.minlat + bounds.maxlat) / 2; 42 | l.osm_json.lon = lon || (bounds.minlon + bounds.maxlon) / 2; 43 | }); 44 | 45 | for await (const l of locations) { 46 | const username = l.tags["payment:coinos"]; 47 | if (username) { 48 | const user = await getUser(username, fields); 49 | if (user) l.osm_json.tags.user = user; 50 | } 51 | } 52 | 53 | locations.push(...previous); 54 | 55 | await s("locations", dedup(locations)); 56 | await s("locations:since", `${new Date().toISOString().split(".")[0]}Z`); 57 | } catch (e) { 58 | console.log(e); 59 | err("problem fetching locations", e); 60 | } 61 | 62 | setTimeout(getLocations, 60000); 63 | }; 64 | -------------------------------------------------------------------------------- /lib/register.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"register.js","sourceRoot":"","sources":["register.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2BAA0B;AAC1B,iCAAuC;AACvC,6BAA0B;AAE1B,oDAA6B;AAC7B,6DAAuC;AACvC,8BAAwC;AACxC,wCAAuC;AACvC,oCAA2C;AAE3C,IAAI,KAAK,GAAG,uBAAuB,CAAC;AACpC,mBAAe,UAAO,IAAI,EAAE,EAAE,EAAE,gBAAgB;;;;;gBACxC,OAAO,GAAiC,IAAI,QAArC,EAAE,QAAQ,GAAuB,IAAI,SAA3B,EAAE,MAAM,GAAe,IAAI,OAAnB,EAAE,QAAQ,GAAK,IAAI,SAAT,CAAU;gBACnD,IAAA,WAAC,EAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;gBAE3B,IAAI,CAAC,QAAQ;oBAAE,IAAA,YAAI,EAAC,mBAAmB,CAAC,CAAC;gBACzC,QAAQ,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;gBAClC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;oBACvB,IAAA,YAAI,EAAC,6CAA6C,CAAC,CAAC;gBAElD,EAAE,GAAG,IAAA,SAAE,GAAE,CAAC;gBACd,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;gBAEA,qBAAM,IAAA,eAAO,EAAC,QAAQ,CAAC,EAAA;;gBAAhC,MAAM,GAAG,SAAuB;gBACpC,IAAI,MAAM;oBAAE,IAAA,YAAI,EAAC,mBAAY,QAAQ,WAAQ,CAAC,CAAC;qBAE3C,QAAQ,EAAR,wBAAQ;gBACV,KAAA,IAAI,CAAA;gBAAY,qBAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE;wBAChD,SAAS,EAAE,QAAQ;wBACnB,IAAI,EAAE,CAAC;qBACR,CAAC,EAAA;;gBAHF,GAAK,QAAQ,GAAG,SAGd,CAAC;;;gBAGL,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;qBAClB,iBAAM,CAAC,UAAU,EAAjB,wBAAiB;;;;gBAIb,qBAAM,IAAA,SAAG,EACX,oCAA6B,EAAE,kBAAQ,iBAAM,CAAC,UAAU,kCAA+B,CACxF,CAAC,IAAI,EAAE,EAAA;;gBAHiB,IAAI,GACzB,CAAA,SAEI,CAAA,sBAHqB;gBAK7B,IAAI,CAAC,QAAQ,GAAG,mBAAS,CAAC,IAAI,CAAC,CAAC;;;;gBAEhC,IAAA,cAAI,EAAC,kCAAkC,EAAE,QAAQ,CAAC,CAAC;;;gBAIvD,IAAI,CAAC,UAAU,qBAAO,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,OAAC,CAAC;gBAC9D,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;gBAClB,IAAI,CAAC,SAAS,GAAG,sBAAa,CAAC,cAAc,EAAE,CAAC;gBAChD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;gBACrB,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;gBAEpB,qBAAM,IAAA,MAAC,EAAC,eAAQ,EAAE,CAAE,EAAE,IAAI,CAAC,EAAA;;gBAA3B,SAA2B,CAAC;gBAC5B,qBAAM,IAAA,MAAC,EAAC,eAAQ,QAAQ,CAAE,EAAE,EAAE,CAAC,EAAA;;gBAA/B,SAA+B,CAAC;gBAChC,qBAAM,IAAA,MAAC,EAAC,eAAQ,MAAM,CAAE,EAAE,EAAE,CAAC,EAAA;;gBAA7B,SAA6B,CAAC;gBAC9B,qBAAM,IAAA,MAAC,EAAC,kBAAW,EAAE,CAAE,EAAE,CAAC,CAAC,EAAA;;gBAA3B,SAA2B,CAAC;gBAE5B,IAAA,WAAC,EAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;gBACxB,sBAAO,IAAI,EAAC;;;KACb,EAAC"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coinos", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "bun index.ts", 7 | "dev": "nodemon --exec bun index.ts", 8 | "format": "biome format *.js **/*.js --write" 9 | }, 10 | "dependencies": { 11 | "@asoltys/clightning-client": "^0.1.10", 12 | "@aws-sdk/client-ses": "^3.886.0", 13 | "@cashu/cashu-ts": "^2.7.2", 14 | "@coinos/rpc": "^0.0.6", 15 | "@fastify/cors": "^8.5.0", 16 | "@fastify/http-proxy": "^9.5.0", 17 | "@fastify/multipart": "^8.3.1", 18 | "@fastify/passport": "^2.5.0", 19 | "@fastify/rate-limit": "^9.1.0", 20 | "@fastify/secure-session": "^7.5.1", 21 | "@fastify/static": "^6.12.0", 22 | "@fastify/websocket": "^10.0.1", 23 | "@noble/hashes": "^1.8.0", 24 | "@scure/base": "^1.2.6", 25 | "bech32": "^2.0.0", 26 | "buffer-crc32": "^0.2.13", 27 | "clightning-client": "github:asoltys/lightning-client-js#master", 28 | "date-fns": "^2.30.0", 29 | "fastify": "^4.29.1", 30 | "file-type": "^18.7.0", 31 | "fluent-ffmpeg": "^2.1.3", 32 | "got": "^13.0.0", 33 | "handlebars": "^4.7.8", 34 | "jsonwebtoken": "^9.0.2", 35 | "lnurl": "^0.24.2", 36 | "mqtt": "^5.14.1", 37 | "nostr": "^0.2.8", 38 | "nostr-tools": "^2.16.2", 39 | "otplib": "^12.0.1", 40 | "passport-jwt": "^4.0.1", 41 | "passport-local": "1.0.0", 42 | "pino": "^8.21.0", 43 | "pino-pretty": "^7.6.1", 44 | "pump": "^3.0.3", 45 | "ramda": "^0.30.1", 46 | "readable-stream-clone": "^0.0.7", 47 | "redis": "^4.7.1", 48 | "sharp": "^0.33.5", 49 | "square": "^43.0.2", 50 | "strfry-rpc": "^0.0.1", 51 | "uuid": "^8.3.2", 52 | "web-push": "^3.6.7", 53 | "ws": "^7.5.10" 54 | }, 55 | "devDependencies": { 56 | "@biomejs/biome": "^1.9.4", 57 | "@types/bun": "^1.2.21", 58 | "nodemon": "^2.0.22", 59 | "typescript": "^5.9.2" 60 | }, 61 | "apidoc": { 62 | "title": "CoinOS Server API Documentation", 63 | "url": "https://coinos.io/api" 64 | }, 65 | "type": "module" 66 | } 67 | -------------------------------------------------------------------------------- /lib/upload.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"upload.js","sourceRoot":"","sources":["upload.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /routes/items.ts: -------------------------------------------------------------------------------- 1 | import { g, s, db } from "$lib/db"; 2 | import { v4 } from "uuid"; 3 | import { bail, fail } from "$lib/utils"; 4 | 5 | export default { 6 | async list(req, res) { 7 | const { 8 | params: { id: uid }, 9 | } = req; 10 | const items = []; 11 | for (const id of await db.lRange(`${uid}:items`, 0, -1)) { 12 | const item = await g(`item:${id}`); 13 | if (item) items.push(item); 14 | else await db.lRem(`${uid}:items`, 0, id); 15 | } 16 | 17 | res.send(items); 18 | }, 19 | 20 | async get(req, res) { 21 | const { 22 | params: { id }, 23 | } = req; 24 | try { 25 | const item = await g(`item:${id}`); 26 | if (!item) fail("Item not found"); 27 | res.send(item); 28 | } catch (e) { 29 | bail(res, e.message); 30 | } 31 | }, 32 | 33 | async create(req, res) { 34 | const { 35 | body: item, 36 | user: { id }, 37 | } = req; 38 | try { 39 | if (!item.id) { 40 | item.id = v4(); 41 | await db.lPush(`${id}:items`, item.id); 42 | } 43 | 44 | if (!parseFloat(item.price)) fail("Invalid price"); 45 | item.name = item.name.replace(/[^a-zA-Z0-9 ]/g, ""); 46 | 47 | await s(`item:${item.id}`, item); 48 | 49 | res.send(item); 50 | } catch (e) { 51 | bail(res, e.message); 52 | } 53 | }, 54 | 55 | async del(req, res) { 56 | const { 57 | body: { item }, 58 | user: { id }, 59 | } = req; 60 | try { 61 | const n = await db.lRem(`${id}:items`, 0, item.id); 62 | if (n) db.del(`item:${item.id}`); 63 | else fail("item not found"); 64 | 65 | res.send({}); 66 | } catch (e) { 67 | bail(res, e.message); 68 | } 69 | }, 70 | 71 | async sort(req, res) { 72 | const { 73 | body: { items }, 74 | user: { id }, 75 | } = req; 76 | try { 77 | await db.del(`${id}:items`); 78 | 79 | for (const item of items) { 80 | await db.rPush(`${id}:items`, item.id); 81 | } 82 | 83 | res.send({}); 84 | } catch (e) { 85 | bail(res, e.message); 86 | } 87 | }, 88 | }; 89 | -------------------------------------------------------------------------------- /lib/rates.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import { g, s } from "$lib/db"; 3 | import { err } from "$lib/logging"; 4 | import { sleep } from "$lib/utils"; 5 | import got from "got"; 6 | import WebSocket from "ws"; 7 | 8 | export let rate; 9 | let last; 10 | let ws; 11 | const connect = async () => { 12 | if (ws && ws.readyState === 1 && Date.now() - last < 5000) return; 13 | if (ws) ws.terminate() && (await sleep(Math.round(Math.random() * 1000))); 14 | 15 | ws = new WebSocket("wss://stream.binance.com:9443/ws/btcusdt@miniTicker"); 16 | 17 | ws.onmessage = async (event) => { 18 | try { 19 | const msg = JSON.parse(event.data); 20 | const rates = (await g("rates")) || {}; 21 | const { fx } = (await g("fx")) || {}; 22 | if (!fx) return; 23 | 24 | Object.keys(fx).map((symbol) => { 25 | rates[symbol] = msg.c * fx[symbol]; 26 | }); 27 | 28 | try { 29 | rates.IRT = ( 30 | (await got( 31 | "https://api.nobitex.ir/v2/orderbook/BTCIRT", 32 | ).json()) as any 33 | ).lastTradePrice; 34 | } catch (e) {} 35 | 36 | rate = msg.c; 37 | s("rate", rate); 38 | s("rates", rates); 39 | last = Date.now(); 40 | } catch (e) { 41 | console.log(e); 42 | err("binance message error", e.message); 43 | } 44 | }; 45 | 46 | ws.onerror = async (error) => { 47 | err("binance socket error", error.message); 48 | }; 49 | 50 | return ws; 51 | }; 52 | 53 | export const getFx = async () => { 54 | connect(); 55 | 56 | let date = 0; 57 | let fx = await g("fx"); 58 | if (fx) ({ date, fx } = fx); 59 | 60 | if (Date.now() - date > 24 * 60 * 60 * 1000) { 61 | date = Date.now(); 62 | try { 63 | if (config.fixer) { 64 | ({ rates: fx } = (await got( 65 | `http://data.fixer.io/api/latest?access_key=${config.fixer}&base=USD`, 66 | ).json()) as any); 67 | } else { 68 | ({ fx } = (await got("https://coinos.io/api/fx").json()) as any); 69 | } 70 | 71 | const USD = fx.USD; 72 | 73 | Object.keys(fx).map((k) => { 74 | fx[k] = fx[k] / USD; 75 | }); 76 | 77 | await s("fx", { date, fx }); 78 | } catch (e) { 79 | err("error fetching rates", e.message); 80 | } 81 | } 82 | 83 | setTimeout(getFx, 30000); 84 | }; 85 | -------------------------------------------------------------------------------- /lib/rates.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"rates.js","sourceRoot":"","sources":["rates.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /routes/items.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"items.js","sourceRoot":"","sources":["items.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/ecash.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"ecash.js","sourceRoot":"","sources":["ecash.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/ln.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | 3 | const mod = (await import("@asoltys/clightning-client")).default; 4 | const { LightningClient } = mod as { LightningClient: any }; 5 | 6 | class LightningUnavailableError extends Error { 7 | code = "LIGHTNING_UNAVAILABLE" as const; 8 | constructor(msg: string) { 9 | super(msg); 10 | this.name = "LightningUnavailableError"; 11 | } 12 | } 13 | 14 | function isUnavailable(e: any) { 15 | const code = e?.code ?? e?.errno; 16 | return code === "ENOENT" || code === 2 || code === "ECONNREFUSED"; 17 | } 18 | 19 | function isSocketDied(e: any) { 20 | const code = e?.code ?? e?.errno; 21 | return code === "EPIPE" || code === "ECONNRESET" || code === "ENOENT" || code === 2; 22 | } 23 | 24 | function lightningProxy(rpcPath: string) { 25 | let client: any = null; 26 | 27 | let nextTryAt = 0; 28 | let backoff = 250; 29 | const maxBackoff = 5000; 30 | 31 | function ensure() { 32 | if (client) return client; 33 | 34 | const now = Date.now(); 35 | if (now < nextTryAt) { 36 | throw new LightningUnavailableError(`Lightning not ready at ${rpcPath}`); 37 | } 38 | 39 | try { 40 | client = new LightningClient(rpcPath); 41 | backoff = 250; 42 | nextTryAt = 0; 43 | return client; 44 | } catch (e: any) { 45 | nextTryAt = Date.now() + backoff; 46 | backoff = Math.min(maxBackoff, Math.floor(backoff * 1.8)); 47 | 48 | if (isUnavailable(e)) { 49 | throw new LightningUnavailableError( 50 | `Lightning RPC unavailable at ${rpcPath} (${e?.code ?? e?.errno ?? "error"})` 51 | ); 52 | } 53 | throw e; 54 | } 55 | } 56 | 57 | return new Proxy( 58 | {}, 59 | { 60 | get(_t, prop: string | symbol) { 61 | if (prop === "toString") return () => "[LightningProxy]"; 62 | if (prop === Symbol.toStringTag) return "LightningProxy"; 63 | 64 | return async (...args: any[]) => { 65 | const c = ensure(); 66 | const v = c[prop as any]; 67 | 68 | if (typeof v !== "function") return v; 69 | 70 | try { 71 | return await v.apply(c, args); 72 | } catch (e: any) { 73 | if (isSocketDied(e)) client = null; 74 | throw e; 75 | } 76 | }; 77 | }, 78 | } 79 | ); 80 | } 81 | 82 | // exports preserved 83 | const ln = lightningProxy(config.lightning); 84 | export default ln; 85 | 86 | export const lnb = lightningProxy(config.lightningb); 87 | export { LightningUnavailableError }; 88 | -------------------------------------------------------------------------------- /routes/square.ts: -------------------------------------------------------------------------------- 1 | import * as crypto from "crypto"; 2 | import config from "$config"; 3 | import { db, g, s } from "$lib/db"; 4 | import { generate } from "$lib/invoices"; 5 | import { SATS, bail, getUser } from "$lib/utils"; 6 | import { sha256 } from "@noble/hashes/sha256"; 7 | import { utf8ToBytes } from "@noble/hashes/utils"; 8 | import { base64urlnopad as base64 } from "@scure/base"; 9 | import { SquareClient } from "square"; 10 | import { v4 } from "uuid"; 11 | 12 | const { scopes, url, appId, clientSecret, environment } = config.square; 13 | 14 | export default { 15 | async connect(req, res) { 16 | const { user } = req; 17 | 18 | if (await db.exists(`${user.id}:square`)) { 19 | return res.send("connected"); 20 | } 21 | 22 | const codeVerifier = base64.encode(crypto.randomBytes(32)); 23 | await s(`${user.id}:codeVerifier`, codeVerifier); 24 | 25 | const challenge = base64.encode(sha256(utf8ToBytes(codeVerifier))); 26 | 27 | const state = base64.encode(crypto.randomBytes(12)); 28 | const scope = scopes.join("+"); 29 | 30 | res.send( 31 | `${url}oauth2/authorize?client_id=${appId}&session=false&scope=${scope}&state=${state}&code_challenge=${challenge}`, 32 | ); 33 | }, 34 | 35 | async auth(req, res) { 36 | const { user } = req; 37 | 38 | const codeVerifier = await g(`${user.id}:codeVerifier`); 39 | 40 | const client = new SquareClient({ 41 | environment, 42 | }); 43 | 44 | const { code } = req.query; 45 | 46 | try { 47 | const result = await client.oAuth.obtainToken({ 48 | code, 49 | clientId: appId, 50 | grantType: "authorization_code", 51 | codeVerifier, 52 | }); 53 | 54 | await s(`${user.id}:square`, result); 55 | await s(result.merchantId, user.id); 56 | res.send({}); 57 | } catch (e) { 58 | console.log(e); 59 | } 60 | }, 61 | 62 | async payment(req, res) { 63 | try { 64 | const { body } = req; 65 | const { data, type, merchant_id } = body; 66 | const { payment } = data.object; 67 | 68 | if ( 69 | type === "payment.created" && 70 | (payment.source_type === "CASH" || payment.source_type === "EXTERNAL") 71 | ) { 72 | const { 73 | amount_money: { amount, currency }, 74 | } = payment; 75 | const rates = await g("rates"); 76 | const rate = rates[currency]; 77 | const uid = await g(merchant_id); 78 | const user = await getUser(uid); 79 | const invoice = { 80 | amount: Math.round(((amount / 100) * SATS) / rate), 81 | own: true, 82 | prompt: user.prompt, 83 | type: "lightning", 84 | }; 85 | 86 | await generate({ invoice, user }); 87 | } 88 | 89 | res.send({}); 90 | } catch (e) { 91 | bail(res, e.message); 92 | } 93 | }, 94 | }; 95 | -------------------------------------------------------------------------------- /routes/invoices.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import { db, g, s } from "$lib/db"; 3 | import { generate } from "$lib/invoices"; 4 | import { err } from "$lib/logging"; 5 | import { bail, fail, fields, getInvoice, getUser } from "$lib/utils"; 6 | import rpc from "@coinos/rpc"; 7 | 8 | export default { 9 | async get(req, res) { 10 | try { 11 | const { 12 | params: { id }, 13 | } = req; 14 | if (id === "undefined") fail("invalid id"); 15 | const invoice = await getInvoice(id); 16 | 17 | if (invoice) { 18 | invoice.secret = undefined; 19 | invoice.user = await getUser(invoice.uid, fields); 20 | 21 | invoice.items ||= []; 22 | } 23 | if (invoice) res.send(invoice); 24 | else fail("invoice not found"); 25 | } catch (e) { 26 | bail(res, e.message); 27 | } 28 | }, 29 | 30 | async create(req, res) { 31 | let { body, user } = req; 32 | const { invoice } = body; 33 | 34 | if (body.user) user = body.user; 35 | if (req.user.username === user.username) invoice.own = true; 36 | else invoice.own = false; 37 | 38 | try { 39 | const result = await generate({ invoice, user }); 40 | res.send(result); 41 | } catch (e) { 42 | err( 43 | "problem generating invoice", 44 | req.user?.username, 45 | body.user?.username, 46 | e.message, 47 | ); 48 | bail(res, e.message); 49 | } 50 | }, 51 | 52 | async update(req, res) { 53 | try { 54 | const { id } = req.params; 55 | const { body } = req; 56 | const { tip, webhook, secret } = body.invoice; 57 | 58 | if (tip < 0) fail("Invalid tip"); 59 | 60 | let invoice = await g(`invoice:${id}`); 61 | const user = await g(`user:${invoice.uid}`); 62 | 63 | if (typeof tip !== "undefined") invoice.tip = tip; 64 | 65 | if (webhook && secret) { 66 | if (invoice.uid !== req.user?.id) fail("Unauthorized"); 67 | invoice.webhook = webhook; 68 | invoice.secret = secret; 69 | } 70 | 71 | invoice = await generate({ invoice, user }); 72 | await s(`invoice:${id}`, invoice); 73 | 74 | res.send(invoice); 75 | } catch (e) { 76 | bail(res, e.message); 77 | } 78 | }, 79 | 80 | async list(req, res) { 81 | const { id } = req.user; 82 | let invoices = await db.lRange(`${id}:invoices`, 0, -1); 83 | invoices = (await Promise.all(invoices.map((i) => getInvoice(i)))).filter( 84 | Boolean, 85 | ); 86 | res.send(invoices); 87 | }, 88 | 89 | async sign(req, res) { 90 | try { 91 | const { address, message, type = "bitcoin" } = req.body; 92 | const node = rpc(config[type]); 93 | 94 | if (config[type].walletpass) 95 | await node.walletPassphrase(config[type].walletpass, 300); 96 | 97 | const signature = await node.signMessage({ address, message }); 98 | res.send({ signature }); 99 | } catch (e) { 100 | bail(res, e.message); 101 | } 102 | }, 103 | }; 104 | -------------------------------------------------------------------------------- /lib/register.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import countries from "$lib/countries"; 3 | import { db, s } from "$lib/db"; 4 | import { l, warn } from "$lib/logging"; 5 | import { fail } from "$lib/utils"; 6 | import { bytesToHex, randomBytes } from "@noble/hashes/utils"; 7 | import { got } from "got"; 8 | import { getPublicKey, nip19 } from "nostr-tools"; 9 | import { encrypt as nip49encrypt } from "nostr-tools/nip49"; 10 | import { authenticator } from "otplib"; 11 | import { v4 } from "uuid"; 12 | 13 | const valid = /^[\p{L}\p{N}]{2,24}$/u; 14 | export default async (user, ip) => { 15 | let { password, pubkey, username } = user; 16 | l("registering", username); 17 | 18 | const reserved = ["ecash"]; 19 | if (!username) fail("Username required"); 20 | username = username.toLowerCase().replace(/\s/g, ""); 21 | if (!valid.test(username)) 22 | fail("Usernames can only have letters and numbers"); 23 | if (reserved.includes(username)) fail("Invalid username"); 24 | 25 | const id = v4(); 26 | user.id = id; 27 | 28 | const exists = await db.exists(`user:${username}`); 29 | if (exists) fail(`Username ${username} taken`); 30 | 31 | if (password) { 32 | user.password = await Bun.password.hash(password, { 33 | algorithm: "bcrypt", 34 | cost: 4, 35 | }); 36 | } 37 | 38 | user.currency = "USD"; 39 | if (config.ipregistry) { 40 | try { 41 | const { 42 | location: { country: { code } }, 43 | }: any = await got( 44 | `https://api.ipregistry.co/${ip}?key=${config.ipregistry}&fields=location.country.code`, 45 | ).json(); 46 | 47 | user.currency = countries[code]; 48 | } catch (e) { 49 | warn("unable to detect country from IP", username); 50 | } 51 | } 52 | 53 | user.currencies = [...new Set([user.currency, "CAD", "USD"])]; 54 | user.fiat = false; 55 | user.otpsecret = authenticator.generateSecret(); 56 | user.migrated = true; 57 | user.locktime = 300; 58 | 59 | let sk; 60 | if (!pubkey) { 61 | sk = randomBytes(32); 62 | pubkey = getPublicKey(sk); 63 | user.pubkey = pubkey; 64 | user.nsec = nip49encrypt(sk, password); 65 | } 66 | 67 | user.npub = nip19.npubEncode(pubkey); 68 | 69 | const account = JSON.stringify({ 70 | id, 71 | type: "ecash", 72 | name: "Spending", 73 | }); 74 | 75 | const bytes = randomBytes(32); 76 | const secret = bytesToHex(bytes); 77 | const app = { 78 | uid: id, 79 | secret, 80 | pubkey: getPublicKey(bytes), 81 | max_amount: 1000000, 82 | budget_renewal: "weekly", 83 | name: username, 84 | created: Date.now(), 85 | }; 86 | 87 | await s(`app:${app.pubkey}`, app); 88 | await db.sAdd(`${id}:apps`, app.pubkey); 89 | 90 | db.multi() 91 | .set(`user:${id}`, JSON.stringify(user)) 92 | .set(`user:${username}`, id) 93 | .set(`user:${pubkey}`, id) 94 | .set(`balance:${id}`, 0) 95 | .set(`account:${id}`, account) 96 | .set(`${pubkey}:follows:n`, 0) 97 | .set(`${pubkey}:followers:n`, 0) 98 | .set(`${pubkey}:pubkeys`, "[]") 99 | .lPush(`${id}:accounts`, id) 100 | .exec(); 101 | 102 | l("new user", username); 103 | if (sk) user.sk = bytesToHex(sk); 104 | 105 | return user; 106 | }; 107 | -------------------------------------------------------------------------------- /lib/ecash.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import { g, s } from "$lib/db"; 3 | import { lnb } from "$lib/ln"; 4 | import { fail, wait } from "$lib/utils"; 5 | import { 6 | CashuMint, 7 | CashuWallet, 8 | MintQuoteState, 9 | PaymentRequest, 10 | PaymentRequestTransportType, 11 | getDecodedToken, 12 | getEncodedToken, 13 | getEncodedTokenV4, 14 | } from "@cashu/cashu-ts"; 15 | 16 | 17 | const { URL } = process.env; 18 | const m = new CashuMint(config.mintUrl); 19 | const w = new CashuWallet(m); 20 | 21 | const enc = (proofs) => 22 | getEncodedToken({ 23 | mint: config.mintUrl, 24 | proofs, 25 | }); 26 | 27 | const ext = async (mint) => { 28 | const issuer = new CashuMint(mint); 29 | const { pubkey: issuerPk } = await issuer.getInfo(); 30 | const { pubkey: ourPk } = await m.getInfo(); 31 | return issuerPk !== ourPk; 32 | }; 33 | 34 | export async function get(id) { 35 | const token = await g(`cash:${id}`); 36 | return token; 37 | } 38 | 39 | export async function claim(token) { 40 | const { proofs: current } = getDecodedToken(await g("cash")); 41 | const { mint } = getDecodedToken(token); 42 | 43 | if (await ext(mint)) fail("Unable to receive from other mints"); 44 | 45 | const rcvd = await w.receive(token); 46 | 47 | await s("cash", enc([...current, ...rcvd])); 48 | return rcvd.reduce((a, b) => a + b.amount, 0); 49 | } 50 | 51 | export async function mint(amount) { 52 | const { keysets } = await m.getKeySets(); 53 | const w = new CashuWallet(m, { keysets }); 54 | const { proofs } = getDecodedToken(await g("cash")); 55 | const { send, keep } = await w.send(amount, proofs); 56 | const rcvd = await w.receive(enc(send)); 57 | const change = enc(keep); 58 | await s("cash", change); 59 | return enc(rcvd); 60 | } 61 | 62 | export async function check(token) { 63 | const { mint, proofs } = getDecodedToken(token); 64 | const total = proofs.reduce((a, b) => a + b.amount, 0); 65 | 66 | const external = await ext(mint); 67 | 68 | let spent = 0; 69 | for (const [i, p] of (await w.checkProofsStates(proofs)).entries()) { 70 | if (p.state === "SPENT") spent += proofs[i].amount; 71 | } 72 | 73 | return { total, spent, mint, external }; 74 | } 75 | 76 | export async function init(amount = 100000) { 77 | try { 78 | await new Promise((r) => setTimeout(r, 2000)); 79 | const { quote, request } = await w.createMintQuote(amount); 80 | await lnb.pay(request); 81 | 82 | await wait(async () => { 83 | const { state } = await w.checkMintQuote(quote); 84 | return state === MintQuoteState.PAID; 85 | }); 86 | 87 | const proofs = await w.mintProofs(amount, quote); 88 | 89 | const cash = getEncodedTokenV4({ 90 | mint: config.mintUrl, 91 | proofs, 92 | }); 93 | 94 | await s("cash", cash); 95 | } catch (e) { 96 | console.log(e); 97 | } 98 | } 99 | 100 | export function request(uuid, amount, memo) { 101 | const target = `${URL}/api/ecash/${uuid}`; 102 | 103 | const { POST: type } = PaymentRequestTransportType; 104 | const transport = [{ type, target }]; 105 | const unit = "sat"; 106 | 107 | return new PaymentRequest( 108 | transport, 109 | uuid, 110 | amount, 111 | unit, 112 | [config.mintUrl], 113 | memo, 114 | ).toEncodedRequest(); 115 | } 116 | -------------------------------------------------------------------------------- /lib/db.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"db.js","sourceRoot":"","sources":["db.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /routes/ecash.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"ecash.js","sourceRoot":"","sources":["ecash.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /routes/lnurl.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"lnurl.js","sourceRoot":"","sources":["lnurl.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/lightning.ts: -------------------------------------------------------------------------------- 1 | import { db, g, s } from "$lib/db"; 2 | import ln from "$lib/ln"; 3 | import { err, warn } from "$lib/logging"; 4 | import { handleZap } from "$lib/nostr"; 5 | import { credit } from "$lib/payments"; 6 | import { PaymentType } from "$lib/types"; 7 | import { getInvoice, getPayment, getUser } from "$lib/utils"; 8 | 9 | export async function listenForLightning() { 10 | const inv = await ln.waitanyinvoice((await g("pay_index")) || 0); 11 | const { 12 | local_offer_id, 13 | bolt11, 14 | bolt12, 15 | description, 16 | pay_index, 17 | payment_hash, 18 | amount_received_msat, 19 | payment_preimage: preimage, 20 | } = inv; 21 | 22 | await s("pay_index", pay_index); 23 | setTimeout(listenForLightning); 24 | 25 | const received = Math.round(amount_received_msat / 1000); 26 | 27 | try { 28 | if (!preimage) return; 29 | 30 | const invoice = await getInvoice(bolt11 ?? local_offer_id ?? bolt12); 31 | if (!invoice) return warn("received lightning with no invoice", bolt11); 32 | 33 | const p = await getPayment(bolt11 || bolt12); 34 | if (p) return warn("already processed", bolt11 || bolt12); 35 | 36 | if (invoice?.memo) { 37 | try { 38 | if (JSON.parse(description).kind === 9734) { 39 | const { pubkey } = await getUser(invoice.uid); 40 | handleZap(inv, pubkey); 41 | } 42 | } catch (e) { 43 | if (!e.message.includes("Unexpected")) 44 | warn("failed to handle zap", e.message); 45 | } 46 | } 47 | 48 | await credit({ 49 | hash: bolt11 || bolt12, 50 | amount: received, 51 | memo: invoice.memo, 52 | ref: preimage, 53 | type: bolt12 ? PaymentType.bolt12 : PaymentType.lightning, 54 | payment_hash, 55 | }); 56 | } catch (e) { 57 | console.log(e); 58 | err("problem receiving lightning payment", e.message); 59 | } 60 | } 61 | 62 | export async function replay(index) { 63 | const inv = await ln.waitanyinvoice(index - 1); 64 | const { 65 | local_offer_id, 66 | bolt11, 67 | bolt12, 68 | description, 69 | amount_received_msat, 70 | payment_preimage: preimage, 71 | } = inv; 72 | 73 | const received = Math.round(amount_received_msat / 1000); 74 | 75 | try { 76 | if (!preimage) return; 77 | 78 | const invoice = await getInvoice(bolt11 ?? local_offer_id ?? bolt12); 79 | if (!invoice) return warn("received lightning with no invoice", bolt11); 80 | 81 | let p = await getPayment(bolt11 || bolt12); 82 | if (p) return warn("already processed", bolt11 || bolt12); 83 | 84 | if (invoice?.memo) { 85 | try { 86 | if (JSON.parse(description).kind === 9734) { 87 | const { pubkey } = await getUser(invoice.uid); 88 | handleZap(inv, pubkey); 89 | } 90 | } catch (e) { 91 | if (!e.message.includes("Unexpected")) 92 | warn("failed to handle zap", e.message); 93 | } 94 | } 95 | 96 | p = await credit({ 97 | hash: bolt11 || bolt12, 98 | amount: received, 99 | memo: invoice.memo, 100 | ref: preimage, 101 | type: bolt12 ? PaymentType.bolt12 : PaymentType.lightning, 102 | }); 103 | 104 | return p; 105 | } catch (e) { 106 | console.log(e); 107 | err("problem receiving lightning payment", e.message); 108 | } 109 | } 110 | 111 | export const fixBolt12 = async (_, res) => { 112 | for await (const k of db.scanIterator({ MATCH: "payment:*" })) { 113 | const p = await g(k); 114 | if (p.type === "bolt12") { 115 | console.log(k); 116 | const { invoices } = await ln.listinvoices({ invstring: p.hash }); 117 | const { local_offer_id } = invoices[0]; 118 | const oid = await g(`payment:${local_offer_id}`); 119 | const op = await g(`payment:${oid}`); 120 | if (op) { 121 | db.del(`payment:${oid}`); 122 | db.del(`payment:${local_offer_id}`); 123 | db.decrBy(`balance:${op.uid}`, op.amount); 124 | } 125 | } 126 | } 127 | 128 | res.send({}); 129 | }; 130 | -------------------------------------------------------------------------------- /lib/invoices.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"invoices.js","sourceRoot":"","sources":["invoices.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/notifications.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import { db, g } from "$lib/db"; 3 | import ln from "$lib/ln"; 4 | import { err, l, warn } from "$lib/logging"; 5 | import { mail, templates } from "$lib/mail"; 6 | import mqtt from "$lib/mqtt"; 7 | import { publish, serverSecret2 } from "$lib/nostr"; 8 | import { emit } from "$lib/sockets"; 9 | import { f, fiat, fmt, getUser, link, nada, t } from "$lib/utils"; 10 | import { hexToBytes } from "@noble/hashes/utils"; 11 | import { finalizeEvent, nip04 } from "nostr-tools"; 12 | import webpush from "web-push"; 13 | 14 | if (config.vapid) { 15 | webpush.setVapidDetails( 16 | `mailto:${config.support}`, 17 | config.vapid.pk, 18 | config.vapid.sk, 19 | ); 20 | } 21 | 22 | export const notify = async (p, user, withdrawal) => { 23 | emit(user.id, "payment", p); 24 | let { username } = user; 25 | const { paymentReceived } = t(user); 26 | username = username.replace(/\s/g, ""); 27 | 28 | try { 29 | if (user.verified && user.notify) { 30 | mail(user, paymentReceived, templates.paymentReceived, { 31 | ...t(user), 32 | username, 33 | payment: { 34 | amount: fmt(p.amount), 35 | link: link(p.id), 36 | tip: p.tip ? fmt(p.tip) : undefined, 37 | fiat: f(fiat(p.amount, p.rate), p.currency), 38 | fiatTip: p.tip ? f(fiat(p.tip, p.rate), p.currency) : undefined, 39 | memo: p.memo, 40 | items: p.items?.map((i) => { 41 | return { 42 | quantity: i.quantity, 43 | name: i.name, 44 | total: i.quantity * i.price, 45 | totalFiat: f(i.quantity * i.price, p.currency), 46 | }; 47 | }), 48 | }, 49 | withdrawal, 50 | }); 51 | } 52 | } catch (e) { 53 | err("problem emailing", e.message); 54 | } 55 | 56 | const subscriptions = await db.sMembers(`${user.id}:subscriptions`); 57 | 58 | const payload = { 59 | title: paymentReceived, 60 | body: `${fmt(p.amount)} ${f(fiat(p.amount, p.rate), p.currency)}`, 61 | url: `/payment/${p.id}`, 62 | }; 63 | 64 | for (const s of subscriptions) { 65 | webpush 66 | .sendNotification(JSON.parse(s), JSON.stringify(payload)) 67 | .catch((e) => { 68 | warn("sub failed", e.message); 69 | db.sRem(`${user.id}:subscriptions`, s); 70 | }); 71 | } 72 | 73 | if (config.mqtt) { 74 | if (!mqtt.connected) await mqtt.reconnect(); 75 | mqtt.publish( 76 | username, 77 | `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}:${p.memo}:${p.items}`, 78 | ); 79 | } 80 | }; 81 | 82 | export const nwcNotify = async (p) => { 83 | try { 84 | const user = await getUser(p.uid); 85 | const pubkeys = await db.sMembers(`${user.id}:apps`); 86 | if (pubkeys.length) { 87 | let payment_hash = ""; 88 | if (p.type === "lightning") ({ payment_hash } = await ln.decode(p.hash)); 89 | for (const pubkey of pubkeys) { 90 | const { notify } = await g(`app:${pubkey}`); 91 | if (!notify) continue; 92 | 93 | l("notifying", pubkey, p.type, p.amount); 94 | const notification = { 95 | type: p.amount > 0 ? "incoming" : "outgoing", 96 | invoice: p.hash, 97 | description: p.memo, 98 | preimage: p.ref, 99 | payment_hash: payment_hash, 100 | amount: Math.abs(p.amount) * 1000, 101 | fees_paid: (parseInt(p.fee) || 0) * 1000, 102 | created_at: Math.round(p.created / 1000), 103 | settled_at: Math.round(p.created / 1000), 104 | }; 105 | 106 | const payload = JSON.stringify({ 107 | notification_type: p.amount > 0 ? "payment_received" : "payment_sent", 108 | notification, 109 | }); 110 | 111 | const content = await nip04.encrypt(serverSecret2, pubkey, payload); 112 | 113 | const unsigned = { 114 | content, 115 | tags: [["p", pubkey]], 116 | kind: 23196, 117 | created_at: Math.floor(Date.now() / 1000), 118 | }; 119 | 120 | const event = finalizeEvent(unsigned, hexToBytes(serverSecret2)); 121 | 122 | publish(event).catch(nada); 123 | } 124 | } 125 | } catch (e) { 126 | console.log(e); 127 | warn("nwc notification failed", e.message); 128 | } 129 | }; 130 | -------------------------------------------------------------------------------- /lib/app.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import cors from "@fastify/cors"; 3 | import fastifyProxy from "@fastify/http-proxy"; 4 | import fastifyMultipart from "@fastify/multipart"; 5 | import fastifyPassport from "@fastify/passport"; 6 | import fastifyRateLimit from "@fastify/rate-limit"; 7 | import fastifySecureSession from "@fastify/secure-session"; 8 | import fastifyStatic from "@fastify/static"; 9 | import fastify from "fastify"; 10 | import pino from "pino"; 11 | 12 | import * as path from "path"; 13 | 14 | import { jwtStrategy } from "$lib/auth"; 15 | 16 | const app = fastify({ 17 | logger: true, 18 | disableRequestLogging: true, 19 | maxParamLength: 500, 20 | }); 21 | 22 | const reqLogger = pino(pino.destination("req")); 23 | const resLogger = pino(pino.destination("res")); 24 | 25 | // app.addHook("onRequest", async (req) => { 26 | // reqLogger.info({ url: req.raw.url, id: req.id }); 27 | // }); 28 | // 29 | 30 | app.addHook("preHandler", async (req) => { 31 | const url = req.raw.url; 32 | const ignore = [ 33 | "/login", 34 | "/ws", 35 | "/me", 36 | "/confirm", 37 | "/public", 38 | "/rates", 39 | "/challenge", 40 | "/rate", 41 | "/lnurlp", 42 | "/subscriptions", 43 | "/accounts", 44 | "/contacts", 45 | "/users", 46 | ]; 47 | if (ignore.some((path) => url.startsWith(path))) return; 48 | 49 | reqLogger.info({ 50 | method: req.method, 51 | url, 52 | headers: req.headers, 53 | query: req.query, 54 | body: req.body, 55 | user: (req.user as any)?.username, 56 | id: req.id, 57 | }); 58 | }); 59 | 60 | app.addHook("onResponse", async (req, reply) => { 61 | const rawCookies = req.raw.headers.cookie || ""; 62 | 63 | const cookies: any = rawCookies.split(";").reduce((acc, cookie) => { 64 | const [key, value] = cookie.split("=").map((s) => s.trim()); 65 | if (key && value) acc[key] = value; 66 | return acc; 67 | }, {}); 68 | 69 | resLogger.info({ 70 | id: req.id, 71 | url: req.raw.url, 72 | statusCode: reply.raw.statusCode, 73 | durationMs: reply.elapsedTime, 74 | username: cookies.username, 75 | }); 76 | }); 77 | 78 | app.register(fastifyRateLimit, { 79 | allowList: (req) => req.raw.url?.includes("public"), 80 | max: 2000, 81 | timeWindow: 2000, 82 | keyGenerator: (req) => { 83 | const ip = (req.headers["cf-connecting-ip"] as string) || req.ip; 84 | const ua = req.headers["user-agent"] || "unknown-ua"; 85 | return req.headers["rate-limit-by"] === "ua" ? ua : ip; 86 | }, 87 | errorResponseBuilder: () => { 88 | return { 89 | statusCode: 429, 90 | error: "Too Many Requests", 91 | message: "Rate limit exceeded, retry in 2 seconds", 92 | }; 93 | }, 94 | }); 95 | 96 | app.register(fastifyRateLimit, { 97 | allowList: (req) => { 98 | const url = req.raw.url || ""; 99 | const matches = url.includes("/login") || url.includes("/send"); 100 | return !matches; 101 | }, 102 | max: 10, 103 | timeWindow: 10000, 104 | keyGenerator: (req) => (req.headers["user-agent"] as string) || "unknown-ua", 105 | errorResponseBuilder: () => ({ 106 | statusCode: 429, 107 | error: "Too Many Requests", 108 | message: "Rate limit exceeded", 109 | }), 110 | }); 111 | 112 | app.register(fastifyProxy, { 113 | upstream: "http://localhost:3120", 114 | prefix: "/ws", 115 | rewritePrefix: "/ws", 116 | websocket: true, 117 | }); 118 | 119 | app.register(fastifySecureSession, { 120 | key: Buffer.from(config.jwt, "hex"), 121 | }); 122 | 123 | app.register(fastifyPassport.initialize()); 124 | app.register(fastifyPassport.secureSession()); 125 | 126 | fastifyPassport.use("jwt", jwtStrategy); 127 | 128 | app.register(fastifyMultipart, { limits: { fileSize: 10 ** 7 } }); 129 | 130 | app.register(fastifyStatic, { 131 | root: path.join("/home/bun/app/data/uploads"), 132 | prefix: "/public/", 133 | }); 134 | 135 | await app.register(cors, { 136 | origin: true, 137 | credentials: true, 138 | }); 139 | 140 | app.setErrorHandler((error, _, reply) => { 141 | if (error instanceof fastify.errorCodes.FST_ERR_BAD_STATUS_CODE) { 142 | reply.status(500).send({ ok: false }); 143 | } else { 144 | reply.send(error); 145 | } 146 | }); 147 | 148 | app.setNotFoundHandler((_, reply) => 149 | reply.code(404).type("text/html").send("Not Found"), 150 | ); 151 | 152 | export default app; 153 | -------------------------------------------------------------------------------- /compose.yml.sample: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | services: 3 | app: 4 | image: asoltys/coinos-server 5 | container_name: app 6 | depends_on: 7 | - db 8 | - bc 9 | - cl 10 | command: "bun run dev" 11 | environment: 12 | NODE_OPTIONS: "--import ./hooks.js" 13 | NODE_ENV: development 14 | URL: http://localhost:5173 15 | ports: 16 | - '3119:3119' 17 | volumes: 18 | - .aws/:/root/.aws 19 | - ./:/home/bun/app 20 | - ./logs:/logs 21 | - ./data/bitcoin/bitcoin.conf:/bitcoin.conf 22 | restart: always 23 | bc: 24 | image: asoltys/bitcoin 25 | container_name: bc 26 | ports: 27 | - '18443:18443' 28 | volumes: 29 | - ./data/bitcoin:/home/bitcoin/.bitcoin 30 | restart: always 31 | lq: 32 | image: asoltys/liquid:23.x 33 | container_name: lq 34 | volumes: 35 | - ./data/liquid:/home/elements/.elements 36 | restart: always 37 | ports: 38 | - 7043:7040 39 | cl: 40 | image: asoltys/cln 41 | container_name: cl 42 | environment: 43 | LIGHTNINGD_NETWORK: regtest 44 | volumes: 45 | - ./data/lightning:/root/.lightning 46 | restart: always 47 | clb: 48 | image: elementsproject/lightningd:v23.11.1-amd64 49 | container_name: clb 50 | environment: 51 | LIGHTNINGD_NETWORK: regtest 52 | volumes: 53 | - ./data/lightningb:/root/.lightning 54 | restart: always 55 | clc: 56 | image: elementsproject/lightningd:v23.11.1-amd64 57 | container_name: clc 58 | environment: 59 | LIGHTNINGD_NETWORK: regtest 60 | volumes: 61 | - ./data/lightningc:/root/.lightning 62 | restart: always 63 | db: 64 | image: eqalpha/keydb:alpine_x86_64_v6.3.4 65 | container_name: db 66 | ports: 67 | - '6379:6379' 68 | volumes: 69 | - ./data/db:/data 70 | - ./data/db/keydb.conf:/etc/keydb/keydb.conf 71 | restart: always 72 | archive: 73 | image: eqalpha/keydb:alpine_x86_64_v6.3.4 74 | container_name: archive 75 | ports: 76 | - '6380:6379' 77 | volumes: 78 | - ./data/archive:/data 79 | restart: always 80 | nostr: 81 | container_name: nostr 82 | image: asoltys/nostr-rs-relay 83 | restart: always 84 | user: 100:100 85 | volumes: 86 | - "./data/nostr/data:/usr/src/app/db" 87 | - "./data/nostr/config.toml:/usr/src/app/config.toml" 88 | ports: 89 | - '8082:8080' 90 | mint: 91 | image: asoltys/nutshell 92 | container_name: mint 93 | ports: 94 | - "3338:3338" 95 | environment: 96 | - MINT_CLNREST_URL=http://cl:3010 97 | - MINT_CLNREST_RUNE=lC9j5Z8-x7uN3oPqEGbpcNk2yknxQ4h5To5Z2Dp8ONE9Mg== 98 | - MINT_BACKEND_BOLT11_SAT=CLNRestWallet 99 | # - MINT_BACKEND_BOLT11_SAT=FakeWallet 100 | - MINT_LISTEN_HOST=0.0.0.0 101 | - MINT_LISTEN_PORT=3338 102 | - MINT_PRIVATE_KEY=TEST_PRIVATE_KEY 103 | - MINT_CLNREST_COINOS_URL=http://app:3119 104 | - MINT_CLNREST_COINOS_JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVjYTM0YWU4LTdiMTItNDU4OS04ODMyLTUyMTNjOWVhMjc5NyIsImlhdCI6MTcyNDA0NjI3OH0.bdX1YLnpgqpjmjrkCdIdDbdmZUdViNSVrAJ0OX3ByPo 105 | command: ["poetry", "run", "mint"] 106 | volumes: 107 | - "./data/nutshell:/app" 108 | - "./data/mint:/app/data/mint" 109 | mintb: 110 | image: asoltys/nutshell 111 | container_name: mintb 112 | ports: 113 | - "3339:3338" 114 | environment: 115 | - MINT_CLNREST_URL=http://clb:3010 116 | - MINT_CLNREST_RUNE=kTyXJ6yJwESVezCl4yB4z0zGMjXi11qggnKRQ4nnc2Y9MA== 117 | - MINT_BACKEND_BOLT11_SAT=CLNRestWallet 118 | # - MINT_BACKEND_BOLT11_SAT=FakeWallet 119 | - MINT_LISTEN_HOST=0.0.0.0 120 | - MINT_LISTEN_PORT=3338 121 | - MINT_PRIVATE_KEY=TEST_PRIVATE_KEYB 122 | - MINT_CLNREST_COINOS_URL=http://app:3119 123 | - MINT_CLNREST_COINOS_JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVjYTM0YWU4LTdiMTItNDU4OS04ODMyLTUyMTNjOWVhMjc5NyIsImlhdCI6MTcyNDA0NjI3OH0.bdX1YLnpgqpjmjrkCdIdDbdmZUdViNSVrAJ0OX3ByPo 124 | command: ["poetry", "run", "mint"] 125 | volumes: 126 | - "./data/nutshell:/app" 127 | - "./data/mint:/app/data/mint" 128 | 129 | wallet: 130 | image: asoltys/nutshell 131 | container_name: wallet 132 | ports: 133 | - "4448:4448" 134 | depends_on: 135 | - mint 136 | environment: 137 | - MINT_URL=http://mint:3338 138 | - API_HOST=0.0.0.0 139 | command: ["poetry", "run", "cashu", "-d"] -------------------------------------------------------------------------------- /lib/countries.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | AF: "AFN", 3 | AL: "ALL", 4 | DZ: "DZD", 5 | AD: "EUR", 6 | AO: "AOA", 7 | AI: "XCD", 8 | AQ: "USD", 9 | AR: "ARS", 10 | AM: "AMD", 11 | AW: "AWG", 12 | AU: "AUD", 13 | AT: "EUR", 14 | AZ: "AZN", 15 | BS: "BSD", 16 | BH: "BHD", 17 | BD: "BDT", 18 | BB: "BBD", 19 | BY: "BYN", 20 | BE: "EUR", 21 | BZ: "BZD", 22 | BJ: "XOF", 23 | BM: "BMD", 24 | BT: "BTN", 25 | BO: "BOB", 26 | BW: "BWP", 27 | BV: "NOK", 28 | BR: "BRL", 29 | IO: "USD", 30 | VG: "USD", 31 | BN: "BND", 32 | BG: "BGN", 33 | BF: "XOF", 34 | BI: "BIF", 35 | KH: "KHR", 36 | CM: "XAF", 37 | CA: "CAD", 38 | IC: "EUR", 39 | CV: "CVE", 40 | KY: "KYD", 41 | CF: "XAF", 42 | TD: "XAF", 43 | CL: "CLP", 44 | CN: "CNY", 45 | CX: "AUD", 46 | CC: "AUD", 47 | CO: "COP", 48 | KM: "KMF", 49 | CG: "XAF", 50 | CD: "CDF", 51 | CK: "NZD", 52 | CR: "CRC", 53 | CI: "XOF", 54 | HR: "HRK", 55 | CU: "CUP", 56 | CW: "ANG", 57 | CY: "EUR", 58 | CZ: "CZK", 59 | DK: "DKK", 60 | DJ: "DJF", 61 | DM: "XCD", 62 | DO: "DOP", 63 | TL: "USD", 64 | EC: "USD", 65 | EG: "EGP", 66 | SV: "USD", 67 | GQ: "XAF", 68 | ER: "ERN", 69 | EE: "EUR", 70 | ET: "ETB", 71 | FK: "FKP", 72 | FO: "DKK", 73 | FJ: "FJD", 74 | FI: "EUR", 75 | FR: "EUR", 76 | GF: "EUR", 77 | PF: "XPF", 78 | TF: "EUR", 79 | GA: "XAF", 80 | GM: "GMD", 81 | GE: "GEL", 82 | DE: "EUR", 83 | GH: "GHS", 84 | GI: "GIP", 85 | GR: "EUR", 86 | GL: "DKK", 87 | GD: "XCD", 88 | GP: "EUR", 89 | GU: "USD", 90 | GT: "GTQ", 91 | GG: "GBP", 92 | GN: "GNF", 93 | GW: "XOF", 94 | GY: "GYD", 95 | HT: "HTG", 96 | HM: "AUD", 97 | HN: "HNL", 98 | HK: "HKD", 99 | HU: "HUF", 100 | IS: "ISK", 101 | IN: "INR", 102 | ID: "IDR", 103 | IR: "IRT", 104 | IQ: "IQD", 105 | IE: "EUR", 106 | IM: "GBP", 107 | IL: "ILS", 108 | IT: "EUR", 109 | JM: "JMD", 110 | JP: "JPY", 111 | JE: "GBP", 112 | JO: "JOD", 113 | KZ: "KZT", 114 | KE: "KES", 115 | KI: "AUD", 116 | XK: "EUR", 117 | KW: "KWD", 118 | KG: "KGS", 119 | LA: "LAK", 120 | LV: "EUR", 121 | LB: "LBP", 122 | LS: "LSL", 123 | LR: "LRD", 124 | LY: "LYD", 125 | LI: "CHF", 126 | LT: "EUR", 127 | LU: "EUR", 128 | MO: "MOP", 129 | MK: "MKD", 130 | MG: "MGA", 131 | MW: "MWK", 132 | MY: "MYR", 133 | MV: "MVR", 134 | ML: "XOF", 135 | MT: "EUR", 136 | MH: "USD", 137 | MQ: "EUR", 138 | MR: "MRO", 139 | MU: "MUR", 140 | YT: "EUR", 141 | MX: "MXN", 142 | FM: "USD", 143 | MD: "MDL", 144 | MC: "EUR", 145 | MN: "MNT", 146 | ME: "EUR", 147 | MS: "XCD", 148 | MA: "MAD", 149 | MZ: "MZN", 150 | MM: "MMK", 151 | NA: "NAD", 152 | NR: "AUD", 153 | NP: "NPR", 154 | NL: "EUR", 155 | AN: "ANG", 156 | NC: "XPF", 157 | NZ: "NZD", 158 | NI: "NIO", 159 | NE: "XOF", 160 | NG: "NGN", 161 | NU: "NZD", 162 | NF: "AUD", 163 | KP: "KPW", 164 | MP: "USD", 165 | NO: "NOK", 166 | OM: "OMR", 167 | PK: "PKR", 168 | PW: "USD", 169 | PS: "ILS", 170 | PA: "PAB", 171 | PG: "PGK", 172 | PY: "PYG", 173 | PE: "PEN", 174 | PH: "PHP", 175 | PN: "NZD", 176 | PL: "PLN", 177 | PT: "EUR", 178 | PR: "USD", 179 | QA: "QAR", 180 | RE: "EUR", 181 | RO: "RON", 182 | RU: "RUB", 183 | RW: "RWF", 184 | BL: "EUR", 185 | SH: "SHP", 186 | KN: "XCD", 187 | LC: "XCD", 188 | MF: "EUR", 189 | PM: "EUR", 190 | VC: "XCD", 191 | WS: "WST", 192 | SM: "EUR", 193 | ST: "STN", 194 | SA: "SAR", 195 | SN: "XOF", 196 | RS: "RSD", 197 | SC: "SCR", 198 | SL: "SLL", 199 | SG: "SGD", 200 | SX: "ANG", 201 | SK: "EUR", 202 | SI: "EUR", 203 | SB: "SBD", 204 | SO: "SOS", 205 | ZA: "ZAR", 206 | GS: "GBP", 207 | KR: "KRW", 208 | SS: "SSP", 209 | ES: "EUR", 210 | LK: "LKR", 211 | SD: "SDG", 212 | SR: "SRD", 213 | SJ: "NOK", 214 | SZ: "SZL", 215 | SE: "SEK", 216 | CH: "CHF", 217 | SY: "SYP", 218 | TW: "TWD", 219 | TJ: "TJS", 220 | TZ: "TZS", 221 | TH: "THB", 222 | TG: "XOF", 223 | TK: "NZD", 224 | TO: "TOP", 225 | TT: "TTD", 226 | TA: "SHP", 227 | TN: "TND", 228 | TR: "TRY", 229 | TM: "TMT", 230 | TC: "USD", 231 | TV: "AUD", 232 | UM: "USD", 233 | VI: "USD", 234 | UG: "UGX", 235 | UA: "UAH", 236 | AE: "AED", 237 | GB: "GBP", 238 | US: "USD", 239 | UY: "UYU", 240 | UZ: "UZS", 241 | VU: "VUV", 242 | VA: "EUR", 243 | VE: "VES", 244 | VN: "VND", 245 | YE: "YER", 246 | ZM: "ZMW", 247 | ZW: "ZWL", 248 | }; 249 | -------------------------------------------------------------------------------- /routes/ecash.ts: -------------------------------------------------------------------------------- 1 | import { db, g, s } from "$lib/db"; 2 | import { check, claim, get, mint } from "$lib/ecash"; 3 | import { err, l } from "$lib/logging"; 4 | import { credit, debit } from "$lib/payments"; 5 | import { emit } from "$lib/sockets"; 6 | import { bail, fail, getInvoice } from "$lib/utils"; 7 | import { getEncodedToken } from "@cashu/cashu-ts"; 8 | import { v4 } from "uuid"; 9 | 10 | import { PaymentType } from "$lib/types"; 11 | const { ecash: type } = PaymentType; 12 | 13 | Error.stackTraceLimit = 100; // Set this to the desired limit 14 | 15 | const sendCash = async ({ amount, user }) => { 16 | const id = v4(); 17 | const hash = v4(); 18 | 19 | const p = await debit({ hash, amount, user, type }); 20 | const token = await mint(parseInt(amount)); 21 | p.memo = id; 22 | const { id: pid } = p; 23 | await s(`payment:${pid}`, p); 24 | s(`cash:${id}`, token); 25 | 26 | return { id, token, pid }; 27 | }; 28 | 29 | export default { 30 | async save(req, res) { 31 | const { 32 | body: { token }, 33 | } = req; 34 | try { 35 | const id = v4(); 36 | await s(`cash:${id}`, token); 37 | res.send({ id }); 38 | } catch (e) { 39 | err(e.message); 40 | bail(res, e.message); 41 | } 42 | }, 43 | 44 | async get(req, res) { 45 | const { 46 | params: { id }, 47 | } = req; 48 | try { 49 | const token = await get(id); 50 | const status = await check(token); 51 | res.send({ token, status }); 52 | } catch (e) { 53 | err(e.message); 54 | bail(res, e.message); 55 | } 56 | }, 57 | 58 | async claim(req, res) { 59 | const { 60 | body: { token }, 61 | user, 62 | } = req; 63 | try { 64 | const amount = await claim(token); 65 | 66 | let memo; 67 | const hash = v4(); 68 | const { currency, id: uid } = user; 69 | const rates = await g("rates"); 70 | await s(`invoice:${hash}`, { 71 | currency, 72 | id: hash, 73 | hash, 74 | rate: rates[currency], 75 | uid, 76 | received: 0, 77 | }); 78 | 79 | await credit({ hash, amount, memo, ref: user.id, type }); 80 | 81 | res.send({ ok: true }); 82 | } catch (e) { 83 | err(e.message); 84 | bail(res, e.message); 85 | } 86 | }, 87 | 88 | async mint(req, res) { 89 | const { body, user } = req; 90 | const amount = parseInt(body.amount); 91 | fail("disabled"); 92 | res.send(await sendCash({ amount, user })); 93 | }, 94 | 95 | async melt(req, res) { 96 | let { 97 | body: { amount, bolt11: hash, preimage }, 98 | user, 99 | } = req; 100 | try { 101 | amount = Math.round(amount / 1000); 102 | const ref = preimage; 103 | const { lightning: type } = PaymentType; 104 | if (user.username !== "mint") fail("unauthorized"); 105 | const { id: uid, currency } = user; 106 | const ourfee = await db.debit( 107 | `balance:${uid}`, 108 | `credit:${type}:${uid}`, 109 | "Insufficient funds", 110 | amount || 0, 111 | 0, 112 | 0, 113 | 0, 114 | 0 115 | ); 116 | 117 | const rates = await g("rates"); 118 | const rate = rates[currency]; 119 | 120 | if (ourfee.err) fail(ourfee.err); 121 | 122 | const id = v4(); 123 | const p = { 124 | id, 125 | amount: -amount, 126 | hash, 127 | ourfee, 128 | uid, 129 | confirmed: true, 130 | rate, 131 | currency, 132 | type, 133 | ref, 134 | created: Date.now(), 135 | }; 136 | 137 | await s(`payment:${hash}`, id); 138 | await s(`payment:${id}`, p); 139 | await db.lPush(`${uid}:payments`, id); 140 | 141 | l(user.username, "sent", type, amount); 142 | emit(user.id, "payment", p); 143 | 144 | res.send(p); 145 | } catch (e) { 146 | bail(res, e.message); 147 | } 148 | }, 149 | 150 | async receive(req, res) { 151 | try { 152 | const { id, proofs, mint, memo } = req.body; 153 | const { uid: ref } = await getInvoice(id); 154 | 155 | const amount = await claim( 156 | getEncodedToken({ 157 | mint, 158 | proofs, 159 | }), 160 | ); 161 | 162 | await credit({ hash: id, amount, memo, ref, type }); 163 | 164 | res.send({ id }); 165 | } catch (e) { 166 | console.log(e); 167 | err(e.message); 168 | bail(res, e.message); 169 | } 170 | }, 171 | }; 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coinos 2 | 3 | Coinos is a web-based bitcoin and nostr client. You can use it as a front end to your personal bitcoin and lightning nodes or host a public instance that allows anyone to register with a username and password. Try ours at https://coinos.io 4 | 5 | This repository contains the code for the API server. The frontend code is at https://github.com/coinos/coinos-ui 6 | 7 | ## Requirements 8 | 9 | - Make sure you have Docker installed in your system.. 10 | 11 | ## Quick Install 12 | 13 | 1. Start the Docker application. 14 | 15 | 2. Run the start up script: 16 | ```bash 17 | chmod +x setup.sh 18 | ./setup.sh 19 | ``` 20 | 21 | The above script will setup docker containers for the different components that Coinos needs to run. 22 | 23 | ## Understanding Coinos: 24 | 25 | Coinos comprises of the folowing components: 26 | - **The Coinos Server:** This is the main server implementation that setups and handles the API requests. Depends on the Bitcoin node, the Lightning node and the KeyDB database. 27 | - **[Bitcoin node](https://github.com/bitcoin/bitcoin):** This is the Bitcoin Core implementation that upholds the chains consensus rules. 28 | - **[Liquid Implementation](https://liquid.net/):** This is a Bitcoin layer-2 network that uses Bitcoin as its native asset and allows users to issue their own assets. 29 | - **[Core Lightning](https://docs.corelightning.org/docs/home):** This is also a Bitcoin layer-2 network that facilitates lightning fast Bitcoin payments via the Lightning Network protocol. 30 | - **[KeyDB](https://docs.keydb.dev/):** KeyDB is a high performance fork of Redis with a focus on multithreading, memory efficiency, and high throughput. 31 | - **[Nostr](https://nostr.com/):** Nostr is a simple, open protocol that enables global, decentralized, and censorship-resistant social media. 32 | - **[Cashu Nutshell](https://github.com/cashubtc/nutshell):** Nutshell is an Ecash wallet and mint for Bitcoin Lightning based on the Cashu protocol. 33 | 34 | A complete Coinos site utilizes all of the above components to support it's features. However, 35 | its important to note the most important ones: **Coinos Server**, **Bitcoin node**, **Core Lightning** and **KeyDB**. Without these, 36 | none of the Coinos features would work and the server would probably not start. 37 | 38 | If you want to run thin, you can remove the other containers 39 | from the setup(You will get to see where to do this later). 40 | 41 | ### How Coinos Works 42 | 43 | Running the startup script `./setup.sh` runs a few commands for you in the background. The commands are performed in this order: 44 | 45 | 1. `cp config.ts.sample config.ts`: 46 | 47 | A `config.ts` file is created and it's contents copied from the sample file `config.ts.sample`. This file holds the configuration options 48 | the various Coinos components discussed above. 49 | 50 | 2. `cp compose.yml.sample compose.yml` 51 | 52 | A `compose.yml` file is created and it's contents copied from the sample file `compose.yml.sample`. This file conatins instructions 53 | on the various Docker containers required to start Coinos. You will also notice duplicate component containers with different names such as cl,clb and clc. These are meant to easen the testing and development process locally as you can simulate transactions and connections. 54 | 55 | 3. `cp -r sampledata data` 56 | 57 | A folder `data` is created and it's contents copied from the `sampledata` folder. This directory contains various folders for the different components integrated by Coinos. This is where you will find specific configurations and settings for the different components such as Bitcoin and Lightning. 58 | 59 | 4. `docker compose up -d` and `docker run -it -v $(pwd):/home/bun/app --entrypoint bun asoltys/coinos-server i` 60 | 61 | Finally, the various Docker containers configured above are started and the Coinos server application is run. At this point, all containers should be running, otherwise it could indicate a problem with the previous tasks execution. You can check your container's status by running this command: `docker ps -a`. 62 | 63 | 5. `docker exec -it bc bitcoin-cli createwallet coinos` and `docker exec -it bc bitcoin-cli rescanblockchain` 64 | 65 | Bitcoin needs a wallet and to be synced to the blockchain to work. The startup script creates a Bitcoin wallet and rescans the blockchain to update it's state. By default, the chain is set to `regtest` so we also generate a few blocks and an address to get going. 66 | 67 | 6. `docker exec -it lq elements-cli createwallet coinos` 68 | 69 | Liquid requires a wallet also, thus we generate one for it. 70 | 71 | Basically, the startup script automates tasks you would have done manually and easen the process. You can also add your own custom commands that you would like executed when starting up. 72 | 73 | ### Tips 74 | -------------------------------------------------------------------------------- /lib/countries.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"countries.js","sourceRoot":"","sources":["countries.ts"],"names":[],"mappings":";;AAAA,kBAAe;IACb,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,KAAK;CACV,CAAC"} -------------------------------------------------------------------------------- /lib/db.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import { fail, sleep } from "$lib/utils"; 3 | import { createClient, defineScript } from "redis"; 4 | import { warn, err } from "$lib/logging"; 5 | 6 | const DEBIT = ` 7 | local balanceKey = KEYS[1] 8 | local creditKey = KEYS[2] 9 | local insufficientString = KEYS[3]; 10 | local amount = tonumber(ARGV[1]) 11 | local tip = tonumber(ARGV[2]) 12 | local fee = tonumber(ARGV[3]) 13 | local ourfee = tonumber(ARGV[4]) 14 | local frozen = tonumber(ARGV[5]) 15 | 16 | local balance = tonumber(redis.call('get', balanceKey) or '0') 17 | local credit = tonumber(redis.call('get', creditKey) or '0') 18 | 19 | local covered = math.min(credit, ourfee) 20 | ourfee = ourfee - covered 21 | 22 | if balance - frozen < amount + tip + fee + ourfee then 23 | return {err = insufficientString .. ' ⚡️' .. balance - frozen .. ' / ' .. amount + tip + fee + ourfee} 24 | end 25 | 26 | redis.call('decrby', creditKey, tostring(math.floor(covered))) 27 | redis.call('decrby', balanceKey, tostring(math.floor(amount + tip + fee + ourfee))) 28 | 29 | return ourfee 30 | `; 31 | 32 | const REVERSE = ` 33 | local paymentKey = KEYS[1] 34 | local balanceKey = KEYS[2] 35 | local creditKey = KEYS[3] 36 | local hashKey = KEYS[4] 37 | local paymentsKey = KEYS[5] 38 | local pid = KEYS[6] 39 | 40 | local total = tonumber(ARGV[1]) 41 | local credit = tonumber(ARGV[2]) 42 | local hash = ARGV[3] 43 | 44 | if redis.call('exists', paymentKey) == 1 then 45 | redis.call('del', paymentKey) 46 | redis.call('srem', 'pending', hash) 47 | redis.call('incrby', balanceKey, total) 48 | redis.call('incrby', creditKey, credit) 49 | redis.call('del', hashKey) 50 | redis.call('lrem', paymentsKey, 0, pid) 51 | redis.call('lrem', 'payments', 0, pid) 52 | return pid 53 | else 54 | error("Payment has already been reversed" .. paymentKey) 55 | end 56 | `; 57 | 58 | const debit = defineScript({ 59 | NUMBER_OF_KEYS: 3, 60 | SCRIPT: DEBIT, 61 | transformArguments: (...args) => args.map((a) => a.toString()), 62 | }); 63 | 64 | const reverse = defineScript({ 65 | NUMBER_OF_KEYS: 6, 66 | SCRIPT: REVERSE, 67 | transformArguments: (...args) => args.map((a) => a.toString()), 68 | }); 69 | 70 | export const db = createClient({ 71 | url: config.db, 72 | scripts: { debit, reverse }, 73 | socket: { 74 | reconnectStrategy: (retries) => Math.min(retries * 50, 5000), 75 | }, 76 | }); 77 | 78 | export const archive = createClient({ 79 | url: config.archive, 80 | socket: { 81 | reconnectStrategy: (retries) => Math.min(retries * 50, 5000), 82 | }, 83 | }); 84 | 85 | export const arc2 = createClient({ 86 | url: config.arc2, 87 | socket: { 88 | reconnectStrategy: (retries) => Math.min(retries * 50, 5000), 89 | }, 90 | }); 91 | 92 | async function dbReconnect() { 93 | try { 94 | await db.connect(); 95 | } catch (err) { 96 | console.error("Failed to connect to Redis, retrying...", err); 97 | setTimeout(dbReconnect, 5000); // Retry after 5 seconds 98 | } 99 | } 100 | 101 | async function archiveReconnect() { 102 | try { 103 | await archive.connect(); 104 | } catch (err) { 105 | console.error("Failed to connect to Redis, retrying...", err); 106 | setTimeout(archiveReconnect, 5000); // Retry after 5 seconds 107 | } 108 | } 109 | 110 | dbReconnect(); 111 | archiveReconnect(); 112 | 113 | db.on("error", (e) => { 114 | if (e.message.startsWith("getaddr")) return; 115 | err("Redis error", e.message); 116 | }); 117 | 118 | db.on("end", () => { 119 | warn("Redis connection ended"); 120 | }); 121 | 122 | export default db; 123 | 124 | export const g = async (k) => { 125 | const v = await db.get(k); 126 | try { 127 | return JSON.parse(v); 128 | } catch (e) { 129 | return v; 130 | } 131 | }; 132 | 133 | export const s = (k, v) => { 134 | if (k === "user:null" || k === "user:undefined") fail("null user"); 135 | db.set(k, JSON.stringify(v)); 136 | }; 137 | 138 | export const ga = async (k) => { 139 | const v = await archive.get(k); 140 | try { 141 | return JSON.parse(v); 142 | } catch (e) { 143 | return v; 144 | } 145 | }; 146 | 147 | export const sa = (k, v) => { 148 | if (k === "user:null" || k === "user:undefined") { 149 | warn("###### NULL USER #######"); 150 | console.trace(); 151 | } 152 | archive.set(k, JSON.stringify(v)); 153 | }; 154 | 155 | const retries = {}; 156 | export const t = async (k, f) => { 157 | try { 158 | await db.watch(k); 159 | await f(await db.get(k), db); 160 | } catch (err) { 161 | if (!err.message.includes("watch")) throw err; 162 | 163 | const r = retries[k] || 0; 164 | retries[k] = r + 1; 165 | 166 | if (r < 10) { 167 | await sleep(100); 168 | await t(k, f); 169 | } else { 170 | delete retries[k]; 171 | fail("unable to obtain lock"); 172 | } 173 | } 174 | 175 | delete retries[k]; 176 | }; 177 | -------------------------------------------------------------------------------- /lib/sockets.ts: -------------------------------------------------------------------------------- 1 | import store from "$lib/store"; 2 | import jwt from "jsonwebtoken"; 3 | import { v4 } from "uuid"; 4 | import { err, warn } from "$lib/logging"; 5 | import { fail, getUser } from "$lib/utils"; 6 | 7 | const code = 1000; 8 | const all = {}; 9 | const subscriptions = []; 10 | const users = {}; 11 | 12 | export const emit = (uid, type, data) => { 13 | if (type === "payment" && data.amount > 0) { 14 | for (let i = subscriptions.length - 1; i >= 0; i--) { 15 | const s = subscriptions[i]; 16 | 17 | if (s.invoice && s.invoice.id === data.iid) { 18 | s.ws.send(JSON.stringify({ type, data })); 19 | } 20 | } 21 | } 22 | 23 | if (!store.sockets[uid]) return; 24 | for (const id in store.sockets[uid]) { 25 | const ws = store.sockets[uid][id]; 26 | ws.send(JSON.stringify({ type, data })); 27 | } 28 | }; 29 | 30 | export const broadcast = (type, data) => { 31 | const sent = []; 32 | for (const uid in store.sockets) { 33 | for (const id in store.sockets[uid]) { 34 | const ws = store.sockets[uid][id]; 35 | ws.send(JSON.stringify({ type, data })); 36 | sent.push(ws.id); 37 | } 38 | } 39 | 40 | Object.values(all).map((ws: any) => { 41 | if (!sent.includes(ws.id)) ws.send(JSON.stringify({ type, data })); 42 | }); 43 | }; 44 | 45 | const track = async (ws, token) => { 46 | const { id } = ws; 47 | const { id: uid } = jwt.decode(token); 48 | 49 | if (!uid) fail("Invalid JWT token"); 50 | const user = await getUser(uid); 51 | if (!user) fail(`User not found ${uid}`); 52 | 53 | if (!store.sockets[uid]) store.sockets[uid] = {}; 54 | 55 | const existing = Object.keys(store.sockets[uid]); 56 | if (existing.length > 4) { 57 | const p = existing.find((sid) => sid !== uid); 58 | store.sockets[uid][p].close(code, "too many sockets"); 59 | } 60 | 61 | store.sockets[uid][id] = ws; 62 | users[id] = uid; 63 | ws.user = user; 64 | }; 65 | 66 | Bun.serve({ 67 | hostname: "0.0.0.0", 68 | port: 3120, 69 | fetch(req, server) { 70 | // upgrade the request to a WebSocket 71 | if (server.upgrade(req)) { 72 | return; // do not return a Response 73 | } 74 | return new Response("Upgrade failed", { status: 500 }); 75 | }, 76 | websocket: { 77 | async message(ws: any, message: string) { 78 | let type; 79 | let data; 80 | 81 | try { 82 | ({ type, data } = JSON.parse(message)); 83 | } catch (e) { 84 | err("coudn't parse socket message"); 85 | } 86 | 87 | switch (type) { 88 | case "heartbeat": 89 | ws.beats = 0; 90 | try { 91 | if (data) await track(ws, data); 92 | ws.send(JSON.stringify({ type: "id", data: ws.id })); 93 | } catch (e) { 94 | // err("Failed to send heartbeat", e.message); 95 | } 96 | break; 97 | 98 | case "login": 99 | try { 100 | if (!data || data === "null" || !jwt.decode(data)) 101 | return ws.close(code, `bad token ${data}`); 102 | 103 | await track(ws, data); 104 | } catch (e) { 105 | if (e.message.includes("not found")) 106 | ws.send(JSON.stringify({ type: "logout" })); 107 | setTimeout( 108 | () => ws?.close(code, `closing due to error ${e.message}`), 109 | 1000, 110 | ); 111 | } 112 | break; 113 | case "token": 114 | if (!ws.user) return; 115 | ws.token = data; 116 | break; 117 | case "lnurl": 118 | store.sessions[data] = ws; 119 | break; 120 | case "subscribe": 121 | subscriptions.push({ invoice: data, ws }); 122 | break; 123 | default: 124 | // warn("received socket message of unknown type", type, data); 125 | } 126 | }, 127 | open(ws: any) { 128 | const id = v4(); 129 | ws.id = id; 130 | ws.beats = 0; 131 | ws.send(JSON.stringify({ type: "connected", data: id })); 132 | all[id] = ws; 133 | }, 134 | close(ws: any) { 135 | const { id } = ws; 136 | try { 137 | const uid = users[id]; 138 | if (store.sockets[uid]?.[id]) { 139 | delete store.sockets[uid][id]; 140 | } 141 | 142 | let i; 143 | ~(i = subscriptions.findIndex((s) => s.ws.id === id)) && 144 | subscriptions.splice(i, 1); 145 | 146 | if (all[id]) delete all[id]; 147 | } catch (e) { 148 | err("problem closing socket", e.message); 149 | } 150 | }, 151 | }, 152 | }); 153 | 154 | export const sendHeartbeat = () => { 155 | for (const uid in store.sockets) { 156 | for (const id in store.sockets[uid]) { 157 | const ws = store.sockets[uid][id]; 158 | ws.beats++; 159 | if (ws.beats > 10) ws.close(code, `${uid} ${id} lost heartbeat`); 160 | } 161 | } 162 | }; 163 | -------------------------------------------------------------------------------- /sampledata/nostr/config.toml: -------------------------------------------------------------------------------- 1 | # Nostr-rs-relay configuration 2 | 3 | [info] 4 | # The advertised URL for the Nostr websocket. 5 | relay_url = "wss://nostr.example.com/" 6 | 7 | # Relay information for clients. Put your unique server name here. 8 | name = "nostr-rs-relay" 9 | 10 | # Description 11 | description = "A newly created nostr-rs-relay.\n\nCustomize this with your own info." 12 | 13 | # Administrative contact pubkey 14 | #pubkey = "0c2d168a4ae8ca58c9f1ab237b5df682599c6c7ab74307ea8b05684b60405d41" 15 | 16 | # Administrative contact URI 17 | #contact = "mailto:contact@example.com" 18 | 19 | [diagnostics] 20 | # Enable tokio tracing (for use with tokio-console) 21 | #tracing = true 22 | 23 | [database] 24 | # Directory for SQLite files. Defaults to the current directory. Can 25 | # also be specified (and overriden) with the "--db dirname" command 26 | # line option. 27 | data_directory = "." 28 | 29 | 30 | # Use an in-memory database instead of 'nostr.db'. 31 | # Caution; this will not survive a process restart! 32 | #in_memory = false 33 | 34 | # Database connection pool settings for subscribers: 35 | 36 | # Minimum number of SQLite reader connections 37 | #min_conn = 4 38 | 39 | # Maximum number of SQLite reader connections 40 | #max_conn = 128 41 | 42 | [network] 43 | # Bind to this network address 44 | address = "0.0.0.0" 45 | 46 | # Listen on this port 47 | port = 8080 48 | 49 | # If present, read this HTTP header for logging client IP addresses. 50 | # Examples for common proxies, cloudflare: 51 | #remote_ip_header = "x-forwarded-for" 52 | #remote_ip_header = "cf-connecting-ip" 53 | 54 | # Websocket ping interval in seconds, defaults to 5 minutes 55 | #ping_interval = 300 56 | 57 | [options] 58 | # Reject events that have timestamps greater than this many seconds in 59 | # the future. Recommended to reject anything greater than 30 minutes 60 | # from the current time, but the default is to allow any date. 61 | reject_future_seconds = 1800 62 | 63 | [limits] 64 | # Limit events created per second, averaged over one minute. Must be 65 | # an integer. If not set (or set to 0), defaults to unlimited. Note: 66 | # this is for the server as a whole, not per-connection. 67 | # messages_per_sec = 0 68 | 69 | # Limit client subscriptions created per second, averaged over one 70 | # minute. Must be an integer. If not set (or set to 0), defaults to 71 | # unlimited. 72 | #subscriptions_per_min = 0 73 | 74 | # UNIMPLEMENTED... 75 | # Limit how many concurrent database connections a client can have. 76 | # This prevents a single client from starting too many expensive 77 | # database queries. Must be an integer. If not set (or set to 0), 78 | # defaults to unlimited (subject to subscription limits). 79 | #db_conns_per_client = 0 80 | 81 | # Limit blocking threads used for database connections. Defaults to 16. 82 | #max_blocking_threads = 16 83 | 84 | # Limit the maximum size of an EVENT message. Defaults to 128 KB. 85 | # Set to 0 for unlimited. 86 | #max_event_bytes = 131072 87 | 88 | # Maximum WebSocket message in bytes. Defaults to 128 KB. 89 | #max_ws_message_bytes = 131072 90 | 91 | # Maximum WebSocket frame size in bytes. Defaults to 128 KB. 92 | #max_ws_frame_bytes = 131072 93 | 94 | # Broadcast buffer size, in number of events. This prevents slow 95 | # readers from consuming memory. 96 | #broadcast_buffer = 16384 97 | 98 | # Event persistence buffer size, in number of events. This provides 99 | # backpressure to senders if writes are slow. 100 | #event_persist_buffer = 4096 101 | 102 | [authorization] 103 | # Pubkey addresses in this array are whitelisted for event publishing. 104 | # Only valid events by these authors will be accepted, if the variable 105 | # is set. 106 | #pubkey_whitelist = [ 107 | # "35d26e4690cbe1a898af61cc3515661eb5fa763b57bd0b42e45099c8b32fd50f", 108 | # "887645fef0ce0c3c1218d2f5d8e6132a19304cdc57cd20281d082f38cfea0072", 109 | #] 110 | 111 | [verified_users] 112 | # NIP-05 verification of users. Can be "enabled" to require NIP-05 113 | # metadata for event authors, "passive" to perform validation but 114 | # never block publishing, or "disabled" to do nothing. 115 | #mode = "disabled" 116 | 117 | # Domain names that will be prevented from publishing events. 118 | #domain_blacklist = ["wellorder.net"] 119 | 120 | # Domain names that are allowed to publish events. If defined, only 121 | # events NIP-05 verified authors at these domains are persisted. 122 | #domain_whitelist = ["example.com"] 123 | 124 | # Consider an pubkey "verified" if we have a successful validation 125 | # from the NIP-05 domain within this amount of time. Note, if the 126 | # domain provides a successful response that omits the account, 127 | # verification is immediately revoked. 128 | #verify_expiration = "1 week" 129 | 130 | # How long to wait between verification attempts for a specific author. 131 | #verify_update_frequency = "24 hours" 132 | 133 | # How many consecutive failed checks before we give up on verifying 134 | # this author. 135 | #max_consecutive_failures = 20 136 | -------------------------------------------------------------------------------- /lib/invoices.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import { db, g, s } from "$lib/db"; 3 | import { request } from "$lib/ecash"; 4 | import ln from "$lib/ln"; 5 | import { emit } from "$lib/sockets"; 6 | import { SATS, bip21, fail, getInvoice, getUser } from "$lib/utils"; 7 | import rpc from "@coinos/rpc"; 8 | import { v4 } from "uuid"; 9 | 10 | import { PaymentType } from "$lib/types"; 11 | 12 | const bc = rpc(config.bitcoin); 13 | const lq = rpc(config.liquid); 14 | 15 | export const generate = async ({ invoice, user }) => { 16 | let { 17 | address_type, 18 | bolt11, 19 | bolt12, 20 | aid, 21 | currency, 22 | expiry, 23 | fiat, 24 | id, 25 | tip, 26 | amount, 27 | items = [], 28 | own, 29 | rate, 30 | request_id, 31 | memo, 32 | memoPrompt, 33 | prompt, 34 | type = PaymentType.lightning, 35 | webhook, 36 | secret, 37 | } = invoice; 38 | 39 | amount = Number.parseInt(amount || 0); 40 | tip = tip == null ? null : Number.parseInt(tip); 41 | 42 | if (user) user = await getUser(user.username); 43 | if (!user) fail("user not provided"); 44 | if (typeof prompt === "undefined") prompt = user.prompt; 45 | 46 | let account = await g(`account:${aid}`); 47 | if (!account) account = await g(`account:${user.id}`); 48 | if (!account) fail("account not found"); 49 | aid = account.id; 50 | 51 | const rates = await g("rates"); 52 | if (!currency) currency = user.currency; 53 | if (!rate) rate = rates[currency]; 54 | if (fiat) amount = Math.round((SATS * fiat) / rate); 55 | if (amount < 0) fail("invalid amount"); 56 | if (tip < 0) fail("invalid tip"); 57 | if (rate < 0) fail("invalid rate"); 58 | if (memo && memo.length > 5000) fail("memo too long"); 59 | 60 | if (!id) id = v4(); 61 | 62 | let hash; 63 | let text; 64 | let path; 65 | let paymentHash; 66 | 67 | if (account.seed) { 68 | type = "bitcoin"; 69 | const node = rpc({ ...config[type], wallet: aid }); 70 | hash = await node.getNewAddress({ address_type }); 71 | text = bip21(hash, invoice); 72 | 73 | ({ hdkeypath: path } = await node.getAddressInfo(hash)); 74 | } else if (type === PaymentType.lightning) { 75 | let r; 76 | if (bolt11) { 77 | const { id: nodeid } = await ln.getinfo(); 78 | r = await ln.decode(bolt11); 79 | if (r.payee !== nodeid) fail("invalid invoice"); 80 | amount = Math.round(r.amount_msat / 1000); 81 | r.bolt11 = bolt11; 82 | } else { 83 | expiry ||= 60 * 60 * 24 * 30; 84 | 85 | r = await ln.invoice({ 86 | amount_msat: amount ? `${amount + tip}sat` : "any", 87 | label: `${id} ${user.username} ${Date.now()}`, 88 | description: memo || "", 89 | expiry, 90 | deschashonly: true, 91 | cltv: 19, 92 | }); 93 | } 94 | 95 | hash = r.bolt11; 96 | text = r.bolt11; 97 | paymentHash = r.payment_hash; 98 | } else if (type === PaymentType.bolt12) { 99 | let r; 100 | if (bolt12) { 101 | const { id: nodeid } = await ln.getinfo(); 102 | r = await ln.decode(bolt12); 103 | if (r.invoice_node_id !== nodeid) fail("invalid invoice"); 104 | amount = Math.round(r.invoice_amount_msat / 1000); 105 | r.bolt12 = bolt12; 106 | } else { 107 | r = await ln.offer({ 108 | amount: amount ? `${amount + tip}sat` : "any", 109 | label: `${id} ${user.username} ${new Date()}`, 110 | description: memo || id, 111 | }); 112 | 113 | if (await getInvoice(r.offer_id)) fail("Duplicate offer exists"); 114 | await s(`invoice:${r.offer_id}`, id); 115 | } 116 | 117 | hash = r.bolt12; 118 | text = r.bolt12; 119 | } else if (type === PaymentType.bitcoin) { 120 | address_type ||= "bech32"; 121 | hash = await bc.getNewAddress({ address_type }); 122 | text = bip21(hash, invoice); 123 | } else if (type === PaymentType.liquid) { 124 | address_type ||= "blech32"; 125 | hash = await lq.getNewAddress({ address_type }); 126 | text = bip21(hash, invoice); 127 | } else if (type === PaymentType.internal) { 128 | hash = id; 129 | } else if (type === PaymentType.ecash) { 130 | hash = id; 131 | text = request(id, amount, memo); 132 | } else { 133 | fail(`unrecognized type ${type}`); 134 | } 135 | 136 | invoice = { 137 | amount, 138 | aid, 139 | address_type, 140 | created: Date.now(), 141 | currency, 142 | hash, 143 | expiry, 144 | id, 145 | items, 146 | memo, 147 | rate, 148 | paymentHash, 149 | pending: 0, 150 | received: 0, 151 | request_id, 152 | memoPrompt, 153 | own, 154 | path, 155 | prompt, 156 | secret, 157 | text, 158 | tip, 159 | type, 160 | uid: user.id, 161 | webhook, 162 | }; 163 | 164 | if (type === "liquid") { 165 | const { unconfidential } = await lq.getAddressInfo(hash); 166 | await s(`invoice:${unconfidential}`, id); 167 | } 168 | 169 | await s(`invoice:${hash}`, id); 170 | await s(`invoice:${id}`, invoice); 171 | await db.lPush(`${aid}:invoices`, id); 172 | 173 | if (request_id) { 174 | const request = await g(`request:${request_id}`); 175 | if (request) { 176 | const { invoice_id: prev } = request; 177 | request.invoice_id = id; 178 | await s(`request:${request_id}`, request); 179 | 180 | if (!prev) emit(request.requester_id, "invoice", invoice); 181 | } 182 | } 183 | 184 | return invoice; 185 | }; 186 | -------------------------------------------------------------------------------- /index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/sockets.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"sockets.js","sourceRoot":"","sources":["sockets.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/migrate.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"migrate.js","sourceRoot":"","sources":["migrate.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import config from "$config"; 2 | import { g } from "$lib/db"; 3 | import locales from "$lib/locales/index"; 4 | 5 | const { URL } = process.env; 6 | 7 | export const fail = (msg) => { 8 | throw new Error(msg); 9 | }; 10 | 11 | export const nada = () => {}; 12 | 13 | export const sleep = (n) => new Promise((r) => setTimeout(r, n)); 14 | export const wait = async (f, s = 300, n = 50) => { 15 | let i = 0; 16 | while (!(await f()) && i < s) (await sleep(n)) && i++; 17 | if (i >= s) fail("timeout"); 18 | return f(); 19 | }; 20 | 21 | export const prod = process.env.NODE_ENV === "production"; 22 | 23 | export const bail = (res, msg) => res.code(500).send(msg); 24 | 25 | export const SATS = 100000000; 26 | export const sats = (n) => Math.round(n * SATS); 27 | export const btc = (n) => parseFloat((n / SATS).toFixed(8)); 28 | export const fiat = (n, r) => (n * r) / SATS; 29 | 30 | export const uniq = (a, k) => [...new Map(a.map((x) => [k(x), x])).values()]; 31 | export const pick = (O, K) => 32 | K.reduce((o, k) => (typeof O[k] !== "undefined" && (o[k] = O[k]), o), {}); 33 | 34 | export const bip21 = (address, { amount, memo, tip, type }) => { 35 | if (!(amount || memo)) return address; 36 | 37 | const network = { liquid: "liquidnetwork", bitcoin: "bitcoin" }[type]; 38 | const url = new URLSearchParams(); 39 | 40 | if (amount) { 41 | url.append("amount", btc(amount + tip).toFixed(8)); 42 | if (type === "liquid") url.append("assetid", config.liquid.btc); 43 | } 44 | 45 | if (memo) url.append("message", memo); 46 | 47 | return `${network}:${address}?${url.toString()}`; 48 | }; 49 | 50 | export const getUser = async (username, fields = undefined) => { 51 | if (username === "undefined") fail("invalid user"); 52 | const k = username?.replace(/\s/g, "").toLowerCase(); 53 | let user = await g(`user:${k}`); 54 | if (typeof user === "string") user = await g(`user:${user}`); 55 | 56 | return fields && user ? pick(user, fields) : user; 57 | }; 58 | 59 | export const getInvoice = async (hash) => { 60 | let iid = await g(`invoice:${hash}`); 61 | if (iid?.id) iid = iid.id; 62 | else if (iid?.hash) iid = iid.hash; 63 | return await g(`invoice:${iid}`); 64 | }; 65 | 66 | export const getPayment = async (id) => { 67 | let p = await g(`payment:${id}`); 68 | if (typeof p === "string") p = await g(`payment:${p}`); 69 | return p; 70 | }; 71 | 72 | export const f = (s, currency) => 73 | new Intl.NumberFormat("en-US", { 74 | style: "currency", 75 | currency, 76 | }) 77 | .format(s) 78 | .replace("CA", ""); 79 | 80 | export function formatReceipt(items, currency) { 81 | function wrapText(text, maxWidth) { 82 | const words = text.split(" "); 83 | const lines = []; 84 | let currentLine = ""; 85 | for (const word of words) { 86 | if (currentLine.length + word.length + 1 > maxWidth) { 87 | lines.push(currentLine); 88 | currentLine = word; 89 | } else { 90 | currentLine += (currentLine.length > 0 ? " " : "") + word; 91 | } 92 | } 93 | if (currentLine) lines.push(currentLine); 94 | return lines; 95 | } 96 | 97 | function calculateColumnWidths(items) { 98 | let maxQuantityLength = 0; 99 | let maxPriceLength = 0; 100 | 101 | for (const item of items) { 102 | const quantityLength = String(item.quantity).length; 103 | if (quantityLength > maxQuantityLength) { 104 | maxQuantityLength = quantityLength; 105 | } 106 | 107 | const priceLength = f(item.price * item.quantity, currency).length; 108 | if (priceLength > maxPriceLength) { 109 | maxPriceLength = priceLength; 110 | } 111 | } 112 | 113 | // Add padding to the widths for aesthetic spacing 114 | return { 115 | quantityColumnWidth: maxQuantityLength + 1, // Space after quantity 116 | priceColumnWidth: maxPriceLength + 1, // Space before price 117 | }; 118 | } 119 | const maxLineWidth = 32; 120 | const { quantityColumnWidth, priceColumnWidth } = 121 | calculateColumnWidths(items); 122 | const nameColumnWidth = maxLineWidth - quantityColumnWidth - priceColumnWidth; 123 | 124 | return items 125 | .map((item) => { 126 | const quantityStr = String(item.quantity).padEnd(quantityColumnWidth); 127 | const priceStr = f(item.price * item.quantity, currency).padStart( 128 | priceColumnWidth, 129 | ); 130 | const nameLines = wrapText(item.name, nameColumnWidth); 131 | 132 | // Construct the full line(s) with the first line including the price 133 | const fullLines = [ 134 | `${quantityStr}${nameLines[0].padEnd(nameColumnWidth)}${priceStr}`, 135 | ]; 136 | // Add any additional name lines, properly indented 137 | for (let i = 1; i < nameLines.length; i++) { 138 | fullLines.push(" ".repeat(quantityColumnWidth) + nameLines[i]); 139 | } 140 | 141 | return fullLines.join("\n"); 142 | }) 143 | .join("\n"); 144 | } 145 | 146 | export const t = ({ language = "en" }) => locales[language]; 147 | 148 | export const time = (() => { 149 | let count = 0; 150 | let started = false; 151 | return (s = "") => { 152 | if (!started) { 153 | console.time(""); 154 | started = true; 155 | } 156 | console.timeLog("", ++count, s); 157 | }; 158 | })(); 159 | 160 | export const fmt = (sats) => 161 | `⚡️${new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format( 162 | sats, 163 | )}`; 164 | 165 | export const link = (id) => `${URL}/payment/${id}`; 166 | 167 | export const fields = [ 168 | "about", 169 | "anon", 170 | "banner", 171 | "banner", 172 | "currency", 173 | "display", 174 | "id", 175 | "hidepay", 176 | "lud16", 177 | "memoPrompt", 178 | "npub", 179 | "picture", 180 | "prompt", 181 | "pubkey", 182 | "username", 183 | "website", 184 | ]; 185 | -------------------------------------------------------------------------------- /lib/utils.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"utils.js","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":"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"} --------------------------------------------------------------------------------