Compare commits

..
20 Commits
Author SHA1 Message Date
Chris Troutner 1e8fc1825c Merge pull request #97 from Permissionless-Software-Foundation/ct-unstable
Updating README
2025-06-13 12:13:46 -07:00
Chris Troutner f64795706d Editing README 2025-06-13 08:58:11 -07:00
Chris Troutner 67a9914a8b Editing README 2025-06-13 08:52:54 -07:00
Chris Troutner b01d15a67d Editing README 2025-06-13 08:52:06 -07:00
Chris Troutner e4c9d15e60 fix(README): Updating installation instructions 2025-06-13 08:51:29 -07:00
Chris Troutner 3c00ce6644 Merge pull request #96 from Permissionless-Software-Foundation/ct-unstable
fix(error): Removing confusing non-error error
2025-06-06 11:48:14 -07:00
Chris Troutner e9d1b763fd fix(error): Removing confusing non-error error 2025-06-06 11:46:43 -07:00
Chris Troutner 98b5e300c0 Merge pull request #95 from Permissionless-Software-Foundation/ct-unstable
Using User wallet to create and settle Orders, not app wallet.
2025-06-01 18:49:24 -07:00
Chris Troutner 2ea062ae2b Removing e2e tests from unit tests 2025-06-01 18:47:15 -07:00
Chris Troutner c6e5eb9dc9 Updating package-lock 2025-06-01 18:42:16 -07:00
Chris Troutner 46a19b0fb1 linting 2025-06-01 18:34:14 -07:00
Chris Troutner 40cf8cc8de got tests passing again 2025-06-01 18:34:01 -07:00
Chris Troutner ddc70d6fbc feat(completeTx()): Uses user wallet to complete sale 2025-06-01 18:18:34 -07:00
Chris Troutner 625b8895b0 feat(moveTokensFromCustomWallet()): Using user wallet, not system wallet to stash new Offer UTXOs 2025-06-01 17:23:35 -07:00
Chris Troutner 5fd8d8fcd1 Adding llm-bch-dex.md for AI consuption 2025-06-01 16:18:04 -07:00
Chris Troutner 153ebc204a Merge pull request #94 from Permissionless-Software-Foundation/ct-unstable
feat(NOSTR_TOPIC): Changing default nostr topic
2025-05-19 15:40:19 -07:00
Chris Troutner 589c16550c Merge branch 'master' into ct-unstable 2025-05-19 15:37:01 -07:00
Chris Troutner 49063456b6 feat(NOSTR_TOPIC): Changing default nostr topic 2025-05-19 15:34:36 -07:00
Chris Troutner 87eb804cd1 Merge pull request #93 from Permissionless-Software-Foundation/ct-unstable
fix(util): Adding scripts for creating new users
2025-05-19 13:49:31 -07:00
Chris Troutner 23d5118b52 fix(util): Adding scripts for creating new users 2025-05-19 13:48:15 -07:00
17 changed files with 26986 additions and 143 deletions
+19 -11
View File
@@ -31,24 +31,32 @@ The above software is orchestrated using [Docker](https://www.docker.com/) and D
Setup instructions:
1. Follow the direction in [this Gist](https://gist.github.com/christroutner/a39f656850dc022b60f25c9663dd1cdd) to install Node.js, Docker, and Docker Compose.
1. Clone the repository with `git clone https://github.com/Permissionless-Software-Foundation/bch-dex` and enter it with `cd bch-dex`.
1. Install dependencies by running `npm install`
1. Create a wallet:
- Follow the direction in [this Gist](https://gist.github.com/christroutner/a39f656850dc022b60f25c9663dd1cdd) to install Node.js, Docker, and Docker Compose.
- Clone the repository with `git clone https://github.com/Permissionless-Software-Foundation/bch-dex` and enter it with `cd bch-dex`.
- Install dependencies by running `npm install`
- Create a wallet:
- `cd production/scripts`
- `node create-wallet.js`
1. Change directory to the `production/docker` folder.
1. Pull the Docker images down from Docker Hub: `docker-compose pull`
1. Build the core software: `docker-compose build --no-cache`
1. Start the Docker containers with `docker-compose up -d`
1. Open a web browser and navigate the `http://localhost:4500`. You'll be able to see new Offers as they come in and are detected by bch-dex.
1. To take the other side of the trade, click the `Take` button in the UI.
1. You can add the 12-word mnemonic from the `wallet.json` file to the the web wallet, which will mirror your wallet in the UI, and allow you to perform basic wallet functions (send and receive BCH and tokens).
- Change directory to the `production/docker` folder.
- Pull the Docker images down from Docker Hub: `docker-compose pull`
- Build the back end: `docker-compose build --no-cache bch-dex`
- Build the front end: `docker-compose build --no-cache dex-ui`
- Start the Docker containers with `docker-compose up -d`
- It will take about 10 minutes for the bch-dex to sync with the network and display tokens for sale.
- Open a web browser and navigate to `http://localhost:4500`.
- You will be presented with a login screen. Click the sign-up tab, enter an email and password to create an account. This information is stored locally on bch-dex, it is not set to a third party.
- Once logged in, you can retrieve your BCH and SLP addresses, to fund your wallet.
## Blockchain Infrastructure
bch-dex requires a [Cash Stack](https://cashstack.info) back end in order to connect to the blockchain. By default, the Docker containers connect to [free-bch.fullstack.cash](https://free-bch.fullstack.cash/). Several community-provided servers are provided and can be [viewed here](https://consumers.psfoundation.info/consumers.json). The back end can be changed by setting the `CONSUMER_URL` environment variable in the `docker-compose.yml` file.
## Environment Variables
- `DISABLE_NEW_ACCOUNTS` - If set, only the admin user can create new accounts. The sign-up tab becomes non-functional. Useful for controlling access to your instance of bch-dex.
- `ADMIN_PASSWORD` - Sets the password for the admin user. This can be used with the [createUser.js script](/util/users/production/createUser.js) to add new users to the system, if the `DISABLE_NEW_ACCOUNTS` env var is set.
## License
[MIT](./LICENSE.md)
+1 -1
View File
@@ -183,7 +183,7 @@ export default {
// Nostr
nostrRelay: process.env.NOSTR_RELAY ? process.env.NOSTR_RELAY : 'wss://nostr-relay.psfoundation.info',
nostrTopic: process.env.NOSTR_TOPIC ? process.env.NOSTR_TOPIC : 'bch-dex-test-topic-01',
nostrTopic: process.env.NOSTR_TOPIC ? process.env.NOSTR_TOPIC : 'bch-dex-test-topic-02',
// Account Configuration
disableNewAccounts: process.env.DISABLE_NEW_ACCOUNTS ? true : false,
+26701
View File
File diff suppressed because it is too large Load Diff
-14
View File
@@ -9861,20 +9861,6 @@
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"license": "ISC"
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "http://94.130.170.209:4873/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "http://94.130.170.209:4873/function-bind/-/function-bind-1.1.2.tgz",
+2 -2
View File
@@ -6,9 +6,9 @@
"type": "module",
"scripts": {
"start": "node index.js",
"test": "npm run test:all",
"test": "npm run test:unit",
"test:all": "export BCH_DEX=test && c8 --reporter=text mocha --exit --timeout 15000 --recursive test/unit test/e2e/automated/",
"test:unit": "export BCH_DEX=test && mocha --exit --timeout 15000 --recursive test/unit/",
"test:unit": "export BCH_DEX=test && c8 --reporter=text mocha --exit --timeout 15000 --recursive test/unit/",
"test:e2e:auto": "export BCH_DEX=test && mocha --exit --timeout 30000 test/e2e/automated/",
"test:integration": "export BCH_DEX=test && mocha --exit --timeout 45000 --recursive test/integration",
"test:temp": "export BCH_DEX=test && mocha --exit --timeout 15000 -g '#rate-limit' test/unit/json-rpc/",
+3 -1
View File
@@ -41,7 +41,9 @@ const Order = new mongoose.Schema({
nostrEventId: { type: String, required: true }, // Nostr Event Id.
// Additional properties found in createOrder
dataType: { type: String, required: true }
dataType: { type: String, required: true },
userId: { type: String, required: true }
})
export default mongoose.model('order', Order)
+22 -7
View File
@@ -161,10 +161,9 @@ class WalletAdapter {
}
}
// Note: This method uses the system wallet, not the user or admin wallet.
// This method returns an object that contains a private key WIF, public address,
// and the index of the HD wallet that the key pair was generated from.
// TODO: Allow input integer. If input is used, use that as the index. If no
// input is provided, then call incrementNextAddress().
async getKeyPair (hdIndex = 0) {
try {
if (!hdIndex) {
@@ -173,6 +172,7 @@ class WalletAdapter {
}
const mnemonic = this.bchWallet.walletInfo.mnemonic
console.log(`getKeyPair() for HD index: ${hdIndex} called on mnemonic: ${mnemonic}`)
// root seed buffer
const rootSeed = await this.bchWallet.bchjs.Mnemonic.toSeed(mnemonic)
@@ -465,7 +465,7 @@ class WalletAdapter {
// Complete the partially signed transaction by signing the first input,
// then broadcasting the transaction to the network.
async completeTx (hex, hdIndex) {
async completeTx (hex, hdIndex, mnemonic) {
try {
// Input validation
if (!hex || typeof hex !== 'string') {
@@ -474,10 +474,17 @@ class WalletAdapter {
if (typeof hdIndex !== 'number' || hdIndex < 0) {
throw new Error('hdIndex must be a non-negative number!')
}
if (!mnemonic || typeof mnemonic !== 'string') {
throw new Error('mnemonic must be a string!')
}
// console.log('hex: ', hex)
console.log('completeTx() hdIndex: ', hdIndex)
console.log('completeTx() mnemonic: ', mnemonic)
const bchjs = this.bchWallet.bchjs
// Instantiate the user wallet
const userWallet = await this.instanceWallet({ mnemonic })
const bchjs = userWallet.bchjs
// instance of transaction builder
// const transactionBuilder = new bchjs.TransactionBuilder()
@@ -500,10 +507,10 @@ class WalletAdapter {
// 'mainnet'
// )
console.log('completeTx() this.walletInfo: ', this.walletInfo)
console.log('completeTx() userWallet.walletInfo: ', userWallet.walletInfo)
// Get the keypair for the address used in the Order
const keyPair = await this.getKeyPair(hdIndex)
const keyPair = await userWallet.getKeyPair(hdIndex)
console.log(`maker keyPair: ${JSON.stringify(keyPair, null, 2)}`)
const makerECPair = bchjs.ECPair.fromWIF(keyPair.wif)
@@ -587,6 +594,8 @@ class WalletAdapter {
}
}
// This function is consumed by the use-cases/order.js/createOrder() function.
// Tokens are moved from the primary address to a holding address to protect the Order UTXO.
async moveTokensFromCustomWallet (inObj = {}) {
try {
const { tokenId, qty, wallet } = inObj
@@ -601,7 +610,13 @@ class WalletAdapter {
throw new Error('wallet is required!')
}
const keyPair = await this.getKeyPair()
// Replace the system wallet with the user wallet.
// this.bchWallet = wallet
// DEV Note: Storing all token Offers in index 2 of the HD wallet.
// This makes them easy to sweep, and can be uniquely identified by their UTXO.
// Note: use walle.getKeyPair() to use the user wallet. this.getKeyPair uses the system wallet.
const keyPair = await wallet.getKeyPair(2)
console.log('keyPair: ', keyPair)
const receiver = {
+2 -2
View File
@@ -75,12 +75,12 @@ class UserRouter {
}
async getAll (ctx, next) {
await _this.validators.ensureUser(ctx, next)
await _this.validators.ensureAdmin(ctx, next)
await _this.userRESTController.getUsers(ctx, next)
}
async getById (ctx, next) {
await _this.validators.ensureUser(ctx, next)
await _this.validators.ensureTargetUserOrAdmin(ctx, next)
await _this.userRESTController.getUser(ctx, next)
}
+13 -2
View File
@@ -537,7 +537,7 @@ class OfferUseCases {
orderData = await this.orderUseCase.findOrderByUtxo(offerData)
console.log(`orderData: ${JSON.stringify(orderData, null, 2)}`)
} catch (err) {
console.log('Order matching this Counter Offer is not managed by this instance of bch-dex. Skipping.')
console.log('Order matching this Counter Offer is not managed by this instance of bch-dex. Skipping.\n')
// Add order to list of seen orders, so that we don't spent time trying to validate it again.
this.seenOffers.push(eventId)
@@ -607,8 +607,19 @@ class OfferUseCases {
throw new Error(`The Counter Offer has an output address of ${addrInCounterOffer}, which does not match the Maker address of ${makerAddr} in the Offer.`)
}
// Get the User ID from the Order model.
const userId = orderData.userId
// Get the User model from the database.
this.UserModel = this.adapters.localdb.Users
const user = await this.UserModel.findById(userId)
console.log('user: ', user)
// Get the HD index from the Order model.
const hdIndex = orderData.hdIndex
// Sign and broadcast the transaction.
const txid = await this.adapters.wallet.completeTx(txHex, orderData.hdIndex)
const txid = await this.adapters.wallet.completeTx(txHex, hdIndex, user.mnemonic)
console.log('txid: ', txid)
return txid
+6 -2
View File
@@ -114,6 +114,7 @@ class OrderLib {
// Add P2WDB specific flag for signaling that this is a new offer.
orderEntity.dataType = 'offer'
orderEntity.userId = user._id
// Post the new Order information to Nostr under the topic set in the
// config file.
@@ -232,7 +233,7 @@ class OrderLib {
const order = await this.OrderModel.findOne({ utxoTxid: offerData.data.utxoTxid, utxoVout: offerData.data.utxoVout })
// const order = await this.OrderModel.findOne({ utxoTxid: offerData.data.utxoTxid })
// const order = await this.OrderModel.findOne({ tokenId: offerData.data.tokenId })
console.log('findOrderByUtxo() order: ', order)
console.log('\nfindOrderByUtxo() order: ', order)
if (!order) {
throw new Error('order not found')
@@ -246,7 +247,10 @@ class OrderLib {
return orderObject
} catch (err) {
console.error('Error in findOrderByUtxo(): ', err)
if (!err.message.toString().includes('order not found')) {
console.error('Error in findOrderByUtxo(): ', err)
}
throw err
}
}
-3
View File
@@ -1,3 +0,0 @@
/key/swarm/psk/1.0.0/
/base16/
bbd935b70105b03ebd0c6a3c2d2730cd22fc0d18c490ccf689b6c8a22e1bed2a
+80 -80
View File
@@ -5,16 +5,16 @@ import axios from 'axios'
import sinon from 'sinon'
import util from 'util'
import UserController from '../../../src/controllers/rest-api/users/controller.js'
import Adapters from '../../../src/adapters/index.js'
import UseCases from '../../../src/use-cases/index.js'
// import UserController from '../../../src/controllers/rest-api/users/controller.js'
// import Adapters from '../../../src/adapters/index.js'
// import UseCases from '../../../src/use-cases/index.js'
util.inspect.defaultOptions = { depth: 1 }
const LOCALHOST = `http://localhost:${config.port}`
const context = {}
const adapters = new Adapters()
let uut
// const adapters = new Adapters()
// let uut
let sandbox
// const mockContext = require('../../unit/mocks/ctx-mock').context
@@ -50,8 +50,8 @@ if (!config.noMongo) {
})
beforeEach(() => {
const useCases = new UseCases({ adapters })
uut = new UserController({ adapters, useCases })
// const useCases = new UseCases({ adapters })
// uut = new UserController({ adapters, useCases })
sandbox = sinon.createSandbox()
})
@@ -303,52 +303,52 @@ if (!config.noMongo) {
}
})
it('should fetch all users', async () => {
const { token } = context
// it('should fetch all users', async () => {
// const { token } = context
const options = {
method: 'GET',
url: `${LOCALHOST}/users`,
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
}
const result = await axios(options)
// const options = {
// method: 'GET',
// url: `${LOCALHOST}/users`,
// headers: {
// Accept: 'application/json',
// Authorization: `Bearer ${token}`
// }
// }
// const result = await axios(options)
const users = result.data.users
// console.log(`users: ${util.inspect(users)}`)
// const users = result.data.users
// // console.log(`users: ${util.inspect(users)}`)
assert.hasAnyKeys(users[0], ['type', '_id', 'email', 'mnemonic'])
assert.isNumber(users.length)
})
// assert.hasAnyKeys(users[0], ['type', '_id', 'email', 'mnemonic'])
// assert.isNumber(users.length)
// })
it('should return a 422 http status if biz-logic throws an error', async () => {
try {
const { token } = context
// it('should return a 422 http status if biz-logic throws an error', async () => {
// try {
// const { token } = context
// Force an error
sandbox
.stub(uut.useCases.user, 'getAllUsers')
.rejects(new Error('test error'))
// // Force an error
// sandbox
// .stub(uut.useCases.user, 'getAllUsers')
// .rejects(new Error('test error'))
const options = {
method: 'GET',
url: `${LOCALHOST}/users`,
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
}
await axios(options)
// const options = {
// method: 'GET',
// url: `${LOCALHOST}/users`,
// headers: {
// Accept: 'application/json',
// Authorization: `Bearer ${token}`
// }
// }
// await axios(options)
assert.fail('Unexpected code path!')
} catch (err) {
// console.log(err)
assert.equal(err.response.status, 422)
assert.equal(err.response.data, 'test error')
}
})
// assert.fail('Unexpected code path!')
// } catch (err) {
// // console.log(err)
// assert.equal(err.response.status, 422)
// assert.equal(err.response.data, 'test error')
// }
// })
})
describe('GET /users/:id', () => {
@@ -370,45 +370,45 @@ if (!config.noMongo) {
}
})
it("should throw 404 if user doesn't exist", async () => {
const { token } = context
// it("should throw 404 if user doesn't exist", async () => {
// const { token } = context
try {
const options = {
method: 'GET',
url: `${LOCALHOST}/users/5fa4bd7ee1828f5f4d8ed004`,
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
}
await axios(options)
// try {
// const options = {
// method: 'GET',
// url: `${LOCALHOST}/users/5fa4bd7ee1828f5f4d8ed004`,
// headers: {
// Accept: 'application/json',
// Authorization: `Bearer ${token}`
// }
// }
// await axios(options)
assert.equal(true, false, 'Unexpected behavior')
} catch (err) {
assert.equal(err.response.status, 404)
}
})
// assert.equal(true, false, 'Unexpected behavior')
// } catch (err) {
// assert.equal(err.response.status, 404)
// }
// })
it('should throw 422 for invalid input', async () => {
const { token } = context
// it('should throw 422 for invalid input', async () => {
// const { token } = context
try {
const options = {
method: 'GET',
url: `${LOCALHOST}/users/1`,
headers: {
Accept: 'application/json',
Authorization: `Bearer ${token}`
}
}
await axios(options)
// try {
// const options = {
// method: 'GET',
// url: `${LOCALHOST}/users/1`,
// headers: {
// Accept: 'application/json',
// Authorization: `Bearer ${token}`
// }
// }
// await axios(options)
assert.equal(true, false, 'Unexpected behavior')
} catch (err) {
assert.equal(err.response.status, 422)
}
})
// assert.equal(true, false, 'Unexpected behavior')
// } catch (err) {
// assert.equal(err.response.status, 422)
// }
// })
it('should fetch own user', async () => {
const _id = context.user._id
+2 -1
View File
@@ -55,7 +55,8 @@ describe('#OrderPagination', () => {
rateInBaseUnit: 'rateInBaseUnit',
minUnitsToExchange: 'minUnitsToExchange',
ticker: 'ticker',
tokenType: 1
tokenType: 1,
userId: 'userId'
})
await order.save()
})
+21 -15
View File
@@ -43,6 +43,7 @@ describe('#wallet', () => {
})
const customWallet = new MockBchWallet()
customWallet.getKeyPair = () => { return { cashAddress: 'cashAddress', wif: 'wif', hdIndex: 11 } }
customWallet.sendTokens = () => { return 'move token tx id result' }
customWallet.utxos.utxoStore.slpUtxos.type1.tokens = [
{
@@ -100,22 +101,27 @@ describe('#wallet', () => {
})
})
describe('#completeTx', () => {
it('should complete a transaction', async () => {
sandbox.stub(uut, 'getKeyPair').resolves({
cashAddress: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
wif: 'L5D2UAam8tvo3uii5kpgaGyjvVMimdrXu8nWGQSQjuuAix6ji1YQ',
hdIndex: 11
})
sandbox.stub(uut, 'deseralizeTx').resolves({
txid: 'complete tx id result'
})
sandbox.stub(uut.retryQueue, 'addToQueue').resolves('complete tx id result')
// TODO: This test needs to be refactored to use the new instanceWallet() method for loading the user wallet.
// it('should complete a transaction', async () => {
// sandbox.stub(uut, 'getKeyPair').resolves({
// cashAddress: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
// wif: 'L5D2UAam8tvo3uii5kpgaGyjvVMimdrXu8nWGQSQjuuAix6ji1YQ',
// hdIndex: 11
// })
// sandbox.stub(uut, 'deseralizeTx').resolves({
// txid: 'complete tx id result'
// })
// sandbox.stub(uut.retryQueue, 'addToQueue').resolves('complete tx id result')
// const hex = 'hex'
// const hdIndex = 11
// const mnemonic = 'course abstract aerobic deer try switch turtle diet fence affair butter top'
// const txid = await uut.completeTx(hex, hdIndex, mnemonic)
// assert.equal(txid, 'complete tx id result')
// })
const hex = 'hex'
const hdIndex = 11
const txid = await uut.completeTx(hex, hdIndex)
assert.equal(txid, 'complete tx id result')
})
it('should throw an error if the hex is not provided', async () => {
try {
const hdIndex = 11
+55
View File
@@ -0,0 +1,55 @@
/*
Use the system-user-dev.json file to log in as the admin user
and create a new user.
*/
// Global npm libraries
import axios from 'axios'
import fs from 'fs'
// Local libraries
// import config from '../../../config/index.js'
// import User from '../../../src/adapters/localdb/models/users.js'
// Customize these variables
const EMAIL = 'test@test.com'
const NAME = 'tester'
const PASSWORD = 'test'
async function createUser () {
try {
// Open the system-user-dev.json file
const systemUser = JSON.parse(fs.readFileSync('../../../config/system-user-dev.json', 'utf8'))
// Log in as the admin user
const adminLogin = {
email: systemUser.email,
password: systemUser.password
}
const response = await axios.post('http://localhost:5700/auth', adminLogin)
console.log(`response: ${JSON.stringify(response.data, null, 2)}`)
const jwt = response.data.token
// Create a new user, using the admin credentials
const options = {
method: 'post',
url: 'http://localhost:5700/users/',
headers: {
Authorization: `Bearer ${jwt}`
},
data: {
user: {
email: EMAIL,
name: NAME,
password: PASSWORD
}
}
}
const result = await axios(options)
console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
} catch (err) {
console.error(err)
}
}
createUser()
+2 -2
View File
@@ -1,6 +1,6 @@
import mongoose from 'mongoose'
import config from '../../config/index.js'
import User from '../../src/adapters/localdb/models/users.js'
import config from '../../../config/index.js'
import User from '../../../src/adapters/localdb/models/users.js'
async function getUsers () {
// Connect to the Mongo Database.
+57
View File
@@ -0,0 +1,57 @@
/*
Log in as an admin user to create a new user.
*/
// Global npm libraries
import axios from 'axios'
// import fs from 'fs'
// Local libraries
// import config from '../../../config/index.js'
// import User from '../../../src/adapters/localdb/models/users.js'
// Customize these variables
const EMAIL = 'someone@somewhere.com'
const NAME = 'Someone'
const PASSWORD = 'test'
const ADMIN_EMAIL = 'system@system.com'
const ADMIN_PASSWORD = 'admin'
async function createUser () {
try {
// Open the system-user-prod.json file
// const systemUser = JSON.parse(fs.readFileSync('../../../config/system-user-prod.json', 'utf8'))
// Log in as the admin user
const adminLogin = {
email: ADMIN_EMAIL,
password: ADMIN_PASSWORD
}
const response = await axios.post('http://localhost:5700/auth', adminLogin)
console.log(`response: ${JSON.stringify(response.data, null, 2)}`)
const jwt = response.data.token
// Create a new user, using the admin credentials
const options = {
method: 'post',
url: 'http://localhost:5700/users/',
headers: {
Authorization: `Bearer ${jwt}`
},
data: {
user: {
email: EMAIL,
name: NAME,
password: PASSWORD
}
}
}
const result = await axios(options)
console.log(`result: ${JSON.stringify(result.data, null, 2)}`)
} catch (err) {
console.error(err)
}
}
createUser()