@bopen-io/messagebox-server 1.2.2 → 1.2.4
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/out/knexfile.js +25 -0
- package/out/knexfile.js.map +1 -0
- package/out/src/__tests/compose.test.js +25 -0
- package/out/src/__tests/compose.test.js.map +1 -0
- package/out/src/app.js +162 -0
- package/out/src/app.js.map +1 -0
- package/out/src/compose.js +239 -0
- package/out/src/compose.js.map +1 -0
- package/out/src/config/firebase.js +150 -0
- package/out/src/config/firebase.js.map +1 -0
- package/out/src/context.js +23 -0
- package/out/src/context.js.map +1 -0
- package/out/src/index.js +311 -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 +135 -0
- package/out/src/routes/__tests/acknowledgeMessage.test.js.map +1 -0
- package/out/src/routes/__tests/listMessages.test.js +217 -0
- package/out/src/routes/__tests/listMessages.test.js.map +1 -0
- package/out/src/routes/__tests/sendMessage.test.js +313 -0
- package/out/src/routes/__tests/sendMessage.test.js.map +1 -0
- package/out/src/routes/acknowledgeMessage.js +133 -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 +63 -0
- package/out/src/routes/listDevices.js.map +1 -0
- package/out/src/routes/listMessages.js +176 -0
- package/out/src/routes/listMessages.js.map +1 -0
- package/out/src/routes/permissions/getPermission.js +138 -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 +179 -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 +139 -0
- package/out/src/routes/registerDevice.js.map +1 -0
- package/out/src/routes/sendMessage.js +438 -0
- package/out/src/routes/sendMessage.js.map +1 -0
- package/out/src/runtimeDeps.js +15 -0
- package/out/src/runtimeDeps.js.map +1 -0
- package/out/src/swagger.js +48 -0
- package/out/src/swagger.js.map +1 -0
- package/out/src/telemetry.js +134 -0
- package/out/src/telemetry.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 +90 -0
- package/out/src/utils/logger.js.map +1 -0
- package/out/src/utils/messagePermissions.js +115 -0
- package/out/src/utils/messagePermissions.js.map +1 -0
- package/out/src/utils/sendFCMNotification.js +110 -0
- package/out/src/utils/sendFCMNotification.js.map +1 -0
- package/out/tsconfig.tsbuildinfo +1 -0
- package/out/types/jest.config.d.ts +4 -0
- package/out/types/jest.config.d.ts.map +1 -0
- package/out/types/jest.config.integration.d.ts +5 -0
- package/out/types/jest.config.integration.d.ts.map +1 -0
- package/out/types/knexfile.d.ts +6 -0
- package/out/types/knexfile.d.ts.map +1 -0
- package/out/types/src/__tests/compose.test.d.ts +2 -0
- package/out/types/src/__tests/compose.test.d.ts.map +1 -0
- package/out/types/src/app.d.ts +63 -0
- package/out/types/src/app.d.ts.map +1 -0
- package/out/types/src/compose.d.ts +22 -0
- package/out/types/src/compose.d.ts.map +1 -0
- package/out/types/src/config/firebase.d.ts +34 -0
- package/out/types/src/config/firebase.d.ts.map +1 -0
- package/out/types/src/context.d.ts +23 -0
- package/out/types/src/context.d.ts.map +1 -0
- package/out/types/src/index.d.ts +43 -0
- package/out/types/src/index.d.ts.map +1 -0
- package/out/types/src/migrations/2022-12-28-001-initial-migration.d.ts +4 -0
- package/out/types/src/migrations/2022-12-28-001-initial-migration.d.ts.map +1 -0
- package/out/types/src/migrations/2023-01-17-messages-update.d.ts +4 -0
- package/out/types/src/migrations/2023-01-17-messages-update.d.ts.map +1 -0
- package/out/types/src/migrations/2024-03-05-001-messageID-upgrade.d.ts +4 -0
- package/out/types/src/migrations/2024-03-05-001-messageID-upgrade.d.ts.map +1 -0
- package/out/types/src/migrations/2025-01-31-001-notification-permissions.d.ts +4 -0
- package/out/types/src/migrations/2025-01-31-001-notification-permissions.d.ts.map +1 -0
- package/out/types/src/migrations/2025-01-31-002-device-registrations.d.ts +4 -0
- package/out/types/src/migrations/2025-01-31-002-device-registrations.d.ts.map +1 -0
- package/out/types/src/routes/__tests/acknowledgeMessage.test.d.ts +2 -0
- package/out/types/src/routes/__tests/acknowledgeMessage.test.d.ts.map +1 -0
- package/out/types/src/routes/__tests/listMessages.test.d.ts +2 -0
- package/out/types/src/routes/__tests/listMessages.test.d.ts.map +1 -0
- package/out/types/src/routes/__tests/sendMessage.test.d.ts +2 -0
- package/out/types/src/routes/__tests/sendMessage.test.d.ts.map +1 -0
- package/out/types/src/routes/acknowledgeMessage.d.ts +100 -0
- package/out/types/src/routes/acknowledgeMessage.d.ts.map +1 -0
- package/out/types/src/routes/index.d.ts +11 -0
- package/out/types/src/routes/index.d.ts.map +1 -0
- package/out/types/src/routes/listDevices.d.ts +19 -0
- package/out/types/src/routes/listDevices.d.ts.map +1 -0
- package/out/types/src/routes/listMessages.d.ts +124 -0
- package/out/types/src/routes/listMessages.d.ts.map +1 -0
- package/out/types/src/routes/permissions/getPermission.d.ts +46 -0
- package/out/types/src/routes/permissions/getPermission.d.ts.map +1 -0
- package/out/types/src/routes/permissions/getQuote.d.ts +50 -0
- package/out/types/src/routes/permissions/getQuote.d.ts.map +1 -0
- package/out/types/src/routes/permissions/index.d.ts +18 -0
- package/out/types/src/routes/permissions/index.d.ts.map +1 -0
- package/out/types/src/routes/permissions/listPermissions.d.ts +106 -0
- package/out/types/src/routes/permissions/listPermissions.d.ts.map +1 -0
- package/out/types/src/routes/permissions/setPermission.d.ts +53 -0
- package/out/types/src/routes/permissions/setPermission.d.ts.map +1 -0
- package/out/types/src/routes/registerDevice.d.ts +67 -0
- package/out/types/src/routes/registerDevice.d.ts.map +1 -0
- package/out/types/src/routes/sendMessage.d.ts +140 -0
- package/out/types/src/routes/sendMessage.d.ts.map +1 -0
- package/out/types/src/runtimeDeps.d.ts +17 -0
- package/out/types/src/runtimeDeps.d.ts.map +1 -0
- package/out/types/src/swagger.d.ts +3 -0
- package/out/types/src/swagger.d.ts.map +1 -0
- package/out/types/src/telemetry.d.ts +2 -0
- package/out/types/src/telemetry.d.ts.map +1 -0
- package/out/types/src/types/messagePermissions.d.ts +15 -0
- package/out/types/src/types/messagePermissions.d.ts.map +1 -0
- package/out/types/src/types/notifications.d.ts +20 -0
- package/out/types/src/types/notifications.d.ts.map +1 -0
- package/out/types/src/utils/logger.d.ts +17 -0
- package/out/types/src/utils/logger.d.ts.map +1 -0
- package/out/types/src/utils/messagePermissions.d.ts +29 -0
- package/out/types/src/utils/messagePermissions.d.ts.map +1 -0
- package/out/types/src/utils/sendFCMNotification.d.ts +22 -0
- package/out/types/src/utils/sendFCMNotification.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/utils/logger.ts +6 -2
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file sendMessage.ts
|
|
3
|
+
* @description
|
|
4
|
+
* Route handler to send a message to another identity's messageBox.
|
|
5
|
+
* This route is used for P2P communication in the MessageBox system.
|
|
6
|
+
*
|
|
7
|
+
* It handles:
|
|
8
|
+
* - Validation of message structure
|
|
9
|
+
* - Validation of the recipient public key
|
|
10
|
+
* - MessageBox creation if one doesn't exist
|
|
11
|
+
* - Insertion of the message into the database
|
|
12
|
+
* - Deduplication based on messageId
|
|
13
|
+
*
|
|
14
|
+
*/
|
|
15
|
+
import { PublicKey, } from '@bsv/sdk';
|
|
16
|
+
import { Logger, log } from '../utils/logger.js';
|
|
17
|
+
import { sendFCMNotification } from '../utils/sendFCMNotification.js';
|
|
18
|
+
import { getRecipientFee, getServerDeliveryFee, shouldUseFCMDelivery } from '../utils/messagePermissions.js';
|
|
19
|
+
import { runtimeDeps, getWallet } from '../runtimeDeps.js';
|
|
20
|
+
const { SERVER_PRIVATE_KEY } = process.env;
|
|
21
|
+
// Validate critical server-side secret
|
|
22
|
+
if (SERVER_PRIVATE_KEY == null || SERVER_PRIVATE_KEY.trim() === '') {
|
|
23
|
+
throw new Error('SERVER_PRIVATE_KEY is not defined in the environment variables.');
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @function calculateMessagePrice
|
|
27
|
+
* @description Determines the price (in satoshis) to send a message, optionally with priority.
|
|
28
|
+
*/
|
|
29
|
+
export function calculateMessagePrice(message, priority = false) {
|
|
30
|
+
const basePrice = 2; // Base fee in satoshis
|
|
31
|
+
const sizeFactor = Math.ceil(Buffer.byteLength(message, 'utf8') / 1024) * 3; // Satoshis per KB
|
|
32
|
+
return basePrice + sizeFactor;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* @openapi
|
|
36
|
+
* /sendMessage:
|
|
37
|
+
* post:
|
|
38
|
+
* summary: Send a message to a recipient’s message box
|
|
39
|
+
* description: |
|
|
40
|
+
* Inserts a message into the target recipient’s message box on the server.
|
|
41
|
+
* The recipient, message box name, and message ID must be provided.
|
|
42
|
+
* tags:
|
|
43
|
+
* - Message
|
|
44
|
+
* requestBody:
|
|
45
|
+
* required: true
|
|
46
|
+
* content:
|
|
47
|
+
* application/json:
|
|
48
|
+
* schema:
|
|
49
|
+
* type: object
|
|
50
|
+
* properties:
|
|
51
|
+
* message:
|
|
52
|
+
* type: object
|
|
53
|
+
* required:
|
|
54
|
+
* - recipient
|
|
55
|
+
* - messageBox
|
|
56
|
+
* - messageId
|
|
57
|
+
* - body
|
|
58
|
+
* properties:
|
|
59
|
+
* recipient:
|
|
60
|
+
* type: string
|
|
61
|
+
* description: Identity key of the recipient
|
|
62
|
+
* messageBox:
|
|
63
|
+
* type: string
|
|
64
|
+
* description: The name of the recipient's message box
|
|
65
|
+
* messageId:
|
|
66
|
+
* type: string
|
|
67
|
+
* description: Unique identifier for the message (usually an HMAC)
|
|
68
|
+
* body:
|
|
69
|
+
* oneOf:
|
|
70
|
+
* - type: string
|
|
71
|
+
* - type: object
|
|
72
|
+
* description: The message content
|
|
73
|
+
* responses:
|
|
74
|
+
* 200:
|
|
75
|
+
* description: Message stored successfully
|
|
76
|
+
* content:
|
|
77
|
+
* application/json:
|
|
78
|
+
* schema:
|
|
79
|
+
* type: object
|
|
80
|
+
* properties:
|
|
81
|
+
* status:
|
|
82
|
+
* type: string
|
|
83
|
+
* example: success
|
|
84
|
+
* messageId:
|
|
85
|
+
* type: string
|
|
86
|
+
* message:
|
|
87
|
+
* type: string
|
|
88
|
+
* 400:
|
|
89
|
+
* description: Invalid request or duplicate message
|
|
90
|
+
* 500:
|
|
91
|
+
* description: Internal server error
|
|
92
|
+
*/
|
|
93
|
+
/**
|
|
94
|
+
* @exports
|
|
95
|
+
* Express-compatible route definition for `/sendMessage`, used to send messages to other users.
|
|
96
|
+
* Contains metadata for auto-generation of route documentation and Swagger/OpenAPI integration.
|
|
97
|
+
*/
|
|
98
|
+
export default {
|
|
99
|
+
type: 'post',
|
|
100
|
+
path: '/sendMessage',
|
|
101
|
+
get knex() { return runtimeDeps.knex; },
|
|
102
|
+
summary: "Use this route to send a message to a recipient's message box.",
|
|
103
|
+
parameters: {
|
|
104
|
+
message: {
|
|
105
|
+
recipient: '028d37b941208cd6b8a4c28288eda5f2f16c2b3ab0fcb6d13c18b47fe37b971fc1',
|
|
106
|
+
messageBox: 'payment_inbox',
|
|
107
|
+
messageId: 'xyz123',
|
|
108
|
+
body: '{}'
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
exampleResponse: { status: 'success' },
|
|
112
|
+
func: async (req, res) => {
|
|
113
|
+
Logger.log('[DEBUG] Processing /sendMessage request...');
|
|
114
|
+
Logger.log('[DEBUG] Request Headers:', JSON.stringify(req.headers, null, 2));
|
|
115
|
+
const senderKey = req.auth?.identityKey;
|
|
116
|
+
if (senderKey == null) {
|
|
117
|
+
return res.status(401).json({
|
|
118
|
+
status: 'error',
|
|
119
|
+
code: 'ERR_AUTH_REQUIRED',
|
|
120
|
+
description: 'Authentication required'
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const { message, payment } = req.body;
|
|
125
|
+
log.info({ operation: 'message.send', message_box: message?.messageBox, has_payment: payment != null }, 'Received message send request');
|
|
126
|
+
if (message == null) {
|
|
127
|
+
Logger.error('[ERROR] No message provided in request body!');
|
|
128
|
+
return res.status(400).json({
|
|
129
|
+
status: 'error',
|
|
130
|
+
code: 'ERR_MESSAGE_REQUIRED',
|
|
131
|
+
description: 'Please provide a valid message to send!'
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (typeof message.messageBox !== 'string' || message.messageBox.trim() === '') {
|
|
135
|
+
return res.status(400).json({ status: 'error', code: 'ERR_INVALID_MESSAGEBOX', description: 'Invalid message box.' });
|
|
136
|
+
}
|
|
137
|
+
if ((typeof message.body !== 'string' && (typeof message.body !== 'object' || message.body === null)) ||
|
|
138
|
+
(typeof message.body === 'string' && message.body.trim() === '')) {
|
|
139
|
+
return res.status(400).json({ status: 'error', code: 'ERR_INVALID_MESSAGE_BODY', description: 'Invalid message body.' });
|
|
140
|
+
}
|
|
141
|
+
// ---------- Back-compat normalization ----------
|
|
142
|
+
// Accept message.recipients (array) or message.recipient (string|array)
|
|
143
|
+
const recipientsRaw = message.recipients ?? message.recipient;
|
|
144
|
+
if (recipientsRaw == null) {
|
|
145
|
+
return res.status(400).json({
|
|
146
|
+
status: 'error',
|
|
147
|
+
code: 'ERR_RECIPIENT_REQUIRED',
|
|
148
|
+
description: 'Missing recipient(s). Provide "recipient" or "recipients".'
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
const recipients = Array.isArray(recipientsRaw)
|
|
152
|
+
? recipientsRaw
|
|
153
|
+
: [recipientsRaw];
|
|
154
|
+
const messageIdRaw = message.messageId;
|
|
155
|
+
if (messageIdRaw == null) {
|
|
156
|
+
return res.status(400).json({
|
|
157
|
+
status: 'error',
|
|
158
|
+
code: 'ERR_MESSAGEID_REQUIRED',
|
|
159
|
+
description: 'Missing messageId.'
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
const messageIds = Array.isArray(messageIdRaw)
|
|
163
|
+
? messageIdRaw
|
|
164
|
+
: [messageIdRaw];
|
|
165
|
+
// If multiple recipients but only one messageId provided, fail clearly (avoid accidental reuse)
|
|
166
|
+
if (recipients.length > 1 && messageIds.length === 1) {
|
|
167
|
+
return res.status(400).json({
|
|
168
|
+
status: 'error',
|
|
169
|
+
code: 'ERR_MESSAGEID_COUNT_MISMATCH',
|
|
170
|
+
description: `Provided 1 messageId for ${recipients.length} recipients. Provide one messageId per recipient (same order).`
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (messageIds.length !== recipients.length) {
|
|
174
|
+
return res.status(400).json({
|
|
175
|
+
status: 'error',
|
|
176
|
+
code: 'ERR_MESSAGEID_COUNT_MISMATCH',
|
|
177
|
+
description: `Recipients (${recipients.length}) and messageId count (${messageIds.length}) must match.`
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// Validate each messageId
|
|
181
|
+
for (const id of messageIds) {
|
|
182
|
+
if (typeof id !== 'string' || id.trim() === '') {
|
|
183
|
+
return res.status(400).json({ status: 'error', code: 'ERR_INVALID_MESSAGEID', description: 'Each messageId must be a non-empty string.' });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Validate recipient keys & build map(recipient -> messageId)
|
|
187
|
+
const recipientsTrimmed = recipients.map(r => String(r).trim());
|
|
188
|
+
const msgIdByRecipient = new Map();
|
|
189
|
+
for (let i = 0; i < recipientsTrimmed.length; i++) {
|
|
190
|
+
const r = recipientsTrimmed[i];
|
|
191
|
+
try {
|
|
192
|
+
PublicKey.fromString(r);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return res.status(400).json({
|
|
196
|
+
status: 'error',
|
|
197
|
+
code: 'ERR_INVALID_RECIPIENT_KEY',
|
|
198
|
+
description: `Invalid recipient key: ${r}`
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
msgIdByRecipient.set(r, messageIds[i]);
|
|
202
|
+
}
|
|
203
|
+
// Ensure messageBox exists for each recipient
|
|
204
|
+
const boxType = message.messageBox.trim();
|
|
205
|
+
for (const r of recipientsTrimmed) {
|
|
206
|
+
const existing = await runtimeDeps.knex('messageBox').where({ identityKey: r, type: boxType }).first();
|
|
207
|
+
if (!existing) {
|
|
208
|
+
await runtimeDeps.knex('messageBox').insert({
|
|
209
|
+
identityKey: r, type: boxType, created_at: new Date(), updated_at: new Date()
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// ---------- Fee evaluation ----------
|
|
214
|
+
const deliveryFeeOnce = await getServerDeliveryFee(boxType);
|
|
215
|
+
const feeRows = [];
|
|
216
|
+
for (const r of recipientsTrimmed) {
|
|
217
|
+
const rf = await getRecipientFee(r, senderKey, boxType); // -1 = blocked; 0 = allow; >0 = sats required
|
|
218
|
+
if (rf === -1)
|
|
219
|
+
feeRows.push({ recipient: r, recipientFee: rf, allowed: false, blockedReason: `Messages to ${r} are blocked` });
|
|
220
|
+
else
|
|
221
|
+
feeRows.push({ recipient: r, recipientFee: rf, allowed: true });
|
|
222
|
+
}
|
|
223
|
+
// Blocked recipients short-circuit
|
|
224
|
+
const blocked = feeRows.filter(f => !f.allowed).map(f => f.recipient);
|
|
225
|
+
if (blocked.length) {
|
|
226
|
+
return res.status(403).json({
|
|
227
|
+
status: 'error',
|
|
228
|
+
code: 'ERR_DELIVERY_BLOCKED',
|
|
229
|
+
description: `Blocked recipients: ${blocked.join(', ')}`,
|
|
230
|
+
blockedRecipients: blocked
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
const anyRecipientFee = feeRows.some(f => f.recipientFee > 0);
|
|
234
|
+
const requiresPayment = (deliveryFeeOnce > 0) || anyRecipientFee;
|
|
235
|
+
// ---------- Payment internalization (batch) ----------
|
|
236
|
+
const perRecipientOutputs = new Map();
|
|
237
|
+
if (requiresPayment) {
|
|
238
|
+
if (!payment?.tx || !Array.isArray(payment.outputs)) {
|
|
239
|
+
return res.status(400).json({
|
|
240
|
+
status: 'error',
|
|
241
|
+
code: 'ERR_MISSING_PAYMENT_TX',
|
|
242
|
+
description: 'Payment transaction data is required for payable delivery.'
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
// Enforce: index 0 is server delivery output (when needed)
|
|
246
|
+
if (deliveryFeeOnce > 0) {
|
|
247
|
+
if (payment.outputs.length === 0) {
|
|
248
|
+
return res.status(400).json({
|
|
249
|
+
status: 'error',
|
|
250
|
+
code: 'ERR_MISSING_DELIVERY_OUTPUT',
|
|
251
|
+
description: 'Delivery fee required but no outputs were provided.'
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
const serverDeliveryOutput = payment.outputs[0];
|
|
255
|
+
try {
|
|
256
|
+
const wallet = await getWallet();
|
|
257
|
+
const internalizeResult = await wallet.internalizeAction({
|
|
258
|
+
tx: payment.tx,
|
|
259
|
+
outputs: [serverDeliveryOutput],
|
|
260
|
+
description: payment.description ?? 'MessageBox delivery payment (batch)'
|
|
261
|
+
});
|
|
262
|
+
if (!internalizeResult.accepted) {
|
|
263
|
+
return res.status(400).json({
|
|
264
|
+
status: 'error',
|
|
265
|
+
code: 'ERR_INSUFFICIENT_PAYMENT',
|
|
266
|
+
description: 'Payment was not accepted by the server.'
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
Logger.log('[DEBUG] Internalized server delivery output at index 0');
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
Logger.error('[ERROR] Failed to internalize delivery fee payment:', error);
|
|
273
|
+
return res.status(500).json({
|
|
274
|
+
status: 'error',
|
|
275
|
+
code: 'ERR_INTERNALIZE_FAILED',
|
|
276
|
+
description: `Failed to internalize payment: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// ---------- Build per-recipient outputs ----------
|
|
281
|
+
const recipientSideOutputs = payment.outputs.slice(deliveryFeeOnce > 0 ? 1 : 0);
|
|
282
|
+
log.info({ operation: 'message.send', recipient_output_count: recipientSideOutputs.length, total_output_count: payment.outputs.length }, 'Payment outputs');
|
|
283
|
+
const feeRecipients = feeRows.filter(f => f.recipientFee > 0).map(f => f.recipient);
|
|
284
|
+
// Try explicit mapping via customInstructions (in insertionRemittance OR paymentRemittance)
|
|
285
|
+
const outputsByRecipientKey = new Map();
|
|
286
|
+
const usedIndexes = new Set();
|
|
287
|
+
for (const out of recipientSideOutputs) {
|
|
288
|
+
const raw = out?.insertionRemittance?.customInstructions ??
|
|
289
|
+
out?.paymentRemittance?.customInstructions ??
|
|
290
|
+
out?.customInstructions;
|
|
291
|
+
if (!raw)
|
|
292
|
+
continue;
|
|
293
|
+
try {
|
|
294
|
+
const instr = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
295
|
+
const key = instr?.recipientIdentityKey;
|
|
296
|
+
if (typeof key === 'string' && key.trim() !== '') {
|
|
297
|
+
if (!outputsByRecipientKey.has(key))
|
|
298
|
+
outputsByRecipientKey.set(key, []);
|
|
299
|
+
outputsByRecipientKey.get(key).push(out);
|
|
300
|
+
if (typeof out?.outputIndex === 'number') {
|
|
301
|
+
usedIndexes.add(out.outputIndex);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
// ignore unparsable instructions
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (outputsByRecipientKey.size === 0) {
|
|
310
|
+
// No explicit tags: fallback to positional mapping for recipients that require a fee
|
|
311
|
+
if (recipientSideOutputs.length < feeRecipients.length) {
|
|
312
|
+
return res.status(400).json({
|
|
313
|
+
status: 'error',
|
|
314
|
+
code: 'ERR_INSUFFICIENT_OUTPUTS',
|
|
315
|
+
description: `Expected at least ${feeRecipients.length} recipient output(s) but received ${recipientSideOutputs.length}`
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
feeRecipients.forEach((r, idx) => {
|
|
319
|
+
const out = recipientSideOutputs[idx];
|
|
320
|
+
if (!perRecipientOutputs.has(r))
|
|
321
|
+
perRecipientOutputs.set(r, []);
|
|
322
|
+
perRecipientOutputs.get(r).push(out);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
// Use tagged outputs where present
|
|
327
|
+
for (const r of feeRecipients) {
|
|
328
|
+
const tagged = outputsByRecipientKey.get(r) ?? [];
|
|
329
|
+
if (tagged.length > 0) {
|
|
330
|
+
perRecipientOutputs.set(r, tagged);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// For any remaining fee recipients without tags, allocate unused outputs (positional)
|
|
334
|
+
const unmapped = feeRecipients.filter(r => !perRecipientOutputs.has(r));
|
|
335
|
+
if (unmapped.length > 0) {
|
|
336
|
+
const remaining = recipientSideOutputs.filter(o => {
|
|
337
|
+
const idx = o?.outputIndex;
|
|
338
|
+
return typeof idx === 'number' ? !usedIndexes.has(idx) : true;
|
|
339
|
+
});
|
|
340
|
+
if (remaining.length < unmapped.length) {
|
|
341
|
+
return res.status(400).json({
|
|
342
|
+
status: 'error',
|
|
343
|
+
code: 'ERR_INSUFFICIENT_OUTPUTS',
|
|
344
|
+
description: `Expected at least ${unmapped.length} additional recipient output(s) but only ${remaining.length} remain`
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
unmapped.forEach((r, i) => {
|
|
348
|
+
const out = remaining[i];
|
|
349
|
+
if (!perRecipientOutputs.has(r))
|
|
350
|
+
perRecipientOutputs.set(r, []);
|
|
351
|
+
perRecipientOutputs.get(r).push(out);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
// Final safety check
|
|
355
|
+
for (const r of feeRecipients) {
|
|
356
|
+
if (!perRecipientOutputs.has(r) || perRecipientOutputs.get(r).length === 0) {
|
|
357
|
+
return res.status(400).json({
|
|
358
|
+
status: 'error',
|
|
359
|
+
code: 'ERR_MISSING_RECIPIENT_OUTPUTS',
|
|
360
|
+
description: `Recipient fee required but no outputs were provided for ${r}`
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// ---------- Store messages (one per recipient) ----------
|
|
367
|
+
const results = [];
|
|
368
|
+
for (const { recipient: r } of feeRows) {
|
|
369
|
+
const mb = await runtimeDeps.knex('messageBox')
|
|
370
|
+
.where({ identityKey: r, type: boxType })
|
|
371
|
+
.select('messageBoxId')
|
|
372
|
+
.first();
|
|
373
|
+
const perRecipientMessageId = msgIdByRecipient.get(r);
|
|
374
|
+
if (!perRecipientMessageId) {
|
|
375
|
+
return res.status(400).json({
|
|
376
|
+
status: 'error',
|
|
377
|
+
code: 'ERR_INVALID_MESSAGEID',
|
|
378
|
+
description: `Missing messageId for recipient ${r}`
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
const perRecipientPayment = perRecipientOutputs.has(r) && req.body.payment
|
|
382
|
+
? { ...req.body.payment, outputs: perRecipientOutputs.get(r) }
|
|
383
|
+
: undefined;
|
|
384
|
+
const storedBody = {
|
|
385
|
+
message: message.body,
|
|
386
|
+
...(perRecipientPayment && { payment: perRecipientPayment })
|
|
387
|
+
};
|
|
388
|
+
try {
|
|
389
|
+
await runtimeDeps.knex('messages')
|
|
390
|
+
.insert({
|
|
391
|
+
messageId: perRecipientMessageId,
|
|
392
|
+
messageBoxId: mb?.messageBoxId ?? null,
|
|
393
|
+
sender: senderKey,
|
|
394
|
+
recipient: r,
|
|
395
|
+
body: JSON.stringify(storedBody),
|
|
396
|
+
created_at: new Date(),
|
|
397
|
+
updated_at: new Date()
|
|
398
|
+
})
|
|
399
|
+
.onConflict('messageId')
|
|
400
|
+
.ignore();
|
|
401
|
+
results.push({ recipient: r, messageId: perRecipientMessageId });
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
if (error?.code === 'ER_DUP_ENTRY') {
|
|
405
|
+
return res.status(400).json({
|
|
406
|
+
status: 'error',
|
|
407
|
+
code: 'ERR_DUPLICATE_MESSAGE',
|
|
408
|
+
description: 'Duplicate message.'
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
throw error;
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
if (shouldUseFCMDelivery(boxType)) {
|
|
415
|
+
await sendFCMNotification(r, { title: 'New Message', messageId: perRecipientMessageId });
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
catch (deliveryError) {
|
|
419
|
+
Logger.error('[ERROR] Error processing FCM delivery:', deliveryError);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return res.status(200).json({
|
|
423
|
+
status: 'success',
|
|
424
|
+
message: `Your message has been sent to ${results.length} recipient(s).`,
|
|
425
|
+
results
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
Logger.error('[ERROR] Internal Server Error:', error);
|
|
430
|
+
return res.status(500).json({
|
|
431
|
+
status: 'error',
|
|
432
|
+
code: 'ERR_INTERNAL',
|
|
433
|
+
description: 'An internal error has occurred.'
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
//# sourceMappingURL=sendMessage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sendMessage.js","sourceRoot":"","sources":["../../../src/routes/sendMessage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Bound before serving; routes read knex/wallet from here. */
|
|
2
|
+
export const runtimeDeps = {
|
|
3
|
+
knex: null
|
|
4
|
+
};
|
|
5
|
+
export function bindMessageBoxRuntime(deps) {
|
|
6
|
+
runtimeDeps.knex = deps.knex;
|
|
7
|
+
runtimeDeps.wallet = deps.wallet;
|
|
8
|
+
}
|
|
9
|
+
export async function getWallet() {
|
|
10
|
+
if (runtimeDeps.wallet == null) {
|
|
11
|
+
throw new Error('Wallet is not initialized');
|
|
12
|
+
}
|
|
13
|
+
return runtimeDeps.wallet;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=runtimeDeps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtimeDeps.js","sourceRoot":"","sources":["../../src/runtimeDeps.ts"],"names":[],"mappings":"AAOA,+DAA+D;AAC/D,MAAM,CAAC,MAAM,WAAW,GAGpB;IACF,IAAI,EAAE,IAAuB;CAC9B,CAAA;AAED,MAAM,UAAU,qBAAqB,CAAE,IAGtC;IACC,WAAW,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;IAC5B,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;AAClC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,IAAI,WAAW,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,WAAW,CAAC,MAAM,CAAA;AAC3B,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import swaggerJsdoc from 'swagger-jsdoc';
|
|
2
|
+
import swaggerUi from 'swagger-ui-express';
|
|
3
|
+
export function setupSwagger(app) {
|
|
4
|
+
const options = {
|
|
5
|
+
definition: {
|
|
6
|
+
openapi: '3.0.0',
|
|
7
|
+
info: {
|
|
8
|
+
title: 'MessageBox Server API',
|
|
9
|
+
version: '1.0.0',
|
|
10
|
+
description: 'API documentation for the MessageBox Server, including message delivery, retrieval, acknowledgment, and overlay routing.'
|
|
11
|
+
},
|
|
12
|
+
servers: [
|
|
13
|
+
{
|
|
14
|
+
url: 'http://localhost:5001',
|
|
15
|
+
description: 'Local Development Server'
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
url: 'https://messagebox.babbage.systems',
|
|
19
|
+
description: 'Production MessageBox Server'
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
components: {
|
|
23
|
+
securitySchemes: {
|
|
24
|
+
BearerAuth: {
|
|
25
|
+
type: 'http',
|
|
26
|
+
scheme: 'bearer',
|
|
27
|
+
bearerFormat: 'JWT',
|
|
28
|
+
description: 'Provide your signed identity key JWT in the `Authorization` header as: `Bearer <token>`'
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
security: [
|
|
33
|
+
{
|
|
34
|
+
BearerAuth: []
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
apis: ['./src/routes/*.ts']
|
|
39
|
+
};
|
|
40
|
+
const swaggerSpec = swaggerJsdoc(options);
|
|
41
|
+
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
|
42
|
+
// Serve raw OpenAPI spec at /openapi.json
|
|
43
|
+
app.get('/openapi.json', (_req, res) => {
|
|
44
|
+
res.setHeader('Content-Type', 'application/json');
|
|
45
|
+
res.send(swaggerSpec);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=swagger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swagger.js","sourceRoot":"","sources":["../../src/swagger.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAA;AACxC,OAAO,SAAS,MAAM,oBAAoB,CAAA;AAG1C,MAAM,UAAU,YAAY,CAAE,GAAY;IACxC,MAAM,OAAO,GAAG;QACd,UAAU,EAAE;YACV,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE;gBACJ,KAAK,EAAE,uBAAuB;gBAC9B,OAAO,EAAE,OAAO;gBAChB,WAAW,EAAE,0HAA0H;aACxI;YACD,OAAO,EAAE;gBACP;oBACE,GAAG,EAAE,uBAAuB;oBAC5B,WAAW,EAAE,0BAA0B;iBACxC;gBACD;oBACE,GAAG,EAAE,oCAAoC;oBACzC,WAAW,EAAE,8BAA8B;iBAC5C;aACF;YACD,UAAU,EAAE;gBACV,eAAe,EAAE;oBACf,UAAU,EAAE;wBACV,IAAI,EAAE,MAAM;wBACZ,MAAM,EAAE,QAAQ;wBAChB,YAAY,EAAE,KAAK;wBACnB,WAAW,EAAE,yFAAyF;qBACvG;iBACF;aACF;YACD,QAAQ,EAAE;gBACR;oBACE,UAAU,EAAE,EAAE;iBACf;aACF;SACF;QACD,IAAI,EAAE,CAAC,mBAAmB,CAAC;KAC5B,CAAA;IAED,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,CAIxC;IAAC,GAAW,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAA;IAEzE,0CAA0C;IAC1C,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACrC,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAA;QACjD,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IACvB,CAAC,CAAC,CAAA;AACJ,CAAC"}
|