├── 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 |
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 |
36 |
37 | {{#if withdrawal.amount}}
38 |
39 | {{withdrawalTriggered}} {{withdrawal.amount}}
40 |
41 |
42 |
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4CAAsB;AACtB,oDAA6B;AAE7B,wCAAyC;AACzC,8BAA+B;AAC/B,oCAAmC;AACnC,0CAA2B;AAE3B,IAAI,IAAI,CAAC;AACT,IAAI,EAAE,CAAC;AACP,IAAI,OAAO,GAAG;;;;;gBACZ,IAAI,EAAE,IAAI,EAAE,CAAC,UAAU,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI;oBAAE,sBAAO;qBAC9D,EAAE,EAAF,wBAAE;gBAAE,KAAA,EAAE,CAAC,SAAS,EAAE,CAAA;yBAAd,wBAAc;gBAAK,qBAAM,IAAA,aAAK,EAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,EAAA;;gBAA9C,KAAA,CAAC,SAA6C,CAAC,CAAA;;;gBAAjE,GAAkE;;;gBAE1E,EAAE,GAAG,IAAI,YAAS,CAAC,iDAAiD,CAAC,CAAC;gBAEtE,EAAE,CAAC,SAAS,GAAG,UAAgB,KAAK;;;;;;;;oCAE5B,QAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oCACrB,qBAAM,IAAA,MAAC,EAAC,OAAO,CAAC,EAAA;;oCAAxB,UAAQ,SAAgB;oCACnB,qBAAM,IAAA,MAAC,EAAC,IAAI,CAAC,EAAA;;oCAAlB,OAAK,SAAa;oCACtB,IAAI,IAAE;wCAAE,CAAC,KAAS,IAAE,EAAT,IAAE,QAAA,CAAQ,CAAC;oCAEtB,MAAM,CAAC,IAAI,CAAC,IAAE,CAAC,CAAC,GAAG,CAAC,UAAC,MAAM;wCACzB,OAAK,CAAC,MAAM,CAAC,GAAG,KAAG,CAAC,CAAC,GAAG,IAAE,CAAC,MAAM,CAAC,CAAC;oCACrC,CAAC,CAAC,CAAC;oCAEH,IAAA,MAAC,EAAC,MAAM,EAAE,KAAG,CAAC,CAAC,CAAC,CAAC;oCACjB,IAAA,MAAC,EAAC,OAAO,EAAE,OAAK,CAAC,CAAC;oCAClB,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;;;;oCAElB,OAAO,CAAC,GAAG,CAAC,GAAC,CAAC,CAAC;oCACf,IAAA,aAAG,EAAC,uBAAuB,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;;;;iBAE3C,CAAC;gBAEF,EAAE,CAAC,OAAO,GAAG,UAAgB,KAAK;;;4BAChC,IAAA,aAAG,EAAC,sBAAsB,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;;;;iBAC5C,CAAC;gBAEF,sBAAO,EAAE,EAAC;;;KACX,CAAC;AAEK,IAAI,KAAK,GAAG;;;;;;gBACjB,OAAO,EAAE,CAAC;gBAEN,IAAI,GAAG,CAAC;gBACL,qBAAM,IAAA,MAAC,EAAC,IAAI,CAAC,EAAA;;gBAAlB,EAAE,GAAG,SAAa;gBACpB,IAAI,EAAE;oBAAE,CAAC,KAAe,EAAE,EAAf,IAAI,UAAA,EAAE,EAAE,QAAA,CAAQ,CAAC;qBAExB,CAAA,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA,EAAvC,wBAAuC;gBACzC,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;;;;gBAER,qBAAM,IAAA,aAAG,EACf,qDAA8C,iBAAM,CAAC,KAAK,CAAE,CAC7D,CAAC,IAAI,EAAE,EAAA;;gBAFJ,CAAC,GAAG,SAEA;gBACF,OAAc,CAAC,MAAN,CAAO;gBAClB,QAAM,IAAE,CAAC,KAAK,CAAC,CAAC;gBAEpB,MAAM,CAAC,IAAI,CAAC,IAAE,CAAC,CAAC,GAAG,CAAC,UAAC,CAAC;oBACpB,IAAE,CAAC,CAAC,CAAC,GAAG,IAAE,CAAC,CAAC,CAAC,GAAG,KAAG,CAAC;gBACtB,CAAC,CAAC,CAAC;gBAEH,qBAAM,IAAA,MAAC,EAAC,IAAI,EAAE,EAAE,IAAI,MAAA,EAAE,EAAE,MAAA,EAAE,CAAC,EAAA;;gBAA3B,SAA2B,CAAC;;;;gBAE5B,IAAA,aAAG,EAAC,sBAAsB,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;gBAI3C,UAAU,CAAC,aAAK,EAAE,KAAK,CAAC,CAAC;;;;KAC1B,CAAC;AA3BS,QAAA,KAAK,SA2Bd"}
--------------------------------------------------------------------------------
/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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8BAAmC;AACnC,oCAAgD;AAChD,oCAAkC;AAClC,0CAAqD;AACrD,6BAA0B;AAC1B,wCAA4C;AAC5C,wCAAoC;AAG9B,IAAO,IAAI,GAAK,gBAAK,MAAV,CAAW;AAE5B,kBAAe;IACP,IAAI;4DAAC,EAAmB,EAAE,GAAG;;gBAAd,KAAK,gBAAA;;;;;wBAElB,EAAE,GAAG,IAAA,SAAE,GAAE,CAAC;wBACd,qBAAM,IAAA,MAAC,EAAC,eAAQ,EAAE,CAAE,EAAE,KAAK,CAAC,EAAA;;wBAA5B,SAA4B,CAAC;wBAC7B,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAA,EAAE,CAAC,CAAC;;;;wBAEjB,IAAA,aAAG,EAAC,GAAC,CAAC,OAAO,CAAC,CAAC;wBACf,IAAA,YAAI,EAAC,GAAG,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;;;;KAExB;IAEK,GAAG;4DAAC,EAAkB,EAAE,GAAG;;gBAAX,EAAE,eAAA;;;;;wBAER,qBAAM,IAAA,MAAC,EAAC,eAAQ,EAAE,CAAE,CAAC,EAAA;;wBAA7B,KAAK,GAAG,SAAqB;wBACpB,qBAAM,IAAA,aAAK,EAAC,KAAK,CAAC,EAAA;;wBAA3B,WAAS,SAAkB;wBAC/B,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,OAAA,EAAE,MAAM,UAAA,EAAE,CAAC,CAAC;;;;wBAE5B,IAAA,aAAG,EAAC,GAAC,CAAC,OAAO,CAAC,CAAC;wBACf,IAAA,YAAI,EAAC,GAAG,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;;;;KAExB;IAEK,KAAK;4DAAC,EAAyB,EAAE,GAAG;;gBAApB,KAAK,gBAAA,EAAI,IAAI,UAAA;;;;;wBAElB,qBAAM,IAAA,aAAK,EAAC,KAAK,CAAC,EAAA;;wBAA3B,MAAM,GAAG,SAAkB;wBAE3B,IAAI,SAAA,CAAC;wBACL,IAAI,GAAG,IAAA,SAAE,GAAE,CAAC;wBACV,QAAQ,GAAc,IAAI,SAAlB,EAAM,GAAG,GAAK,IAAI,GAAT,CAAU;wBACrB,qBAAM,IAAA,MAAC,EAAC,OAAO,CAAC,EAAA;;wBAAxB,KAAK,GAAG,SAAgB;wBAC5B,qBAAM,IAAA,MAAC,EAAC,kBAAW,IAAI,CAAE,EAAE;gCACzB,QAAQ,UAAA;gCACR,EAAE,EAAE,IAAI;gCACR,IAAI,MAAA;gCACJ,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC;gCACrB,GAAG,KAAA;gCACH,QAAQ,EAAE,CAAC;6BACZ,CAAC,EAAA;;wBAPF,SAOE,CAAC;wBAEH,qBAAM,IAAA,iBAAM,EAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,EAAA;;wBAA/C,SAA+C,CAAC;wBAEhD,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;;;;wBAEvB,IAAA,aAAG,EAAC,GAAC,CAAC,OAAO,CAAC,CAAC;wBACf,IAAA,YAAI,EAAC,GAAG,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;;;;KAExB;IAEK,IAAI;4DAAC,EAA0B,EAAE,GAAG;;gBAArB,MAAM,iBAAA,EAAI,IAAI,UAAA;;;;;wBAE3B,EAAE,GAAG,IAAA,SAAE,GAAE,CAAC;wBACV,IAAI,GAAG,IAAA,SAAE,GAAE,CAAC;wBAER,qBAAM,IAAA,gBAAK,EAAC,EAAE,IAAI,MAAA,EAAE,MAAM,QAAA,EAAE,IAAI,MAAA,EAAE,IAAI,MAAA,EAAE,CAAC,EAAA;;wBAA7C,CAAC,GAAG,SAAyC;wBACrC,qBAAM,IAAA,YAAI,EAAC,MAAM,CAAC,EAAA;;wBAA1B,KAAK,GAAG,SAAkB;wBAC9B,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC;wBACf,qBAAM,IAAA,MAAC,EAAC,kBAAW,CAAC,CAAC,EAAE,CAAE,EAAE,CAAC,CAAC,EAAA;;wBAA7B,SAA6B,CAAC;wBAC9B,IAAA,MAAC,EAAC,eAAQ,EAAE,CAAE,EAAE,KAAK,CAAC,CAAC;wBAEvB,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAA,EAAE,CAAC,CAAC;;;;wBAEjB,IAAA,aAAG,EAAC,GAAC,CAAC,OAAO,CAAC,CAAC;wBACf,IAAA,YAAI,EAAC,GAAG,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;;;;KAExB;IAEK,IAAI;4DAAC,EAAkD,EAAE,GAAG;;gBAArD,YAAwC,EAAhC,MAAM,YAAA,EAAU,IAAI,YAAA,EAAE,QAAQ,cAAA,EAAI,IAAI,UAAA;;;;;wBAEvD,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;wBAC/B,GAAG,GAAG,QAAQ,CAAC;wBACb,SAAoB,gBAAK,UAAV,CAAW;wBAChC,IAAI,IAAI,CAAC,QAAQ,KAAK,MAAM;4BAAE,IAAI,CAAC,cAAc,CAAC,CAAC;wBACzC,GAAG,GAAyB,IAAI,GAA7B,EAAE,QAAQ,GAAe,IAAI,SAAnB,EAAE,QAAQ,GAAK,IAAI,SAAT,CAAU;wBAC9B,qBAAM,OAAE,CAAC,KAAK,CACzB,kBAAW,GAAG,CAAE,EAChB,iBAAU,MAAI,cAAI,GAAG,CAAE,EACvB,MAAM,IAAI,CAAC,EACX,CAAC,EACD,CAAC,EACD,CAAC,CACF,EAAA;;wBAPG,MAAM,GAAG,SAOZ;wBAEW,qBAAM,IAAA,MAAC,EAAC,OAAO,CAAC,EAAA;;wBAAxB,KAAK,GAAG,SAAgB;wBACxB,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;wBAE3B,IAAI,MAAM,CAAC,GAAG;4BAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;wBAE7B,EAAE,GAAG,IAAA,SAAE,GAAE,CAAC;wBACV,CAAC,GAAG;4BACN,EAAE,IAAA;4BACF,MAAM,EAAE,CAAC,MAAM;4BACf,IAAI,MAAA;4BACJ,MAAM,QAAA;4BACN,GAAG,KAAA;4BACH,SAAS,EAAE,IAAI;4BACf,IAAI,MAAA;4BACJ,QAAQ,UAAA;4BACR,IAAI,QAAA;4BACJ,GAAG,KAAA;4BACH,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;yBACpB,CAAC;wBAEF,qBAAM,IAAA,MAAC,EAAC,kBAAW,IAAI,CAAE,EAAE,EAAE,CAAC,EAAA;;wBAA9B,SAA8B,CAAC;wBAC/B,qBAAM,IAAA,MAAC,EAAC,kBAAW,EAAE,CAAE,EAAE,CAAC,CAAC,EAAA;;wBAA3B,SAA2B,CAAC;wBAC5B,qBAAM,OAAE,CAAC,KAAK,CAAC,UAAG,GAAG,cAAW,EAAE,EAAE,CAAC,EAAA;;wBAArC,SAAqC,CAAC;wBAEtC,IAAA,WAAC,EAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAI,EAAE,MAAM,CAAC,CAAC;wBACvC,IAAA,cAAI,EAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;wBAE5B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;;;wBAEZ,IAAA,YAAI,EAAC,GAAG,EAAE,GAAC,CAAC,OAAO,CAAC,CAAC;;;;;;KAExB;CACF,CAAC"}
--------------------------------------------------------------------------------
/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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"}
--------------------------------------------------------------------------------