@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
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { initializeApp, getApps, getApp, cert, applicationDefault } from 'firebase-admin/app';
|
|
2
|
+
import { getMessaging } from 'firebase-admin/messaging';
|
|
3
|
+
import { getFirestore } from 'firebase-admin/firestore';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import dotenv from 'dotenv';
|
|
6
|
+
dotenv.config();
|
|
7
|
+
let firebaseApp = null;
|
|
8
|
+
/**
|
|
9
|
+
* Initialize Firebase Admin SDK.
|
|
10
|
+
* Returns null (and logs a warning) when ENABLE_FIREBASE is not 'true',
|
|
11
|
+
* or when FIREBASE_PROJECT_ID is absent, so the server can run without Firebase.
|
|
12
|
+
*/
|
|
13
|
+
export function initializeFirebase() {
|
|
14
|
+
const enableFirebase = process.env.ENABLE_FIREBASE;
|
|
15
|
+
if (enableFirebase !== 'true') {
|
|
16
|
+
console.log(`Firebase is disabled (ENABLE_FIREBASE=${enableFirebase ?? 'unset'}). Skipping initialization.`);
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
if (firebaseApp != null) {
|
|
20
|
+
console.log('Firebase already initialized');
|
|
21
|
+
return firebaseApp;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const serviceAccountJson = process.env.FIREBASE_SERVICE_ACCOUNT_JSON;
|
|
25
|
+
const serviceAccountPath = process.env.FIREBASE_SERVICE_ACCOUNT_PATH;
|
|
26
|
+
const projectId = process.env.FIREBASE_PROJECT_ID;
|
|
27
|
+
if (projectId == null || projectId === '') {
|
|
28
|
+
throw new Error('FIREBASE_PROJECT_ID environment variable is required');
|
|
29
|
+
}
|
|
30
|
+
let firebaseCredential; // Will be assigned based on auth method
|
|
31
|
+
if (serviceAccountJson != null && serviceAccountJson !== '') {
|
|
32
|
+
console.log('Using Firebase service account from environment variable');
|
|
33
|
+
try {
|
|
34
|
+
console.log('Service account JSON length:', serviceAccountJson.length);
|
|
35
|
+
console.log('First 100 chars:', serviceAccountJson.substring(0, 100));
|
|
36
|
+
// Debug credential functions
|
|
37
|
+
console.log('cert function:', typeof cert);
|
|
38
|
+
console.log('applicationDefault function:', typeof applicationDefault);
|
|
39
|
+
const serviceAccount = JSON.parse(serviceAccountJson);
|
|
40
|
+
console.log('Parsed service account keys:', Object.keys(serviceAccount ?? {}));
|
|
41
|
+
if (serviceAccount == null || typeof serviceAccount !== 'object') {
|
|
42
|
+
throw new Error('Parsed service account is not a valid object');
|
|
43
|
+
}
|
|
44
|
+
if (serviceAccount.private_key == null || serviceAccount.client_email == null || serviceAccount.project_id == null) {
|
|
45
|
+
throw new Error('Service account missing required fields (private_key, client_email, project_id)');
|
|
46
|
+
}
|
|
47
|
+
firebaseCredential = cert(serviceAccount);
|
|
48
|
+
console.log('✅ Firebase credential created successfully');
|
|
49
|
+
}
|
|
50
|
+
catch (parseError) {
|
|
51
|
+
console.error('❌ Firebase service account parsing failed:', parseError);
|
|
52
|
+
throw new Error(`Failed to parse FIREBASE_SERVICE_ACCOUNT_JSON: ${(parseError instanceof Error) ? parseError.message : 'Invalid JSON'}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else if (serviceAccountPath != null && serviceAccountPath !== '') {
|
|
56
|
+
console.log('Using Firebase service account key file');
|
|
57
|
+
const absolutePath = path.resolve(process.cwd(), serviceAccountPath);
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
59
|
+
firebaseCredential = cert(require(absolutePath));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log('Using Firebase default credentials');
|
|
63
|
+
firebaseCredential = applicationDefault();
|
|
64
|
+
}
|
|
65
|
+
// Check if Firebase app is already initialized
|
|
66
|
+
if (getApps().length === 0) {
|
|
67
|
+
firebaseApp = initializeApp({
|
|
68
|
+
credential: firebaseCredential,
|
|
69
|
+
projectId
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
firebaseApp = getApp();
|
|
74
|
+
}
|
|
75
|
+
console.log('✅ Firebase Admin SDK initialized successfully');
|
|
76
|
+
return firebaseApp;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
console.error('❌ Firebase initialization failed:', error);
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get Firebase Messaging instance, or null if Firebase is not initialized.
|
|
85
|
+
*/
|
|
86
|
+
export function getFirebaseMessaging() {
|
|
87
|
+
if (firebaseApp == null) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return getMessaging(firebaseApp);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get Firestore instance
|
|
94
|
+
*/
|
|
95
|
+
export function getFirebaseFirestore() {
|
|
96
|
+
if (firebaseApp == null) {
|
|
97
|
+
throw new Error('Firebase not initialized. Call initializeFirebase() first.');
|
|
98
|
+
}
|
|
99
|
+
return getFirestore(firebaseApp);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Send a push notification via FCM
|
|
103
|
+
*/
|
|
104
|
+
export async function sendNotification(fcmToken, payload) {
|
|
105
|
+
try {
|
|
106
|
+
const messaging = getFirebaseMessaging();
|
|
107
|
+
if (messaging == null) {
|
|
108
|
+
throw new Error('Firebase Messaging is not initialized (ENABLE_FIREBASE != true)');
|
|
109
|
+
}
|
|
110
|
+
const message = {
|
|
111
|
+
token: fcmToken,
|
|
112
|
+
notification: {
|
|
113
|
+
title: payload.title,
|
|
114
|
+
body: payload.body,
|
|
115
|
+
...(payload.icon && { imageUrl: payload.icon }),
|
|
116
|
+
},
|
|
117
|
+
data: payload.data || {},
|
|
118
|
+
android: {
|
|
119
|
+
priority: "high",
|
|
120
|
+
notification: {
|
|
121
|
+
clickAction: "OPEN_ACTIVITY_1",
|
|
122
|
+
...(payload.badge && { notificationCount: payload.badge }),
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
apns: {
|
|
126
|
+
headers: {
|
|
127
|
+
"apns-priority": "10",
|
|
128
|
+
},
|
|
129
|
+
payload: {
|
|
130
|
+
aps: {
|
|
131
|
+
alert: {
|
|
132
|
+
title: payload.title,
|
|
133
|
+
body: payload.body,
|
|
134
|
+
},
|
|
135
|
+
sound: "default",
|
|
136
|
+
badge: payload.badge || 1,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
const response = await messaging.send(message);
|
|
142
|
+
console.log("✅ Notification sent successfully:", response);
|
|
143
|
+
return { success: true, messageId: response };
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
console.error("❌ Failed to send notification:", error);
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=firebase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firebase.js","sourceRoot":"","sources":["../../../src/config/firebase.ts"],"names":[],"mappings":"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"}
|
package/out/src/index.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.ts
|
|
3
|
+
* @description
|
|
4
|
+
* Main entry point for the MessageBox Server.
|
|
5
|
+
*
|
|
6
|
+
* Responsibilities:
|
|
7
|
+
* - Initializes environment variables and config
|
|
8
|
+
* - Creates HTTP and WebSocket servers
|
|
9
|
+
* - Boots authentication and route handlers
|
|
10
|
+
* - Sets up database migrations after a short delay
|
|
11
|
+
* - Emits and handles real-time message events over WebSocket
|
|
12
|
+
*
|
|
13
|
+
* Exports:
|
|
14
|
+
* - `start()` for programmatic bootstrapping
|
|
15
|
+
* - `http` and `io` server instances
|
|
16
|
+
* - `HTTP_PORT` and `ROUTING_PREFIX` for external reference
|
|
17
|
+
*/
|
|
18
|
+
import * as dotenv from 'dotenv';
|
|
19
|
+
import { app, appReady, getWallet, knex } from './app.js';
|
|
20
|
+
import { createServer } from 'http';
|
|
21
|
+
import { PublicKey } from '@bsv/sdk';
|
|
22
|
+
import { Logger } from './utils/logger.js';
|
|
23
|
+
import { AuthSocketServer } from '@bsv/authsocket';
|
|
24
|
+
import * as crypto from 'crypto';
|
|
25
|
+
import { initializeFirebase } from './config/firebase.js';
|
|
26
|
+
global.self = { crypto };
|
|
27
|
+
dotenv.config();
|
|
28
|
+
// Load environment variables
|
|
29
|
+
const { NODE_ENV = 'development', PORT, SERVER_PRIVATE_KEY, ENABLE_WEBSOCKETS = 'true', ROUTING_PREFIX = '' } = process.env;
|
|
30
|
+
// if (NODE_ENV === 'development' || process.env.LOGGING_ENABLED === 'true') {
|
|
31
|
+
// Logger.enable()
|
|
32
|
+
// }
|
|
33
|
+
Logger.enable();
|
|
34
|
+
// Determine which port to listen on
|
|
35
|
+
const parsedPort = Number(PORT);
|
|
36
|
+
const parsedEnvPort = Number(process.env.HTTP_PORT);
|
|
37
|
+
const HTTP_PORT = NODE_ENV !== 'development'
|
|
38
|
+
? 3000
|
|
39
|
+
: !isNaN(parsedPort) && parsedPort > 0
|
|
40
|
+
? parsedPort
|
|
41
|
+
: !isNaN(parsedEnvPort) && parsedEnvPort > 0
|
|
42
|
+
? parsedEnvPort
|
|
43
|
+
: 8080;
|
|
44
|
+
// Ensure private key is available before proceeding
|
|
45
|
+
if (SERVER_PRIVATE_KEY === undefined || SERVER_PRIVATE_KEY === null || SERVER_PRIVATE_KEY.trim() === '') {
|
|
46
|
+
throw new Error('SERVER_PRIVATE_KEY is not defined in the environment variables.');
|
|
47
|
+
}
|
|
48
|
+
// Initialize Firebase Admin (only when ENABLE_FIREBASE=true)
|
|
49
|
+
initializeFirebase();
|
|
50
|
+
// Create HTTP server
|
|
51
|
+
/* eslint-disable @typescript-eslint/no-misused-promises */
|
|
52
|
+
const http = createServer(app);
|
|
53
|
+
// WebSocket setup (only if enabled)
|
|
54
|
+
let io = null;
|
|
55
|
+
/**
|
|
56
|
+
* @function start
|
|
57
|
+
* @description
|
|
58
|
+
* Initializes the WebSocket server with identity-key-based authentication
|
|
59
|
+
* and attaches all supported event handlers for:
|
|
60
|
+
* - `sendMessage`
|
|
61
|
+
* - `joinRoom`
|
|
62
|
+
* - `leaveRoom`
|
|
63
|
+
* - `disconnect`
|
|
64
|
+
*
|
|
65
|
+
* Only runs if `ENABLE_WEBSOCKETS` is set to `true` in the environment.
|
|
66
|
+
*
|
|
67
|
+
* @returns {Promise<void>} Resolves once WebSocket listeners are fully attached.
|
|
68
|
+
*/
|
|
69
|
+
export const start = async () => {
|
|
70
|
+
await appReady;
|
|
71
|
+
if (ENABLE_WEBSOCKETS.toLowerCase() === 'true') {
|
|
72
|
+
Logger.log('[WEBSOCKET] Initializing WebSocket support...');
|
|
73
|
+
io = new AuthSocketServer(http, {
|
|
74
|
+
wallet: await getWallet(),
|
|
75
|
+
cors: {
|
|
76
|
+
origin: '*',
|
|
77
|
+
methods: ['GET', 'POST']
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// Map to store authenticated identity keys
|
|
81
|
+
const authenticatedSockets = new Map();
|
|
82
|
+
io.on('connection', (socket) => {
|
|
83
|
+
Logger.log('[WEBSOCKET] New connection established.');
|
|
84
|
+
// Handle immediate authentication if identityKey is available
|
|
85
|
+
if (typeof socket.identityKey === 'string' && socket.identityKey.trim() !== '') {
|
|
86
|
+
try {
|
|
87
|
+
const parsedIdentityKey = PublicKey.fromString(socket.identityKey);
|
|
88
|
+
Logger.log('[DEBUG] Parsed WebSocket Identity Key Successfully:', parsedIdentityKey.toString());
|
|
89
|
+
authenticatedSockets.set(socket.id, parsedIdentityKey.toString());
|
|
90
|
+
Logger.log('[WEBSOCKET] Identity key stored for socket ID:', socket.id);
|
|
91
|
+
// Send confirmation immediately if identity key is provided on connection
|
|
92
|
+
void socket.emit('authenticationSuccess', { status: 'success' });
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
Logger.error('[ERROR] Failed to parse WebSocket identity key:', error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Wait for 'authenticated' event if identityKey was not in handshake
|
|
100
|
+
Logger.warn('[WARN] WebSocket connection received without identity key. Waiting for authentication...');
|
|
101
|
+
let identityKeyHandled = false;
|
|
102
|
+
const authListener = async (data) => {
|
|
103
|
+
if (identityKeyHandled)
|
|
104
|
+
return;
|
|
105
|
+
Logger.log('[WEBSOCKET] Received authentication data:', data);
|
|
106
|
+
if (data !== null && data !== undefined && typeof data.identityKey === 'string' && data.identityKey.trim().length > 0) {
|
|
107
|
+
try {
|
|
108
|
+
const parsedIdentityKey = PublicKey.fromString(data.identityKey);
|
|
109
|
+
Logger.log('[DEBUG] Retrieved and parsed Identity Key after connection:', parsedIdentityKey.toString());
|
|
110
|
+
authenticatedSockets.set(socket.id, parsedIdentityKey.toString());
|
|
111
|
+
Logger.log('[WEBSOCKET] Stored authenticated Identity Key for socket ID:', socket.id);
|
|
112
|
+
identityKeyHandled = true;
|
|
113
|
+
Logger.log(`New authenticated WebSocket connection from: ${authenticatedSockets.get(socket.id) ?? 'unknown'}`);
|
|
114
|
+
// Emit authentication success message
|
|
115
|
+
await socket.emit('authenticationSuccess', { status: 'success' }).catch(error => {
|
|
116
|
+
Logger.error('[WEBSOCKET ERROR] Failed to send authentication success event:', error);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
Logger.error('[ERROR] Failed to parse Identity Key from authenticated event:', error);
|
|
121
|
+
await socket.emit('authenticationFailed', { reason: 'Invalid identity key format' });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
Logger.warn('[WARN] Invalid or missing identity key in authentication event.');
|
|
126
|
+
await socket.emit('authenticationFailed', { reason: 'Missing identity key' });
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
// Ensure `authListener` is used properly
|
|
130
|
+
socket.on('authenticated', authListener);
|
|
131
|
+
}
|
|
132
|
+
// Handle sendMessage over WebSocket
|
|
133
|
+
socket.on('sendMessage', async (data) => {
|
|
134
|
+
if (typeof data !== 'object' || data == null) {
|
|
135
|
+
Logger.error('[WEBSOCKET ERROR] Invalid data object received.');
|
|
136
|
+
await socket.emit('messageFailed', { reason: 'Invalid data object' });
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const { roomId, message } = data;
|
|
140
|
+
if (!authenticatedSockets.has(socket.id)) {
|
|
141
|
+
Logger.warn('[WEBSOCKET] Unauthorized attempt to send a message.');
|
|
142
|
+
await socket.emit('paymentFailed', { reason: 'Unauthorized: WebSocket not authenticated' });
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
Logger.log(`[WEBSOCKET] Processing sendMessage for room: ${roomId}`);
|
|
146
|
+
try {
|
|
147
|
+
if (typeof roomId !== 'string' || roomId.trim() === '') {
|
|
148
|
+
Logger.error('[WEBSOCKET ERROR] Invalid roomId:', roomId);
|
|
149
|
+
await socket.emit('messageFailed', { reason: 'Invalid room ID' });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (typeof message !== 'object' || message == null) {
|
|
153
|
+
Logger.error('[WEBSOCKET ERROR] Invalid message object:', message);
|
|
154
|
+
await socket.emit('messageFailed', { reason: 'Invalid message object' });
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (typeof message.body !== 'string' || message.body.trim() === '') {
|
|
158
|
+
Logger.error('[WEBSOCKET ERROR] Invalid message body:', message.body);
|
|
159
|
+
await socket.emit('messageFailed', { reason: 'Invalid message body' });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
Logger.log(`[WEBSOCKET] Acknowledging message ${message.messageId} to sender.`);
|
|
163
|
+
const ackPayload = {
|
|
164
|
+
status: 'success',
|
|
165
|
+
messageId: message.messageId
|
|
166
|
+
};
|
|
167
|
+
Logger.log(`[WEBSOCKET] Emitting ack event: sendMessageAck-${roomId}`);
|
|
168
|
+
socket.emit(`sendMessageAck-${roomId}`, ackPayload).catch((error) => {
|
|
169
|
+
Logger.error(`[WEBSOCKET ERROR] Failed to emit sendMessageAck-${roomId}:`, error);
|
|
170
|
+
});
|
|
171
|
+
// Store message in the database just like HTTP sendMessage route
|
|
172
|
+
try {
|
|
173
|
+
const parts = roomId.split('-');
|
|
174
|
+
const messageBoxType = parts.length > 1 ? parts[1] : 'default';
|
|
175
|
+
Logger.log(`[WEBSOCKET] Parsed messageBoxType: ${messageBoxType}`);
|
|
176
|
+
Logger.log(`[WEBSOCKET] Attempting to store message for recipient: ${message.recipient}, box type: ${messageBoxType}`);
|
|
177
|
+
let messageBox = await knex('messageBox')
|
|
178
|
+
.where({ identityKey: message.recipient, type: messageBoxType })
|
|
179
|
+
.first();
|
|
180
|
+
if (messageBox === null || messageBox === undefined) {
|
|
181
|
+
Logger.log('[WEBSOCKET] messageBox not found. Creating new messageBox.');
|
|
182
|
+
await knex('messageBox').insert({
|
|
183
|
+
identityKey: message.recipient,
|
|
184
|
+
type: messageBoxType,
|
|
185
|
+
created_at: new Date(),
|
|
186
|
+
updated_at: new Date()
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
messageBox = await knex('messageBox')
|
|
190
|
+
.where({ identityKey: message.recipient, type: messageBoxType })
|
|
191
|
+
.select('messageBoxId')
|
|
192
|
+
.first();
|
|
193
|
+
const messageBoxId = messageBox?.messageBoxId ?? null;
|
|
194
|
+
if (messageBoxId === null || messageBoxId === undefined) {
|
|
195
|
+
Logger.warn('[WEBSOCKET WARNING] messageBoxId is null — message may not be stored correctly!');
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
Logger.log(`[WEBSOCKET] Resolved messageBoxId: ${String(messageBoxId)}`);
|
|
199
|
+
}
|
|
200
|
+
const senderKey = authenticatedSockets.get(socket.id) ?? null;
|
|
201
|
+
const insertResult = await knex('messages')
|
|
202
|
+
.insert({
|
|
203
|
+
messageId: message.messageId,
|
|
204
|
+
messageBoxId,
|
|
205
|
+
sender: senderKey,
|
|
206
|
+
recipient: message.recipient,
|
|
207
|
+
body: message.body,
|
|
208
|
+
created_at: new Date(),
|
|
209
|
+
updated_at: new Date()
|
|
210
|
+
})
|
|
211
|
+
.onConflict('messageId')
|
|
212
|
+
.ignore();
|
|
213
|
+
if (insertResult.length === 0) {
|
|
214
|
+
Logger.warn('[WEBSOCKET WARNING] Message insert was ignored due to conflict (duplicate messageId?)');
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
Logger.log('[WEBSOCKET] Message successfully stored in DB.');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (dbError) {
|
|
221
|
+
Logger.error('[WEBSOCKET ERROR] Failed to store message in DB:', dbError);
|
|
222
|
+
await socket.emit('messageFailed', { reason: 'Failed to store message' });
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (io != null) {
|
|
226
|
+
Logger.log(`[WEBSOCKET] Emitting message to room ${roomId}`);
|
|
227
|
+
io.emit(`sendMessage-${roomId}`, {
|
|
228
|
+
sender: authenticatedSockets.get(socket.id),
|
|
229
|
+
messageId: message.messageId,
|
|
230
|
+
body: message.body
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
Logger.error('[WEBSOCKET ERROR] io is null, cannot emit message.');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
Logger.error('[WEBSOCKET ERROR] Unexpected failure in sendMessage handler:', error);
|
|
239
|
+
await socket.emit('messageFailed', { reason: 'Unexpected error occurred' });
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
// Handle joining/leaving rooms
|
|
243
|
+
socket.on('joinRoom', async (roomId) => {
|
|
244
|
+
if (!authenticatedSockets.has(socket.id)) {
|
|
245
|
+
Logger.warn('[WEBSOCKET] Unauthorized attempt to join a room.');
|
|
246
|
+
await socket.emit('joinFailed', { reason: 'Unauthorized: WebSocket not authenticated' });
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (roomId == null || typeof roomId !== 'string' || roomId.trim() === '') {
|
|
250
|
+
Logger.error('[WEBSOCKET ERROR] Invalid roomId:', roomId);
|
|
251
|
+
await socket.emit('joinFailed', { reason: 'Invalid room ID' });
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
Logger.log(`[WEBSOCKET] User ${socket.id} joined room ${roomId}`);
|
|
255
|
+
await socket.emit('joinedRoom', { roomId });
|
|
256
|
+
});
|
|
257
|
+
socket.on('leaveRoom', async (roomId) => {
|
|
258
|
+
if (!authenticatedSockets.has(socket.id)) {
|
|
259
|
+
Logger.warn('[WEBSOCKET] Unauthorized attempt to leave a room.');
|
|
260
|
+
await socket.emit('leaveFailed', { reason: 'Unauthorized: WebSocket not authenticated' });
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (roomId == null || roomId === '' || typeof roomId !== 'string' || roomId.trim() === '') {
|
|
264
|
+
Logger.error('[WEBSOCKET ERROR] Invalid roomId:', roomId);
|
|
265
|
+
await socket.emit('leaveFailed', { reason: 'Invalid room ID' });
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
Logger.log(`[WEBSOCKET] User ${socket.id} left room ${roomId}`);
|
|
269
|
+
await socket.emit('leftRoom', { roomId });
|
|
270
|
+
});
|
|
271
|
+
// Clean up on disconnect
|
|
272
|
+
socket.on('disconnect', (reason) => {
|
|
273
|
+
Logger.log(`[WEBSOCKET] Disconnected: ${reason}`);
|
|
274
|
+
authenticatedSockets.delete(socket.id);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
// Export for testing and CLI use
|
|
280
|
+
export { io, http, HTTP_PORT, ROUTING_PREFIX };
|
|
281
|
+
// Only run server if not in test mode
|
|
282
|
+
if (NODE_ENV !== 'test') {
|
|
283
|
+
http.listen(HTTP_PORT, () => {
|
|
284
|
+
Logger.log('MessageBox listening on port', HTTP_PORT);
|
|
285
|
+
(async () => {
|
|
286
|
+
await knex.migrate.latest();
|
|
287
|
+
})().catch((error) => {
|
|
288
|
+
Logger.error('[STARTUP ERROR]', error);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
start().catch(error => {
|
|
292
|
+
Logger.error('[SERVER INIT ERROR]', error);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export async function up(knex) {
|
|
2
|
+
await knex.schema.createTable('messageBox', (table) => {
|
|
3
|
+
table.increments('messageBoxId').primary();
|
|
4
|
+
table.timestamps(true, true);
|
|
5
|
+
table.string('type').notNullable(); // What type of messages go in here?
|
|
6
|
+
table.string('identityKey').notNullable(); // Who's message box is this?
|
|
7
|
+
table.unique(['type', 'identityKey']);
|
|
8
|
+
});
|
|
9
|
+
await knex.schema.createTable('messages', (table) => {
|
|
10
|
+
table.increments('messageId').primary();
|
|
11
|
+
table.timestamps(true, true);
|
|
12
|
+
table
|
|
13
|
+
.integer('messageBoxId')
|
|
14
|
+
.unsigned()
|
|
15
|
+
.references('messageBoxId')
|
|
16
|
+
.inTable('messageBox')
|
|
17
|
+
.onDelete('CASCADE'); // All messages get deleted if the messageBox is deleted
|
|
18
|
+
table.string('sender').notNullable();
|
|
19
|
+
table.string('recipient').notNullable();
|
|
20
|
+
table.text('body', 'longtext').notNullable(); // Contents of the message
|
|
21
|
+
table.boolean('acknowledged').defaultTo(false);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export async function down(knex) {
|
|
25
|
+
await knex.schema.dropTableIfExists('messages');
|
|
26
|
+
await knex.schema.dropTableIfExists('messageBox');
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=2022-12-28-001-initial-migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"2022-12-28-001-initial-migration.js","sourceRoot":"","sources":["../../../src/migrations/2022-12-28-001-initial-migration.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAE,IAAU;IAClC,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE;QACpD,KAAK,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE,CAAA;QAC1C,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC5B,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAA,CAAC,oCAAoC;QACvE,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,CAAA,CAAC,6BAA6B;QACvE,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;IAEF,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE;QAClD,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAA;QACvC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC5B,KAAK;aACF,OAAO,CAAC,cAAc,CAAC;aACvB,QAAQ,EAAE;aACV,UAAU,CAAC,cAAc,CAAC;aAC1B,OAAO,CAAC,YAAY,CAAC;aACrB,QAAQ,CAAC,SAAS,CAAC,CAAA,CAAC,wDAAwD;QAC/E,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAA;QACpC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAA;QACvC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,WAAW,EAAE,CAAA,CAAC,0BAA0B;QACvE,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAChD,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAE,IAAU;IACpC,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAA;IAC/C,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAA;AACnD,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export async function up(knex) {
|
|
2
|
+
await knex.schema.alterTable('messages', (table) => {
|
|
3
|
+
table.dropColumn('acknowledged');
|
|
4
|
+
});
|
|
5
|
+
}
|
|
6
|
+
export async function down(knex) {
|
|
7
|
+
await knex.schema.alterTable('messages', (table) => {
|
|
8
|
+
table.boolean('acknowledged').defaultTo(false);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=2023-01-17-messages-update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"2023-01-17-messages-update.js","sourceRoot":"","sources":["../../../src/migrations/2023-01-17-messages-update.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAE,IAAU;IAClC,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE;QACjD,KAAK,CAAC,UAAU,CAAC,cAAc,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAE,IAAU;IACpC,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE;QACjD,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAChD,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export async function up(knex) {
|
|
2
|
+
// Drop the existing 'messageId' column
|
|
3
|
+
await knex.schema.alterTable('messages', (table) => {
|
|
4
|
+
table.dropColumn('messageId');
|
|
5
|
+
});
|
|
6
|
+
// Add 'messageId' back as a unique string
|
|
7
|
+
await knex.schema.alterTable('messages', (table) => {
|
|
8
|
+
table.string('messageId').unique().notNullable();
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export async function down(knex) {
|
|
12
|
+
// Drop the new 'messageId' column
|
|
13
|
+
await knex.schema.alterTable('messages', (table) => {
|
|
14
|
+
table.dropColumn('messageId');
|
|
15
|
+
});
|
|
16
|
+
// Restore 'messageId' as an auto-incrementing primary key
|
|
17
|
+
await knex.schema.alterTable('messages', (table) => {
|
|
18
|
+
table.increments('messageId').primary();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=2024-03-05-001-messageID-upgrade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"2024-03-05-001-messageID-upgrade.js","sourceRoot":"","sources":["../../../src/migrations/2024-03-05-001-messageID-upgrade.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAE,IAAU;IAClC,uCAAuC;IACvC,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE;QACjD,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,0CAA0C;IAC1C,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE;QACjD,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAA;IAClD,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAE,IAAU;IACpC,kCAAkC;IAClC,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE;QACjD,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,0DAA0D;IAC1D,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE;QACjD,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAA;IACzC,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export async function up(knex) {
|
|
2
|
+
// Create generalized message permissions table for all box/sender combinations
|
|
3
|
+
await knex.schema.createTable('message_permissions', (table) => {
|
|
4
|
+
table.increments('id').primary();
|
|
5
|
+
table.timestamp('created_at').defaultTo(knex.fn.now());
|
|
6
|
+
table.timestamp('updated_at').defaultTo(knex.fn.now());
|
|
7
|
+
table.string('recipient', 255).notNullable(); // identityKey of who this permission belongs to
|
|
8
|
+
table.string('sender', 255).nullable(); // identityKey of sender (NULL for box-wide defaults)
|
|
9
|
+
table.string('message_box', 255).notNullable(); // messageBox type (e.g., 'notifications', 'inbox', etc.)
|
|
10
|
+
table.integer('recipient_fee').notNullable(); // -1 = block all, 0 = always allow, >0 = satoshi amount required
|
|
11
|
+
table.unique(['recipient', 'sender', 'message_box']);
|
|
12
|
+
table.index('recipient');
|
|
13
|
+
table.index(['recipient', 'message_box']);
|
|
14
|
+
table.index('message_box');
|
|
15
|
+
table.index('sender');
|
|
16
|
+
});
|
|
17
|
+
// Create server fee configuration table
|
|
18
|
+
await knex.schema.createTable('server_fees', (table) => {
|
|
19
|
+
table.increments('id').primary();
|
|
20
|
+
table.timestamp('created_at').defaultTo(knex.fn.now());
|
|
21
|
+
table.timestamp('updated_at').defaultTo(knex.fn.now());
|
|
22
|
+
table.string('message_box').notNullable(); // messageBox type
|
|
23
|
+
table.integer('delivery_fee').notNullable(); // Server delivery fee in satoshis
|
|
24
|
+
table.unique(['message_box']); // Each box type can only have one server fee setting
|
|
25
|
+
});
|
|
26
|
+
// Insert default server fees for different message box types
|
|
27
|
+
const existingFees = await knex('server_fees').select('message_box');
|
|
28
|
+
const existingMessageBoxes = existingFees.map(fee => fee.message_box);
|
|
29
|
+
const defaultFees = [
|
|
30
|
+
{
|
|
31
|
+
message_box: 'notifications',
|
|
32
|
+
delivery_fee: 10, // Higher fee for FCM delivery
|
|
33
|
+
created_at: new Date(),
|
|
34
|
+
updated_at: new Date()
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
message_box: 'inbox',
|
|
38
|
+
delivery_fee: 0, // Free for regular inbox
|
|
39
|
+
created_at: new Date(),
|
|
40
|
+
updated_at: new Date()
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
message_box: 'payment_inbox',
|
|
44
|
+
delivery_fee: 0, // Free for payment inbox
|
|
45
|
+
created_at: new Date(),
|
|
46
|
+
updated_at: new Date()
|
|
47
|
+
}
|
|
48
|
+
];
|
|
49
|
+
// Only insert fees for message boxes that don't already exist
|
|
50
|
+
const feesToInsert = defaultFees.filter(fee => !existingMessageBoxes.includes(fee.message_box));
|
|
51
|
+
if (feesToInsert.length > 0) {
|
|
52
|
+
await knex('server_fees').insert(feesToInsert);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function down(knex) {
|
|
56
|
+
await knex.schema.dropTableIfExists('server_fees');
|
|
57
|
+
await knex.schema.dropTableIfExists('message_permissions');
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=2025-01-31-001-notification-permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"2025-01-31-001-notification-permissions.js","sourceRoot":"","sources":["../../../src/migrations/2025-01-31-001-notification-permissions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export async function up(knex) {
|
|
2
|
+
// Create device registrations table for FCM push notifications
|
|
3
|
+
await knex.schema.createTable('device_registrations', (table) => {
|
|
4
|
+
table.increments('id').primary();
|
|
5
|
+
table.timestamp('created_at').defaultTo(knex.fn.now());
|
|
6
|
+
table.timestamp('updated_at').defaultTo(knex.fn.now());
|
|
7
|
+
table.string('identity_key', 255).notNullable(); // User's identity key
|
|
8
|
+
table.string('fcm_token', 500).notNullable(); // Firebase Cloud Messaging token (can be long)
|
|
9
|
+
table.string('device_id', 255).nullable(); // Optional device identifier
|
|
10
|
+
table.string('platform', 50).nullable(); // 'ios', 'android', 'web', etc.
|
|
11
|
+
table.timestamp('last_used').nullable(); // When this token was last successfully used
|
|
12
|
+
table.boolean('active').defaultTo(true); // Whether this token is still active
|
|
13
|
+
// Unique constraint to prevent duplicate token registrations
|
|
14
|
+
table.unique(['fcm_token']);
|
|
15
|
+
// Index for efficient lookups by identity key
|
|
16
|
+
table.index('identity_key');
|
|
17
|
+
table.index(['identity_key', 'active']);
|
|
18
|
+
table.index('last_used');
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export async function down(knex) {
|
|
22
|
+
await knex.schema.dropTableIfExists('device_registrations');
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=2025-01-31-002-device-registrations.js.map
|