@bopen-io/messagebox-server 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +372 -0
- package/knexfile.d.ts +5 -0
- package/knexfile.ts +29 -0
- package/out/knexfile.js +25 -0
- package/out/knexfile.js.map +1 -0
- package/out/src/app.js +160 -0
- package/out/src/app.js.map +1 -0
- package/out/src/config/firebase.js +150 -0
- package/out/src/config/firebase.js.map +1 -0
- package/out/src/index.js +295 -0
- package/out/src/index.js.map +1 -0
- package/out/src/migrations/2022-12-28-001-initial-migration.js +28 -0
- package/out/src/migrations/2022-12-28-001-initial-migration.js.map +1 -0
- package/out/src/migrations/2023-01-17-messages-update.js +11 -0
- package/out/src/migrations/2023-01-17-messages-update.js.map +1 -0
- package/out/src/migrations/2024-03-05-001-messageID-upgrade.js +21 -0
- package/out/src/migrations/2024-03-05-001-messageID-upgrade.js.map +1 -0
- package/out/src/migrations/2025-01-31-001-notification-permissions.js +59 -0
- package/out/src/migrations/2025-01-31-001-notification-permissions.js.map +1 -0
- package/out/src/migrations/2025-01-31-002-device-registrations.js +24 -0
- package/out/src/migrations/2025-01-31-002-device-registrations.js.map +1 -0
- package/out/src/routes/__tests/acknowledgeMessage.test.js +129 -0
- package/out/src/routes/__tests/acknowledgeMessage.test.js.map +1 -0
- package/out/src/routes/__tests/listMessages.test.js +211 -0
- package/out/src/routes/__tests/listMessages.test.js.map +1 -0
- package/out/src/routes/__tests/sendMessage.test.js +307 -0
- package/out/src/routes/__tests/sendMessage.test.js.map +1 -0
- package/out/src/routes/acknowledgeMessage.js +144 -0
- package/out/src/routes/acknowledgeMessage.js.map +1 -0
- package/out/src/routes/index.js +17 -0
- package/out/src/routes/index.js.map +1 -0
- package/out/src/routes/listDevices.js +74 -0
- package/out/src/routes/listDevices.js.map +1 -0
- package/out/src/routes/listMessages.js +186 -0
- package/out/src/routes/listMessages.js.map +1 -0
- package/out/src/routes/permissions/getPermission.js +149 -0
- package/out/src/routes/permissions/getPermission.js.map +1 -0
- package/out/src/routes/permissions/getQuote.js +165 -0
- package/out/src/routes/permissions/getQuote.js.map +1 -0
- package/out/src/routes/permissions/index.js +12 -0
- package/out/src/routes/permissions/index.js.map +1 -0
- package/out/src/routes/permissions/listPermissions.js +190 -0
- package/out/src/routes/permissions/listPermissions.js.map +1 -0
- package/out/src/routes/permissions/setPermission.js +137 -0
- package/out/src/routes/permissions/setPermission.js.map +1 -0
- package/out/src/routes/registerDevice.js +150 -0
- package/out/src/routes/registerDevice.js.map +1 -0
- package/out/src/routes/sendMessage.js +451 -0
- package/out/src/routes/sendMessage.js.map +1 -0
- package/out/src/swagger.js +49 -0
- package/out/src/swagger.js.map +1 -0
- package/out/src/types/messagePermissions.js +2 -0
- package/out/src/types/messagePermissions.js.map +1 -0
- package/out/src/types/notifications.js +2 -0
- package/out/src/types/notifications.js.map +1 -0
- package/out/src/utils/logger.js +24 -0
- package/out/src/utils/logger.js.map +1 -0
- package/out/src/utils/messagePermissions.js +126 -0
- package/out/src/utils/messagePermissions.js.map +1 -0
- package/out/src/utils/sendFCMNotification.js +121 -0
- package/out/src/utils/sendFCMNotification.js.map +1 -0
- package/out/tsconfig.tsbuildinfo +1 -0
- package/package.json +88 -0
- package/src/migrations/2022-12-28-001-initial-migration.ts +31 -0
- package/src/migrations/2023-01-17-messages-update.ts +13 -0
- package/src/migrations/2024-03-05-001-messageID-upgrade.ts +25 -0
- package/src/migrations/2025-01-31-001-notification-permissions.ts +68 -0
- package/src/migrations/2025-01-31-002-device-registrations.ts +28 -0
- package/src/templates/documentation.ejs +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# Message Box Server
|
|
2
|
+
|
|
3
|
+
A secure, peer-to-peer message routing server designed for the Bitcoin SV ecosystem. Supports both HTTP and WebSocket message transport, encrypted payloads, identity-based routing, and overlay advertisement via SHIP.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Overview](#1-overview)
|
|
8
|
+
2. [Concepts](#2-concepts)
|
|
9
|
+
3. [Environment Variables](#3-environment-variables)
|
|
10
|
+
4. [Quick Start](#4-quick-start)
|
|
11
|
+
5. [Examples](#5-examples)
|
|
12
|
+
6. [API Reference](#6-api-reference)
|
|
13
|
+
- [POST /sendMessage](#post-sendmessage)
|
|
14
|
+
- [POST /listMessages](#post-listmessages)
|
|
15
|
+
- [POST /acknowledgeMessage](#post-acknowledgemessage)
|
|
16
|
+
7. [WebSocket Support](#7-websocket-support)
|
|
17
|
+
8. [Authentication](#8-authentication)
|
|
18
|
+
9. [Scripts](#9-scripts)
|
|
19
|
+
10. [Deploying](#10-deploying)
|
|
20
|
+
11. [License](#11-license)
|
|
21
|
+
|
|
22
|
+
## 1. Overview
|
|
23
|
+
|
|
24
|
+
MessageBox is a peer-to-peer messaging API that enables secure, encrypted, and authenticated communication between users on the MetaNet. This is primarily achieved through a message-box architecture combined with optional overlay routing and real-time WebSocket transport.
|
|
25
|
+
|
|
26
|
+
When a user sends a message, they must specify the messageBox type and the intended recipient (an identity key). This design allows for protocol-specific messageBox types to be defined at higher application layers, while maintaining clear separation of messages by type and recipient. Each message is routed into a box associated with a specific recipient and use case.
|
|
27
|
+
|
|
28
|
+
Security is a critical aspect of MessageBox. It relies on [AuthExpress middleware](https://github.com/bitcoin-sv/auth-express-middleware) to ensure that only the recipient can access and acknowledge their own messages. Encrypted payloads are supported automatically following [BRC-2](https://github.com/bitcoin-sv/BRCs/blob/master/wallet/0002.md), using secure symmetric encryption between authenticated peers.
|
|
29
|
+
|
|
30
|
+
MessageBox also supports [@bsv/authsocket](https://www.npmjs.com/package/@bsv/authsocket) for real-time authenticated WebSocket communication. This enables clients to receive messages instantly and interact with rooms associated with their identity key and chosen messageBox.
|
|
31
|
+
|
|
32
|
+
To interact with MessageBox, use [MessageBoxClient](https://github.com/bitcoin-sv/p2p), the client-side library designed to handle authentication, encryption, WebSocket communication, and overlay resolution.
|
|
33
|
+
|
|
34
|
+
## 2. Concepts
|
|
35
|
+
- **Identity Key:** All messages are addressed to an identity key (a public key)
|
|
36
|
+
- **MessageBox:** A named message stream associated with an identity key (e.g., payment_inbox)
|
|
37
|
+
- **Encrypted Payloads:** Messages can be AES-encrypted and include metadata
|
|
38
|
+
- **Live Messaging:** Clients can join WebSocket rooms and receive messages in real-time
|
|
39
|
+
- **Acknowledgment:** Messages must be acknowledged to be removed from the database
|
|
40
|
+
________________________________________
|
|
41
|
+
|
|
42
|
+
## 3. Environment Variables
|
|
43
|
+
**Variables**
|
|
44
|
+
|
|
45
|
+
NODE_ENV - Set to development, staging, or production
|
|
46
|
+
|
|
47
|
+
PORT - Port for the HTTP server (default: 8080 or 3000)
|
|
48
|
+
|
|
49
|
+
ROUTING_PREFIX - (Optional) Prefix for all HTTP and WebSocket routes (e.g., /api)
|
|
50
|
+
|
|
51
|
+
HOSTING_DOMAIN - (Optional) Full domain where this server is hosted (used in overlay metadata)
|
|
52
|
+
|
|
53
|
+
SERVER_PRIVATE_KEY - Required. 256-bit hex private key used for identity, signing, and authentication
|
|
54
|
+
|
|
55
|
+
WALLET_STORAGE_URL - URL of a wallet-storage service that stores key derivation metadata
|
|
56
|
+
|
|
57
|
+
NETWORK - Target network chain (e.g., main, test, or regtest)
|
|
58
|
+
|
|
59
|
+
KNEX_DB_CONNECTION - (Optional) JSON-formatted connection string for MySQL/Knex (if not using default)
|
|
60
|
+
|
|
61
|
+
ENABLE_WEBSOCKETS - Set to 'true' to enable real-time messaging over WebSocket
|
|
62
|
+
|
|
63
|
+
LOGGING_ENABLED - Set to 'true' to enable verbose debug logging in any environment
|
|
64
|
+
|
|
65
|
+
MIGRATE_KEY - (Optional) Key used to authorize protected migration operations, if required
|
|
66
|
+
|
|
67
|
+
________________________________________
|
|
68
|
+
|
|
69
|
+
## 4. Quick Start
|
|
70
|
+
|
|
71
|
+
To run the MessageBox Server locally or in a hosted environment, follow the steps below.
|
|
72
|
+
|
|
73
|
+
1. **Clone the Repository**
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/bitcoin-sv/messagebox-server.git
|
|
76
|
+
cd messagebox-server
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
2. **Configure Environment Variables**
|
|
80
|
+
Create a .env file based on the variables listed in the Environment Variables section:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
cp .env.example .env
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Then fill in or update the necessary fields in .env, including:
|
|
87
|
+
|
|
88
|
+
SERVER_PRIVATE_KEY – your root private key for identity/auth
|
|
89
|
+
|
|
90
|
+
WALLET_STORAGE_URL – points to a wallet-storage instance
|
|
91
|
+
|
|
92
|
+
(Optional) KNEX_DB_CONNECTION – if you're not using the default MySQL config
|
|
93
|
+
|
|
94
|
+
For local development, use:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
.env
|
|
98
|
+
NODE_ENV=development
|
|
99
|
+
BSV_NETWORK=local
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
3. **Install Dependencies**
|
|
103
|
+
```bash
|
|
104
|
+
npm install
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
4. **Start the Server**
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm run dev
|
|
111
|
+
```
|
|
112
|
+
**Note:**
|
|
113
|
+
MessageBox Server requires a MySQL database for storing messages.
|
|
114
|
+
See [Deploying](#10-deploying) for instructions on setting up the required database locally or in production environments.
|
|
115
|
+
|
|
116
|
+
This launches the Express-based MessageBox Server on the configured port (default: 8080 or 3000 in production). WebSocket support will be enabled if ENABLE_WEBSOCKETS=true in your environment.
|
|
117
|
+
|
|
118
|
+
You can customize the HTTP port using the PORT variable in .env.
|
|
119
|
+
|
|
120
|
+
You're now ready to send and receive messages using the MessageBoxClient, test endpoints, and participate in the overlay network.
|
|
121
|
+
|
|
122
|
+
________________________________________
|
|
123
|
+
|
|
124
|
+
## 5. Examples
|
|
125
|
+
|
|
126
|
+
This section provides quick examples for sending, listing, and acknowledging messages using the MessageBoxClient library.
|
|
127
|
+
|
|
128
|
+
1. **Sending a Message**
|
|
129
|
+
```ts
|
|
130
|
+
import { WalletClient } from '@bsv/sdk'
|
|
131
|
+
import { MessageBoxClient } from '@bsv/p2p'
|
|
132
|
+
|
|
133
|
+
const wallet = new WalletClient()
|
|
134
|
+
const mb = new MessageBoxClient({
|
|
135
|
+
walletClient: wallet,
|
|
136
|
+
host: 'http://localhost:8080' // your MessageBoxServer instance
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
await mb.sendMessage({
|
|
140
|
+
recipient: '028d37b941208cd6b8a4c28288eda5f2f16c2b3ab0fcb6d13c18b47fe37b971fc1',
|
|
141
|
+
messageBox: 'demo_inbox',
|
|
142
|
+
body: { text: 'Hello there!' }
|
|
143
|
+
})
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
2. **Listening for Live Messages (WebSocket)**
|
|
147
|
+
```ts
|
|
148
|
+
await mb.initializeConnection()
|
|
149
|
+
|
|
150
|
+
await mb.listenForLiveMessages({
|
|
151
|
+
messageBox: 'demo_inbox',
|
|
152
|
+
onMessage: (msg) => {
|
|
153
|
+
console.log('New Message:', msg)
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
3. **Listing Messages via HTTP**
|
|
159
|
+
```ts
|
|
160
|
+
const messages = await mb.listMessages({
|
|
161
|
+
messageBox: 'demo_inbox'
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
console.log('All Messages:', messages)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
4. **Acknowledging Messages (Marking as Read)**
|
|
168
|
+
```ts
|
|
169
|
+
const toAcknowledge = messages.map(m => m.messageId.toString())
|
|
170
|
+
|
|
171
|
+
await mb.acknowledgeMessage({
|
|
172
|
+
messageIds: toAcknowledge
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
5. **Sending a Live Message with Fallback**
|
|
177
|
+
```ts
|
|
178
|
+
await mb.sendLiveMessage({
|
|
179
|
+
recipient: '028d37b941208cd6b8a4c28288eda5f2f16c2b3ab0fcb6d13c18b47fe37b971fc1',
|
|
180
|
+
messageBox: 'demo_inbox',
|
|
181
|
+
body: 'This will try WebSocket first, and fallback to HTTP if needed.'
|
|
182
|
+
})
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
________________________________________
|
|
186
|
+
|
|
187
|
+
## 6. API Reference
|
|
188
|
+
The MessageBox Server exposes a small set of authenticated HTTP endpoints to support secure, store-and-forward messaging. All routes require identity-based authentication using @bsv/auth-express-middleware.
|
|
189
|
+
|
|
190
|
+
### POST /sendMessage
|
|
191
|
+
Send a message to a specific recipient’s message box.
|
|
192
|
+
|
|
193
|
+
**Request Body**
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"message": {
|
|
197
|
+
"recipient": "IDENTITY_PUBLIC_KEY",
|
|
198
|
+
"messageBox": "payment_inbox",
|
|
199
|
+
"messageId": "abc123",
|
|
200
|
+
"body": "{\"amount\":10000}"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Response**
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"status": "success",
|
|
209
|
+
"messageId": "abc123",
|
|
210
|
+
"message": "Your message has been sent to IDENTITY_PUBLIC_KEY"
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Messages are persisted in the recipient’s messageBox.
|
|
215
|
+
|
|
216
|
+
messageId must be globally unique (can be derived using HMAC).
|
|
217
|
+
|
|
218
|
+
Duplicate messageIds will be ignored.
|
|
219
|
+
|
|
220
|
+
### POST /listMessages
|
|
221
|
+
List all messages from a given messageBox owned by the authenticated identity.
|
|
222
|
+
|
|
223
|
+
**Request Body**
|
|
224
|
+
```json
|
|
225
|
+
{
|
|
226
|
+
"messageBox": "payment_inbox"
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Response**
|
|
231
|
+
```json
|
|
232
|
+
{
|
|
233
|
+
"status": "success",
|
|
234
|
+
"messages": [
|
|
235
|
+
{
|
|
236
|
+
"messageId": "abc123",
|
|
237
|
+
"body": "{\"amount\":10000}",
|
|
238
|
+
"sender": "SENDER_PUBLIC_KEY",
|
|
239
|
+
"created_at": "2024-12-01T12:00:00Z",
|
|
240
|
+
"updated_at": "2024-12-01T12:01:00Z"
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Message bodies will be returned as strings (plain or encrypted).
|
|
247
|
+
|
|
248
|
+
Empty arrays are returned if no messages exist or the box is unregistered.
|
|
249
|
+
|
|
250
|
+
### POST /acknowledgeMessage
|
|
251
|
+
Permanently delete one or more messages after they have been processed.
|
|
252
|
+
|
|
253
|
+
**Request Body**
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"messageIds": ["abc123", "def456"]
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Response**
|
|
261
|
+
```json
|
|
262
|
+
{
|
|
263
|
+
"status": "success"
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
This action cannot be undone.
|
|
268
|
+
|
|
269
|
+
Only messages owned by the authenticated identity can be acknowledged.
|
|
270
|
+
|
|
271
|
+
Note: All requests must include automatically signed and verified headers following [BRC-103](https://github.com/bitcoin-sv/BRCs/blob/master/peer-to-peer/0103.md) authentication.
|
|
272
|
+
Mutual authentication is handled transparently by [@bsv/auth-express-middleware](https://www.npmjs.com/package/@bsv/auth-express-middleware).
|
|
273
|
+
|
|
274
|
+
________________________________________
|
|
275
|
+
|
|
276
|
+
## 7. WebSocket Support
|
|
277
|
+
The MessageBox Server supports real-time messaging over WebSocket using @bsv/authsocket. This allows clients to send and receive messages instantly without polling.
|
|
278
|
+
|
|
279
|
+
**Connection**
|
|
280
|
+
Clients should connect to the same host and port used by the HTTP server (e.g., ws://localhost:8080) and must authenticate using their identity key.
|
|
281
|
+
|
|
282
|
+
**Authentication**
|
|
283
|
+
There are two ways to authenticate:
|
|
284
|
+
|
|
285
|
+
During initial connection: Send the identityKey as part of the connection handshake.
|
|
286
|
+
|
|
287
|
+
After connecting: Emit an authenticated event with { identityKey }.
|
|
288
|
+
|
|
289
|
+
Once authenticated, the server emits authenticationSuccess. If the key is missing or invalid, authenticationFailed is returned.
|
|
290
|
+
|
|
291
|
+
**Room Format**
|
|
292
|
+
Each messageBox uses a room in the format:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
{identityKey}-{messageBox}
|
|
296
|
+
```
|
|
297
|
+
For example:
|
|
298
|
+
028d37b94120...-payment_inbox
|
|
299
|
+
|
|
300
|
+
**Events**
|
|
301
|
+
|
|
302
|
+
authenticated - Authenticate using an identity key
|
|
303
|
+
joinRoom - Subscribe to a specific messageBox room
|
|
304
|
+
sendMessage - Send a message to a room (triggers DB storage + broadcast)
|
|
305
|
+
sendMessageAck-ROOM - Acknowledgment that the message was received by the server
|
|
306
|
+
sendMessage-ROOM - Broadcasted message to all listeners in the room
|
|
307
|
+
leaveRoom - Unsubscribe from a room
|
|
308
|
+
|
|
309
|
+
**Example WebSocket Usage (Client)**
|
|
310
|
+
```ts
|
|
311
|
+
import { io } from 'socket.io-client'
|
|
312
|
+
|
|
313
|
+
const socket = io('ws://localhost:8080')
|
|
314
|
+
|
|
315
|
+
// Step 1: Authenticate after connection
|
|
316
|
+
socket.emit('authenticated', { identityKey })
|
|
317
|
+
|
|
318
|
+
// Step 2: Join a messageBox room
|
|
319
|
+
socket.emit('joinRoom', '028d...-payment_inbox')
|
|
320
|
+
|
|
321
|
+
// Step 3: Send a message
|
|
322
|
+
socket.emit('sendMessage', {
|
|
323
|
+
roomId: '028d...-payment_inbox',
|
|
324
|
+
message: {
|
|
325
|
+
messageId: 'abc123',
|
|
326
|
+
recipient: '028d...',
|
|
327
|
+
body: JSON.stringify({ hello: 'world' })
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
// Step 4: Listen for acknowledgment and broadcast
|
|
332
|
+
socket.on('sendMessageAck-028d...-payment_inbox', (ack) => {
|
|
333
|
+
console.log('Message acknowledged:', ack)
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
socket.on('sendMessage-028d...-payment_inbox', (msg) => {
|
|
337
|
+
console.log('New message received:', msg)
|
|
338
|
+
})
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Notes**
|
|
342
|
+
Messages sent via WebSocket are also persisted in the database.
|
|
343
|
+
|
|
344
|
+
If a room is not joined or the recipient isn't online, delivery is deferred until the recipient polls via HTTP.
|
|
345
|
+
|
|
346
|
+
If ENABLE_WEBSOCKETS is not set to 'true', this functionality is disabled.
|
|
347
|
+
|
|
348
|
+
________________________________________
|
|
349
|
+
## 8. Authentication
|
|
350
|
+
All HTTP and WebSocket communications use full mutual authentication based on [BRC-103](https://github.com/bitcoin-sv/BRCs/blob/master/peer-to-peer/0103.md).
|
|
351
|
+
- HTTP requests are authenticated using [@bsv/auth-express-middleware](https://www.npmjs.com/package/@bsv/auth-express-middleware), with automatically signed and verified headers.
|
|
352
|
+
- WebSocket connections are authenticated using [@bsv/authsocket](https://www.npmjs.com/package/@bsv/authsocket), signing the WebSocket handshake and room interactions.
|
|
353
|
+
|
|
354
|
+
Clients authenticate automatically when using the [MessageBoxClient](https://github.com/bitcoin-sv/p2p) library. Manual integrations must follow BRC-103 signing conventions.
|
|
355
|
+
|
|
356
|
+
________________________________________
|
|
357
|
+
|
|
358
|
+
## 9. Scripts
|
|
359
|
+
```bash
|
|
360
|
+
npm run dev # Start with hot reloading
|
|
361
|
+
npm run start # Start in production
|
|
362
|
+
npm run test # Run all tests
|
|
363
|
+
npm run build # Compile documentation
|
|
364
|
+
```
|
|
365
|
+
________________________________________
|
|
366
|
+
|
|
367
|
+
## 10. Deploying
|
|
368
|
+
See [DEPLOYING.md](./DEPLOYING.md) for tips.
|
|
369
|
+
________________________________________
|
|
370
|
+
## 11. License
|
|
371
|
+
This project is released under the [Open BSV License](https://www.bsvlicense.org/).
|
|
372
|
+
|
package/knexfile.d.ts
ADDED
package/knexfile.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import dotenv from 'dotenv'
|
|
2
|
+
import type { Knex } from 'knex'
|
|
3
|
+
dotenv.config()
|
|
4
|
+
|
|
5
|
+
const connectionConfig = process.env.KNEX_DB_CONNECTION != null && process.env.KNEX_DB_CONNECTION.trim() !== ''
|
|
6
|
+
? JSON.parse(process.env.KNEX_DB_CONNECTION)
|
|
7
|
+
: undefined
|
|
8
|
+
|
|
9
|
+
const config: Knex.Config = {
|
|
10
|
+
client: process.env.KNEX_DB_CLIENT ?? 'mysql2',
|
|
11
|
+
connection: connectionConfig,
|
|
12
|
+
useNullAsDefault: true,
|
|
13
|
+
migrations: {
|
|
14
|
+
directory: './out/src/migrations'
|
|
15
|
+
},
|
|
16
|
+
pool: {
|
|
17
|
+
min: 0,
|
|
18
|
+
max: 7,
|
|
19
|
+
idleTimeoutMillis: 15000
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const knexfile: { [key: string]: Knex.Config } = {
|
|
24
|
+
development: config,
|
|
25
|
+
staging: config,
|
|
26
|
+
production: config
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default knexfile
|
package/out/knexfile.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
const connectionConfig = process.env.KNEX_DB_CONNECTION != null && process.env.KNEX_DB_CONNECTION.trim() !== ''
|
|
4
|
+
? JSON.parse(process.env.KNEX_DB_CONNECTION)
|
|
5
|
+
: undefined;
|
|
6
|
+
const config = {
|
|
7
|
+
client: process.env.KNEX_DB_CLIENT ?? 'mysql2',
|
|
8
|
+
connection: connectionConfig,
|
|
9
|
+
useNullAsDefault: true,
|
|
10
|
+
migrations: {
|
|
11
|
+
directory: './out/src/migrations'
|
|
12
|
+
},
|
|
13
|
+
pool: {
|
|
14
|
+
min: 0,
|
|
15
|
+
max: 7,
|
|
16
|
+
idleTimeoutMillis: 15000
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const knexfile = {
|
|
20
|
+
development: config,
|
|
21
|
+
staging: config,
|
|
22
|
+
production: config
|
|
23
|
+
};
|
|
24
|
+
export default knexfile;
|
|
25
|
+
//# sourceMappingURL=knexfile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knexfile.js","sourceRoot":"","sources":["../knexfile.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAA;AAE3B,MAAM,CAAC,MAAM,EAAE,CAAA;AAEf,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,EAAE,KAAK,EAAE;IAC7G,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAC5C,CAAC,CAAC,SAAS,CAAA;AAEb,MAAM,MAAM,GAAgB;IAC1B,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,QAAQ;IAC9C,UAAU,EAAE,gBAAgB;IAC5B,gBAAgB,EAAE,IAAI;IACtB,UAAU,EAAE;QACV,SAAS,EAAE,sBAAsB;KAClC;IACD,IAAI,EAAE;QACJ,GAAG,EAAE,CAAC;QACN,GAAG,EAAE,CAAC;QACN,iBAAiB,EAAE,KAAK;KACzB;CACF,CAAA;AAED,MAAM,QAAQ,GAAmC;IAC/C,WAAW,EAAE,MAAM;IACnB,OAAO,EAAE,MAAM;IACf,UAAU,EAAE,MAAM;CACnB,CAAA;AAED,eAAe,QAAQ,CAAA"}
|
package/out/src/app.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file app.ts
|
|
3
|
+
* @description
|
|
4
|
+
* Initializes the MessageBoxServer Express app.
|
|
5
|
+
*
|
|
6
|
+
* Responsibilities:
|
|
7
|
+
* - Parses environment variables and loads config
|
|
8
|
+
* - Sets up Knex for DB access
|
|
9
|
+
* - Initializes WalletClient from the BSV SDK
|
|
10
|
+
* - Mounts Express routes (pre-auth and post-auth)
|
|
11
|
+
* - Applies auth middleware using wallet identity
|
|
12
|
+
*
|
|
13
|
+
* This file exports:
|
|
14
|
+
* - `app`: the configured Express instance
|
|
15
|
+
* - `walletReady`: a promise that resolves once the wallet is ready
|
|
16
|
+
* - `getWallet()`: async accessor for the WalletClient
|
|
17
|
+
* - `useRoutes()`: middleware + route initialization
|
|
18
|
+
* - `appReady`: promise that completes once all setup is done
|
|
19
|
+
*/
|
|
20
|
+
import * as dotenv from 'dotenv';
|
|
21
|
+
import express from 'express';
|
|
22
|
+
import bodyParser from 'body-parser';
|
|
23
|
+
import { preAuth, postAuth } from './routes/index.js';
|
|
24
|
+
import { Logger } from './utils/logger.js';
|
|
25
|
+
import sendMessageRoute from './routes/sendMessage.js';
|
|
26
|
+
import { Setup } from '@bsv/wallet-toolbox';
|
|
27
|
+
import knexLib from 'knex';
|
|
28
|
+
import knexConfig from '../knexfile.js';
|
|
29
|
+
import { createAuthMiddleware } from '@bsv/auth-express-middleware';
|
|
30
|
+
import { createPaymentMiddleware } from '@bsv/payment-express-middleware';
|
|
31
|
+
import { setupSwagger } from './swagger.js';
|
|
32
|
+
import * as crypto from 'crypto';
|
|
33
|
+
global.self = { crypto };
|
|
34
|
+
dotenv.config();
|
|
35
|
+
// Create the Express app instance
|
|
36
|
+
export const app = express();
|
|
37
|
+
// Load environment variables
|
|
38
|
+
const { NODE_ENV = 'development', ROUTING_PREFIX = '', SERVER_PRIVATE_KEY, WALLET_STORAGE_URL, BSV_NETWORK = 'mainnet' } = process.env;
|
|
39
|
+
// Enable logger in dev mode or if explicitly enabled
|
|
40
|
+
if (NODE_ENV === 'development' || process.env.LOGGING_ENABLED === 'true') {
|
|
41
|
+
Logger.enable();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Knex instance connected based on environment (development, production, or staging).
|
|
45
|
+
*/
|
|
46
|
+
export const knex = knexLib.default?.(NODE_ENV === 'production' || NODE_ENV === 'staging'
|
|
47
|
+
? knexConfig.production
|
|
48
|
+
: knexConfig.development) ?? knexLib(NODE_ENV === 'production' || NODE_ENV === 'staging'
|
|
49
|
+
? knexConfig.production
|
|
50
|
+
: knexConfig.development);
|
|
51
|
+
// Wallet initialization logic
|
|
52
|
+
let _wallet;
|
|
53
|
+
let _resolveReady;
|
|
54
|
+
export const walletReady = new Promise((resolve) => {
|
|
55
|
+
_resolveReady = resolve;
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* @function initializeWallet
|
|
59
|
+
* @description Initializes the WalletClient with a root identity key and storage backend.
|
|
60
|
+
*
|
|
61
|
+
* Loads configuration from the environment and connects to the wallet service.
|
|
62
|
+
*
|
|
63
|
+
* @returns {Promise<void>} Resolves when the wallet is initialized.
|
|
64
|
+
* @throws If SERVER_PRIVATE_KEY is missing or invalid.
|
|
65
|
+
*/
|
|
66
|
+
export async function initializeWallet() {
|
|
67
|
+
if (SERVER_PRIVATE_KEY == null || SERVER_PRIVATE_KEY.trim() === '') {
|
|
68
|
+
throw new Error('SERVER_PRIVATE_KEY is not defined in environment variables.');
|
|
69
|
+
}
|
|
70
|
+
_wallet = await Setup.createWalletClientNoEnv({
|
|
71
|
+
chain: BSV_NETWORK === 'testnet' ? 'test' : 'main',
|
|
72
|
+
rootKeyHex: SERVER_PRIVATE_KEY,
|
|
73
|
+
storageUrl: WALLET_STORAGE_URL
|
|
74
|
+
});
|
|
75
|
+
_resolveReady();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* @function getWallet
|
|
79
|
+
* @description Waits for the WalletClient to be ready and returns the instance.
|
|
80
|
+
*
|
|
81
|
+
* @returns {Promise<WalletInterface>} The initialized wallet client
|
|
82
|
+
* @throws {Error} If called before the wallet is initialized
|
|
83
|
+
*/
|
|
84
|
+
export async function getWallet() {
|
|
85
|
+
await walletReady;
|
|
86
|
+
if (_wallet == null) {
|
|
87
|
+
throw new Error('Wallet has not been initialized yet.');
|
|
88
|
+
}
|
|
89
|
+
return _wallet;
|
|
90
|
+
}
|
|
91
|
+
// Run on app startup to prep wallet and activate routes
|
|
92
|
+
export const appReady = (async () => {
|
|
93
|
+
await initializeWallet();
|
|
94
|
+
await useRoutes();
|
|
95
|
+
})();
|
|
96
|
+
/**
|
|
97
|
+
* @function useRoutes
|
|
98
|
+
* @description Registers all routes and middleware on the Express app instance.
|
|
99
|
+
*
|
|
100
|
+
* Steps:
|
|
101
|
+
* - Applies JSON body parser
|
|
102
|
+
* - Enables CORS headers for all routes
|
|
103
|
+
* - Waits for WalletClient to initialize
|
|
104
|
+
* - Adds authentication middleware
|
|
105
|
+
* - Mounts pre-auth and post-auth route handlers
|
|
106
|
+
*
|
|
107
|
+
* @returns {Promise<void>} Once all middleware and routes are mounted
|
|
108
|
+
* @throws If wallet is not available when needed
|
|
109
|
+
*/
|
|
110
|
+
export async function useRoutes() {
|
|
111
|
+
// Parse incoming JSON bodies with a high limit
|
|
112
|
+
app.use(bodyParser.json({ limit: '1gb', type: 'application/json' }));
|
|
113
|
+
// CORS setup
|
|
114
|
+
app.use((req, res, next) => {
|
|
115
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
116
|
+
res.header('Access-Control-Allow-Headers', '*');
|
|
117
|
+
res.header('Access-Control-Allow-Methods', '*');
|
|
118
|
+
res.header('Access-Control-Expose-Headers', '*');
|
|
119
|
+
res.header('Access-Control-Allow-Private-Network', 'true');
|
|
120
|
+
if (req.method === 'OPTIONS') {
|
|
121
|
+
res.sendStatus(200);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
next();
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
// Enable Swagger docs
|
|
128
|
+
setupSwagger(app);
|
|
129
|
+
await walletReady;
|
|
130
|
+
if (_wallet == null) {
|
|
131
|
+
throw new Error('Wallet is not initialized for auth middleware');
|
|
132
|
+
}
|
|
133
|
+
app.use(createAuthMiddleware({
|
|
134
|
+
wallet: _wallet,
|
|
135
|
+
logger: console
|
|
136
|
+
}));
|
|
137
|
+
app.use(createPaymentMiddleware({
|
|
138
|
+
wallet: _wallet,
|
|
139
|
+
calculateRequestPrice: async (req) => {
|
|
140
|
+
if (req.url.includes('/sendMessage')) {
|
|
141
|
+
// TODO: Configure a custom price calculation as needed.
|
|
142
|
+
}
|
|
143
|
+
return 0;
|
|
144
|
+
}
|
|
145
|
+
}));
|
|
146
|
+
// Register pre-authentication routes (no auth required)
|
|
147
|
+
preAuth.forEach((route) => {
|
|
148
|
+
app[route.type](`${String(ROUTING_PREFIX)}${String(route.path)}`, route.func);
|
|
149
|
+
});
|
|
150
|
+
// Register post-authentication routes (requires auth header)
|
|
151
|
+
postAuth.forEach((route) => {
|
|
152
|
+
if (route.path === '/sendMessage') {
|
|
153
|
+
app[route.type](`${ROUTING_PREFIX}${route.path}`, sendMessageRoute.func);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
app[route.type](`${ROUTING_PREFIX}${route.path}`, route.func);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/app.ts"],"names":[],"mappings":"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"}
|