@joystick.js/db-canary 0.0.0-canary.2251 → 0.0.0-canary.2252

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.
Files changed (97) hide show
  1. package/dist/client/database.js +1 -1
  2. package/dist/client/index.js +1 -1
  3. package/dist/server/cluster/master.js +4 -4
  4. package/dist/server/cluster/worker.js +1 -1
  5. package/dist/server/index.js +1 -1
  6. package/dist/server/lib/auto_index_manager.js +1 -1
  7. package/dist/server/lib/backup_manager.js +1 -1
  8. package/dist/server/lib/index_manager.js +1 -1
  9. package/dist/server/lib/operation_dispatcher.js +1 -1
  10. package/dist/server/lib/operations/admin.js +1 -1
  11. package/dist/server/lib/operations/bulk_write.js +1 -1
  12. package/dist/server/lib/operations/create_index.js +1 -1
  13. package/dist/server/lib/operations/delete_many.js +1 -1
  14. package/dist/server/lib/operations/delete_one.js +1 -1
  15. package/dist/server/lib/operations/find.js +1 -1
  16. package/dist/server/lib/operations/find_one.js +1 -1
  17. package/dist/server/lib/operations/insert_one.js +1 -1
  18. package/dist/server/lib/operations/update_one.js +1 -1
  19. package/dist/server/lib/send_response.js +1 -1
  20. package/dist/server/lib/tcp_protocol.js +1 -1
  21. package/package.json +2 -2
  22. package/src/client/database.js +92 -119
  23. package/src/client/index.js +279 -345
  24. package/src/server/cluster/master.js +265 -156
  25. package/src/server/cluster/worker.js +26 -18
  26. package/src/server/index.js +553 -330
  27. package/src/server/lib/auto_index_manager.js +85 -23
  28. package/src/server/lib/backup_manager.js +117 -70
  29. package/src/server/lib/index_manager.js +63 -25
  30. package/src/server/lib/operation_dispatcher.js +339 -168
  31. package/src/server/lib/operations/admin.js +343 -205
  32. package/src/server/lib/operations/bulk_write.js +458 -194
  33. package/src/server/lib/operations/create_index.js +127 -34
  34. package/src/server/lib/operations/delete_many.js +204 -67
  35. package/src/server/lib/operations/delete_one.js +164 -52
  36. package/src/server/lib/operations/find.js +552 -319
  37. package/src/server/lib/operations/find_one.js +530 -304
  38. package/src/server/lib/operations/insert_one.js +147 -52
  39. package/src/server/lib/operations/update_one.js +334 -93
  40. package/src/server/lib/send_response.js +37 -17
  41. package/src/server/lib/tcp_protocol.js +158 -53
  42. package/test_data_api_key_1758233848259_cglfjzhou/data.mdb +0 -0
  43. package/test_data_api_key_1758233848259_cglfjzhou/lock.mdb +0 -0
  44. package/test_data_api_key_1758233848502_urlje2utd/data.mdb +0 -0
  45. package/test_data_api_key_1758233848502_urlje2utd/lock.mdb +0 -0
  46. package/test_data_api_key_1758233848738_mtcpfe5ns/data.mdb +0 -0
  47. package/test_data_api_key_1758233848738_mtcpfe5ns/lock.mdb +0 -0
  48. package/test_data_api_key_1758233848856_9g97p6gag/data.mdb +0 -0
  49. package/test_data_api_key_1758233848856_9g97p6gag/lock.mdb +0 -0
  50. package/test_data_api_key_1758233857008_0tl9zzhj8/data.mdb +0 -0
  51. package/test_data_api_key_1758233857008_0tl9zzhj8/lock.mdb +0 -0
  52. package/test_data_api_key_1758233857120_60c2f2uhu/data.mdb +0 -0
  53. package/test_data_api_key_1758233857120_60c2f2uhu/lock.mdb +0 -0
  54. package/test_data_api_key_1758233857232_aw7fkqgd9/data.mdb +0 -0
  55. package/test_data_api_key_1758233857232_aw7fkqgd9/lock.mdb +0 -0
  56. package/test_data_api_key_1758234881285_4aeflubjb/data.mdb +0 -0
  57. package/test_data_api_key_1758234881285_4aeflubjb/lock.mdb +0 -0
  58. package/test_data_api_key_1758234881520_kb0amvtqb/data.mdb +0 -0
  59. package/test_data_api_key_1758234881520_kb0amvtqb/lock.mdb +0 -0
  60. package/test_data_api_key_1758234881756_k04gfv2va/data.mdb +0 -0
  61. package/test_data_api_key_1758234881756_k04gfv2va/lock.mdb +0 -0
  62. package/test_data_api_key_1758234881876_wn90dpo1z/data.mdb +0 -0
  63. package/test_data_api_key_1758234881876_wn90dpo1z/lock.mdb +0 -0
  64. package/test_data_api_key_1758234889461_26xz3dmbr/data.mdb +0 -0
  65. package/test_data_api_key_1758234889461_26xz3dmbr/lock.mdb +0 -0
  66. package/test_data_api_key_1758234889572_uziz7e0p5/data.mdb +0 -0
  67. package/test_data_api_key_1758234889572_uziz7e0p5/lock.mdb +0 -0
  68. package/test_data_api_key_1758234889684_5f9wmposh/data.mdb +0 -0
  69. package/test_data_api_key_1758234889684_5f9wmposh/lock.mdb +0 -0
  70. package/test_data_api_key_1758235657729_prwgm6mxr/data.mdb +0 -0
  71. package/test_data_api_key_1758235657729_prwgm6mxr/lock.mdb +0 -0
  72. package/test_data_api_key_1758235657961_rc2da0dc2/data.mdb +0 -0
  73. package/test_data_api_key_1758235657961_rc2da0dc2/lock.mdb +0 -0
  74. package/test_data_api_key_1758235658193_oqqxm0sny/data.mdb +0 -0
  75. package/test_data_api_key_1758235658193_oqqxm0sny/lock.mdb +0 -0
  76. package/test_data_api_key_1758235658309_vggac1pj6/data.mdb +0 -0
  77. package/test_data_api_key_1758235658309_vggac1pj6/lock.mdb +0 -0
  78. package/test_data_api_key_1758235665968_61ko07dd1/data.mdb +0 -0
  79. package/test_data_api_key_1758235665968_61ko07dd1/lock.mdb +0 -0
  80. package/test_data_api_key_1758235666082_50lrt6sq8/data.mdb +0 -0
  81. package/test_data_api_key_1758235666082_50lrt6sq8/lock.mdb +0 -0
  82. package/test_data_api_key_1758235666194_ykvauwlzh/data.mdb +0 -0
  83. package/test_data_api_key_1758235666194_ykvauwlzh/lock.mdb +0 -0
  84. package/test_data_api_key_1758236187207_9c4paeh09/data.mdb +0 -0
  85. package/test_data_api_key_1758236187207_9c4paeh09/lock.mdb +0 -0
  86. package/test_data_api_key_1758236187441_4n3o3gkkl/data.mdb +0 -0
  87. package/test_data_api_key_1758236187441_4n3o3gkkl/lock.mdb +0 -0
  88. package/test_data_api_key_1758236187672_jt6b21ye0/data.mdb +0 -0
  89. package/test_data_api_key_1758236187672_jt6b21ye0/lock.mdb +0 -0
  90. package/test_data_api_key_1758236187788_oo84fz9u6/data.mdb +0 -0
  91. package/test_data_api_key_1758236187788_oo84fz9u6/lock.mdb +0 -0
  92. package/test_data_api_key_1758236195507_o9zeznwlm/data.mdb +0 -0
  93. package/test_data_api_key_1758236195507_o9zeznwlm/lock.mdb +0 -0
  94. package/test_data_api_key_1758236195619_qsqd60y41/data.mdb +0 -0
  95. package/test_data_api_key_1758236195619_qsqd60y41/lock.mdb +0 -0
  96. package/test_data_api_key_1758236195731_im13iq284/data.mdb +0 -0
  97. package/test_data_api_key_1758236195731_im13iq284/lock.mdb +0 -0
@@ -26,6 +26,35 @@ import admin_operation from './operations/admin.js';
26
26
 
27
27
  const { create_context_logger } = create_logger('operation_dispatcher');
28
28
 
29
+ /**
30
+ * Checks if database name exceeds length limit.
31
+ * @param {string} database_name - Database name to check
32
+ * @returns {boolean} True if name exceeds limit
33
+ */
34
+ const exceeds_length_limit = (database_name) => {
35
+ return database_name.length > 64;
36
+ };
37
+
38
+ /**
39
+ * Checks if database name is reserved.
40
+ * @param {string} database_name - Database name to check
41
+ * @returns {boolean} True if name is reserved
42
+ */
43
+ const is_reserved_database_name = (database_name) => {
44
+ const reserved_names = ['admin', 'config', 'local'];
45
+ return reserved_names.includes(database_name.toLowerCase());
46
+ };
47
+
48
+ /**
49
+ * Checks if database name follows naming convention.
50
+ * @param {string} database_name - Database name to check
51
+ * @returns {boolean} True if name follows convention
52
+ */
53
+ const follows_naming_convention = (database_name) => {
54
+ const valid_name_pattern = /^[a-zA-Z0-9_-]+$/;
55
+ return valid_name_pattern.test(database_name);
56
+ };
57
+
29
58
  /**
30
59
  * Validates database name according to naming rules.
31
60
  * @param {string} database_name - Database name to validate
@@ -36,20 +65,268 @@ const validate_database_name = (database_name) => {
36
65
  return false;
37
66
  }
38
67
 
39
- // NOTE: Check length limit.
40
- if (database_name.length > 64) {
68
+ if (exceeds_length_limit(database_name)) {
41
69
  return false;
42
70
  }
43
71
 
44
- // NOTE: Check for reserved names.
45
- const reserved_names = ['admin', 'config', 'local'];
46
- if (reserved_names.includes(database_name.toLowerCase())) {
72
+ if (is_reserved_database_name(database_name)) {
47
73
  return false;
48
74
  }
49
75
 
50
- // NOTE: Check naming convention (alphanumeric, underscores, hyphens).
51
- const valid_name_pattern = /^[a-zA-Z0-9_-]+$/;
52
- return valid_name_pattern.test(database_name);
76
+ return follows_naming_convention(database_name);
77
+ };
78
+
79
+ /**
80
+ * Creates authentication error response.
81
+ * @returns {Object} Authentication error response
82
+ */
83
+ const create_authentication_error_response = () => ({
84
+ ok: 0,
85
+ error: 'Authentication required'
86
+ });
87
+
88
+ /**
89
+ * Creates database name validation error response.
90
+ * @returns {Object} Database validation error response
91
+ */
92
+ const create_database_validation_error_response = () => ({
93
+ ok: 0,
94
+ error: 'Invalid database name. Database names must be alphanumeric with underscores/hyphens, max 64 characters, and cannot be reserved names (admin, config, local).'
95
+ });
96
+
97
+ /**
98
+ * Sends encoded response to socket.
99
+ * @param {net.Socket} socket - Client socket
100
+ * @param {Object} response - Response object
101
+ */
102
+ const send_encoded_response = (socket, response) => {
103
+ const encoded_response = encode_message(response);
104
+ socket.write(encoded_response);
105
+ };
106
+
107
+ /**
108
+ * Logs performance metrics for operation.
109
+ * @param {string} socket_id - Socket identifier
110
+ * @param {string} op_type - Operation type
111
+ * @param {string} collection - Collection name
112
+ * @param {number} duration_ms - Operation duration
113
+ * @param {string} status - Operation status
114
+ * @param {string} error_message - Error message if failed
115
+ * @param {number} request_size - Request size in bytes
116
+ * @param {number} response_size - Response size in bytes
117
+ */
118
+ const log_operation_performance = (socket_id, op_type, collection, duration_ms, status, error_message, request_size, response_size) => {
119
+ performance_monitor.log_structured_operation(
120
+ socket_id,
121
+ op_type,
122
+ collection,
123
+ duration_ms,
124
+ status,
125
+ error_message,
126
+ request_size,
127
+ response_size
128
+ );
129
+ };
130
+
131
+ /**
132
+ * Logs operation completion details.
133
+ * @param {Function} log - Logger function
134
+ * @param {string} socket_id - Socket identifier
135
+ * @param {string} op_type - Operation type
136
+ * @param {string} collection - Collection name
137
+ * @param {number} duration_ms - Operation duration
138
+ * @param {string} status - Operation status
139
+ * @param {number} request_size - Request size in bytes
140
+ * @param {number} response_size - Response size in bytes
141
+ * @param {string} error_message - Error message if failed
142
+ */
143
+ const log_operation_details = (log, socket_id, op_type, collection, duration_ms, status, request_size, response_size, error_message = null) => {
144
+ const log_data = {
145
+ client_id: socket_id,
146
+ op: op_type,
147
+ collection,
148
+ duration_ms,
149
+ status,
150
+ request_size
151
+ };
152
+
153
+ if (status === 'success') {
154
+ log_data.response_size = response_size;
155
+ log.info('Database operation completed', log_data);
156
+ } else {
157
+ log_data.error = error_message;
158
+ log.error('Database operation failed', log_data);
159
+ }
160
+ };
161
+
162
+ /**
163
+ * Executes database operation based on type.
164
+ * @param {string} op_type - Operation type
165
+ * @param {string} database_name - Database name
166
+ * @param {Object} data - Operation data
167
+ * @returns {Promise<Object>} Operation result
168
+ */
169
+ const execute_database_operation = async (op_type, database_name, data) => {
170
+ switch (op_type) {
171
+ case 'insert_one':
172
+ return await insert_one(database_name, data.collection, data.document, data.options);
173
+
174
+ case 'update_one':
175
+ return await update_one(database_name, data.collection, data.filter, data.update, data.options);
176
+
177
+ case 'delete_one':
178
+ return await delete_one(database_name, data.collection, data.filter, data.options);
179
+
180
+ case 'delete_many':
181
+ return await delete_many(database_name, data.collection, data.filter, data.options);
182
+
183
+ case 'bulk_write':
184
+ return await bulk_write(database_name, data.collection, data.operations, data.options);
185
+
186
+ case 'find_one':
187
+ return await find_one(database_name, data.collection, data.filter, data.options);
188
+
189
+ case 'find':
190
+ return await find(database_name, data.collection, data.filter, data.options);
191
+
192
+ case 'create_index':
193
+ return await create_index_operation(database_name, data.collection, data.field, data.options);
194
+
195
+ case 'drop_index':
196
+ return await drop_index_operation(database_name, data.collection, data.field);
197
+
198
+ case 'get_indexes':
199
+ return await get_indexes_operation(database_name, data.collection);
200
+
201
+ default:
202
+ throw new Error(`Unsupported operation: ${op_type}`);
203
+ }
204
+ };
205
+
206
+ /**
207
+ * Formats operation result into response object.
208
+ * @param {string} op_type - Operation type
209
+ * @param {Object} result - Operation result
210
+ * @returns {Object} Formatted response
211
+ */
212
+ const format_operation_response = (op_type, result) => {
213
+ if (op_type === 'find_one') {
214
+ return { ok: 1, document: result };
215
+ } else if (op_type === 'find') {
216
+ return { ok: 1, documents: result };
217
+ } else {
218
+ return { ok: 1, ...result };
219
+ }
220
+ };
221
+
222
+ /**
223
+ * Determines if operation is a write operation.
224
+ * @param {string} op_type - Operation type
225
+ * @returns {boolean} True if write operation
226
+ */
227
+ const is_write_operation = (op_type) => {
228
+ const read_operations = ['find', 'find_one', 'get_indexes'];
229
+ return !read_operations.includes(op_type);
230
+ };
231
+
232
+ /**
233
+ * Queues operation for replication if it's a write operation.
234
+ * @param {string} op_type - Operation type
235
+ * @param {Object} data - Operation data
236
+ */
237
+ const queue_replication_if_write_operation = (op_type, data) => {
238
+ if (!is_write_operation(op_type)) {
239
+ return;
240
+ }
241
+
242
+ const replication_manager = get_replication_manager();
243
+ replication_manager.queue_replication(op_type, data.collection, data);
244
+
245
+ setImmediate(() => check_and_grow_map_size());
246
+ };
247
+
248
+ /**
249
+ * Handles successful database operation.
250
+ * @param {net.Socket} socket - Client socket
251
+ * @param {string} op_type - Operation type
252
+ * @param {Object} data - Operation data
253
+ * @param {Object} result - Operation result
254
+ * @param {number} start_time - Operation start time
255
+ * @param {number} raw_data_size - Request size in bytes
256
+ */
257
+ const handle_successful_operation = (socket, op_type, data, result, start_time, raw_data_size) => {
258
+ const log = create_context_logger();
259
+ const duration_ms = Date.now() - start_time;
260
+
261
+ const response = format_operation_response(op_type, result);
262
+ const encoded_response = encode_message(response);
263
+ const response_size = encoded_response.length;
264
+
265
+ send_encoded_response(socket, response);
266
+
267
+ log_operation_performance(
268
+ socket.id,
269
+ op_type,
270
+ data.collection,
271
+ duration_ms,
272
+ 'success',
273
+ null,
274
+ raw_data_size,
275
+ response_size
276
+ );
277
+
278
+ log_operation_details(
279
+ log,
280
+ socket.id,
281
+ op_type,
282
+ data.collection,
283
+ duration_ms,
284
+ 'success',
285
+ raw_data_size,
286
+ response_size
287
+ );
288
+
289
+ queue_replication_if_write_operation(op_type, data);
290
+ };
291
+
292
+ /**
293
+ * Handles failed database operation.
294
+ * @param {net.Socket} socket - Client socket
295
+ * @param {string} op_type - Operation type
296
+ * @param {Object} data - Operation data
297
+ * @param {Error} error - Operation error
298
+ * @param {number} start_time - Operation start time
299
+ * @param {number} raw_data_size - Request size in bytes
300
+ */
301
+ const handle_failed_operation = (socket, op_type, data, error, start_time, raw_data_size) => {
302
+ const log = create_context_logger();
303
+ const duration_ms = Date.now() - start_time;
304
+
305
+ log_operation_performance(
306
+ socket.id,
307
+ op_type,
308
+ data.collection,
309
+ duration_ms,
310
+ 'error',
311
+ error.message,
312
+ raw_data_size,
313
+ 0
314
+ );
315
+
316
+ log_operation_details(
317
+ log,
318
+ socket.id,
319
+ op_type,
320
+ data.collection,
321
+ duration_ms,
322
+ 'error',
323
+ raw_data_size,
324
+ 0,
325
+ error.message
326
+ );
327
+
328
+ const error_response = { ok: 0, error: error.message };
329
+ send_encoded_response(socket, error_response);
53
330
  };
54
331
 
55
332
  /**
@@ -58,181 +335,89 @@ const validate_database_name = (database_name) => {
58
335
  * @param {string} op_type - Type of database operation
59
336
  * @param {Object} data - Operation data
60
337
  * @param {Function} check_authentication - Function to check if socket is authenticated
61
- * @param {number} [raw_data_size=0] - Size of raw request data for monitoring
62
- * @param {Object} [connection_manager] - Connection manager instance (optional)
63
- * @param {Set} [authenticated_clients] - Set of authenticated client IDs (optional)
338
+ * @param {number} raw_data_size - Size of raw request data for monitoring
339
+ * @param {Object} connection_manager - Connection manager instance (optional)
340
+ * @param {Set} authenticated_clients - Set of authenticated client IDs (optional)
64
341
  */
65
342
  export const handle_database_operation = async (socket, op_type, data, check_authentication, raw_data_size = 0, connection_manager = null, authenticated_clients = null) => {
66
- const log = create_context_logger();
67
343
  const start_time = Date.now();
68
344
 
69
345
  if (!check_authentication(socket)) {
70
- const response = { ok: 0, error: 'Authentication required' };
71
- const encoded_response = encode_message(response);
72
- socket.write(encoded_response);
73
- performance_monitor.log_structured_operation(socket.id, op_type, null, 0, 'error', 'Authentication required', raw_data_size, 0);
346
+ const response = create_authentication_error_response();
347
+ send_encoded_response(socket, response);
348
+ log_operation_performance(socket.id, op_type, null, 0, 'error', 'Authentication required', raw_data_size, 0);
74
349
  return;
75
350
  }
76
351
 
77
- // NOTE: Extract and validate database name from request.
78
352
  const database_name = data.database || 'default';
79
353
 
80
354
  if (!validate_database_name(database_name)) {
81
- const response = { ok: 0, error: 'Invalid database name. Database names must be alphanumeric with underscores/hyphens, max 64 characters, and cannot be reserved names (admin, config, local).' };
82
- const encoded_response = encode_message(response);
83
- socket.write(encoded_response);
84
- performance_monitor.log_structured_operation(socket.id, op_type, data.collection, 0, 'error', 'Invalid database name', raw_data_size, 0);
355
+ const response = create_database_validation_error_response();
356
+ send_encoded_response(socket, response);
357
+ log_operation_performance(socket.id, op_type, data.collection, 0, 'error', 'Invalid database name', raw_data_size, 0);
85
358
  return;
86
359
  }
87
360
 
88
- // NOTE: Check if this is a secondary node and should forward write operations.
89
361
  const write_forwarder = get_write_forwarder();
90
362
  const forwarded = await write_forwarder.forward_operation(socket, op_type, data);
91
363
 
92
364
  if (forwarded) {
93
- // Operation was forwarded to primary, response will be handled by forwarder
94
365
  return;
95
366
  }
96
367
 
97
368
  try {
98
- let result;
99
-
100
- switch (op_type) {
101
- case 'insert_one':
102
- result = await insert_one(database_name, data.collection, data.document, data.options);
103
- break;
104
-
105
- case 'update_one':
106
- result = await update_one(database_name, data.collection, data.filter, data.update, data.options);
107
- break;
108
-
109
- case 'delete_one':
110
- result = await delete_one(database_name, data.collection, data.filter, data.options);
111
- break;
112
-
113
- case 'delete_many':
114
- result = await delete_many(database_name, data.collection, data.filter, data.options);
115
- break;
116
-
117
- case 'bulk_write':
118
- result = await bulk_write(database_name, data.collection, data.operations, data.options);
119
- break;
120
-
121
- case 'find_one':
122
- result = await find_one(database_name, data.collection, data.filter, data.options);
123
- break;
124
-
125
- case 'find':
126
- result = await find(database_name, data.collection, data.filter, data.options);
127
- break;
128
-
129
- case 'create_index':
130
- result = await create_index_operation(database_name, data.collection, data.field, data.options);
131
- break;
132
-
133
- case 'drop_index':
134
- result = await drop_index_operation(database_name, data.collection, data.field);
135
- break;
136
-
137
- case 'get_indexes':
138
- result = await get_indexes_operation(database_name, data.collection);
139
- break;
140
-
141
- default:
142
- throw new Error(`Unsupported operation: ${op_type}`);
143
- }
144
-
145
- const duration_ms = Date.now() - start_time;
146
-
147
- // NOTE: All operations now return structured responses for consistency.
148
- let response;
149
- if (op_type === 'find_one') {
150
- response = { ok: 1, document: result };
151
- } else if (op_type === 'find') {
152
- response = { ok: 1, documents: result };
153
- } else {
154
- response = { ok: 1, ...result };
155
- }
156
-
157
- const encoded_response = encode_message(response);
158
- const response_size = encoded_response.length;
159
-
160
- socket.write(encoded_response);
161
-
162
- // NOTE: Log structured operation with performance monitoring.
163
- performance_monitor.log_structured_operation(
164
- socket.id,
165
- op_type,
166
- data.collection,
167
- duration_ms,
168
- 'success',
169
- null,
170
- raw_data_size,
171
- response_size
172
- );
173
-
174
- log.info('Database operation completed', {
175
- client_id: socket.id,
176
- op: op_type,
177
- collection: data.collection,
178
- duration_ms,
179
- status: 'success',
180
- request_size: raw_data_size,
181
- response_size
182
- });
183
-
184
- // NOTE: Queue write operations for replication to secondary nodes.
185
- if (op_type !== 'find' && op_type !== 'find_one' && op_type !== 'get_indexes') {
186
- const replication_manager = get_replication_manager();
187
- replication_manager.queue_replication(op_type, data.collection, data);
188
-
189
- setImmediate(() => check_and_grow_map_size());
190
- }
369
+ const result = await execute_database_operation(op_type, database_name, data);
370
+ handle_successful_operation(socket, op_type, data, result, start_time, raw_data_size);
191
371
  } catch (error) {
192
- const duration_ms = Date.now() - start_time;
193
-
194
- // NOTE: Log structured operation with error.
195
- performance_monitor.log_structured_operation(
196
- socket.id,
197
- op_type,
198
- data.collection,
199
- duration_ms,
200
- 'error',
201
- error.message,
202
- raw_data_size,
203
- 0
204
- );
205
-
206
- log.error('Database operation failed', {
207
- client_id: socket.id,
208
- op: op_type,
209
- collection: data.collection,
210
- duration_ms,
211
- status: 'error',
212
- error: error.message,
213
- request_size: raw_data_size
214
- });
215
-
216
- const error_response = { ok: 0, error: error.message };
217
- const encoded_error_response = encode_message(error_response);
218
- socket.write(encoded_error_response);
372
+ handle_failed_operation(socket, op_type, data, error, start_time, raw_data_size);
373
+ }
374
+ };
375
+
376
+ /**
377
+ * Creates admin authentication error response.
378
+ * @returns {Object} Admin authentication error response
379
+ */
380
+ const create_admin_authentication_error_response = () => ({
381
+ ok: false,
382
+ error: 'Authentication required'
383
+ });
384
+
385
+ /**
386
+ * Creates admin operation response.
387
+ * @param {string} admin_action - Admin action type
388
+ * @param {Object} result - Operation result
389
+ * @returns {Object} Admin response object
390
+ */
391
+ const create_admin_operation_response = (admin_action, result) => {
392
+ if (!admin_action) {
393
+ return { ok: true, ...result };
394
+ } else {
395
+ return { ok: 1, ...result };
219
396
  }
220
397
  };
221
398
 
399
+ /**
400
+ * Creates admin operation error response.
401
+ * @param {string} error_message - Error message
402
+ * @returns {Object} Admin error response
403
+ */
404
+ const create_admin_operation_error_response = (error_message) => ({
405
+ ok: 0,
406
+ error: `Admin operation failed: ${error_message}`
407
+ });
408
+
222
409
  /**
223
410
  * Handles admin operations with authentication and error handling.
224
411
  * @param {net.Socket} socket - Client socket connection
225
412
  * @param {Object} data - Admin operation data
226
413
  * @param {Function} check_authentication - Function to check if socket is authenticated
227
- * @param {Object} [connection_manager] - Connection manager instance (optional)
228
- * @param {Set} [authenticated_clients] - Set of authenticated client IDs (optional)
414
+ * @param {Object} connection_manager - Connection manager instance (optional)
415
+ * @param {Set} authenticated_clients - Set of authenticated client IDs (optional)
229
416
  */
230
417
  export const handle_admin_operation = async (socket, data, check_authentication, connection_manager = null, authenticated_clients = null) => {
231
418
  if (!check_authentication(socket)) {
232
- // NOTE: Unauthenticated admin requests should return ok: false.
233
- const response = { ok: false, error: 'Authentication required' };
234
- const encoded_response = encode_message(response);
235
- socket.write(encoded_response);
419
+ const response = create_admin_authentication_error_response();
420
+ send_encoded_response(socket, response);
236
421
  return;
237
422
  }
238
423
 
@@ -241,24 +426,11 @@ export const handle_admin_operation = async (socket, data, check_authentication,
241
426
  const admin_data = data || {};
242
427
 
243
428
  const result = await admin_operation(admin_action, admin_data, connection_manager, authenticated_clients);
244
-
245
- // NOTE: Handle different response formats for admin operations.
246
- if (!admin_action) {
247
- // NOTE: Default admin operation (backward compatibility) - return result directly with ok: true.
248
- const response = { ok: true, ...result };
249
- const encoded_response = encode_message(response);
250
- socket.write(encoded_response);
251
- } else {
252
- // NOTE: Specific admin actions - return result directly with ok: 1.
253
- const response = { ok: 1, ...result };
254
- const encoded_response = encode_message(response);
255
- socket.write(encoded_response);
256
- }
429
+ const response = create_admin_operation_response(admin_action, result);
430
+ send_encoded_response(socket, response);
257
431
  } catch (error) {
258
- // NOTE: Admin operation errors use ok: 0.
259
- const response = { ok: 0, error: `Admin operation failed: ${error.message}` };
260
- const encoded_response = encode_message(response);
261
- socket.write(encoded_response);
432
+ const response = create_admin_operation_error_response(error.message);
433
+ send_encoded_response(socket, response);
262
434
  }
263
435
  };
264
436
 
@@ -268,6 +440,5 @@ export const handle_admin_operation = async (socket, data, check_authentication,
268
440
  */
269
441
  export const handle_ping_operation = (socket) => {
270
442
  const ping_response = { ok: 1 };
271
- const encoded_ping_response = encode_message(ping_response);
272
- socket.write(encoded_ping_response);
443
+ send_encoded_response(socket, ping_response);
273
444
  };