@joystick.js/db-canary 0.0.0-canary.2209
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/.build/getFilesToBuild.js +26 -0
- package/.build/getPlatformSafeFilePath.js +6 -0
- package/.build/getPlatformSafePath.js +6 -0
- package/.build/index.js +88 -0
- package/.build/isWindows.js +3 -0
- package/API_KEY +1 -0
- package/README.md +1821 -0
- package/data/data.mdb +0 -0
- package/data/lock.mdb +0 -0
- package/dist/client/database.js +1 -0
- package/dist/client/index.js +1 -0
- package/dist/server/cluster/index.js +1 -0
- package/dist/server/cluster/master.js +20 -0
- package/dist/server/cluster/worker.js +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/lib/api_key_manager.js +9 -0
- package/dist/server/lib/auth_manager.js +1 -0
- package/dist/server/lib/auto_index_manager.js +1 -0
- package/dist/server/lib/backup_manager.js +1 -0
- package/dist/server/lib/connection_manager.js +1 -0
- package/dist/server/lib/disk_utils.js +2 -0
- package/dist/server/lib/http_server.js +405 -0
- package/dist/server/lib/index_manager.js +1 -0
- package/dist/server/lib/load_settings.js +1 -0
- package/dist/server/lib/logger.js +1 -0
- package/dist/server/lib/op_types.js +1 -0
- package/dist/server/lib/operation_dispatcher.js +1 -0
- package/dist/server/lib/operations/admin.js +1 -0
- package/dist/server/lib/operations/bulk_write.js +1 -0
- package/dist/server/lib/operations/create_index.js +1 -0
- package/dist/server/lib/operations/delete_one.js +1 -0
- package/dist/server/lib/operations/drop_index.js +1 -0
- package/dist/server/lib/operations/find.js +1 -0
- package/dist/server/lib/operations/find_one.js +1 -0
- package/dist/server/lib/operations/get_indexes.js +1 -0
- package/dist/server/lib/operations/insert_one.js +1 -0
- package/dist/server/lib/operations/update_one.js +1 -0
- package/dist/server/lib/performance_monitor.js +1 -0
- package/dist/server/lib/query_engine.js +1 -0
- package/dist/server/lib/recovery_manager.js +1 -0
- package/dist/server/lib/replication_manager.js +1 -0
- package/dist/server/lib/safe_json_parse.js +1 -0
- package/dist/server/lib/send_response.js +1 -0
- package/dist/server/lib/tcp_protocol.js +1 -0
- package/dist/server/lib/write_forwarder.js +1 -0
- package/dist/server/lib/write_queue.js +1 -0
- package/increment_version.js +3 -0
- package/logs/.013e15b54597d05db4b4b53ecc37b10c92a72927-audit.json +20 -0
- package/logs/.02de550a67ea0f5961faa2dfd458a4d06f59ebd1-audit.json +20 -0
- package/logs/.03494ba24eb3c72214b4068a77d54b8993bee651-audit.json +20 -0
- package/logs/.06309ec60b339be1259a7993dd09c732f8907fbc-audit.json +20 -0
- package/logs/.0663a04dcfa17285661e5e1b8cfa51f41523b210-audit.json +20 -0
- package/logs/.0f06e6c4c9b824622729e13927587479e5060391-audit.json +20 -0
- package/logs/.16ccf58682ecb22b3e3ec63f0da1b7fe9be56528-audit.json +20 -0
- package/logs/.1fa1a5d02f496474b1ab473524c65c984146a9ad-audit.json +20 -0
- package/logs/.2223c0ae3bea6f0d62c62b1d319cc8634856abb7-audit.json +20 -0
- package/logs/.23dc79ffda3e083665e6f5993f59397adcbf4a46-audit.json +20 -0
- package/logs/.28104f49b03906b189eefd1cd462cb46c3c0af22-audit.json +20 -0
- package/logs/.29cdbf13808abe6a0ce70ee2f2efdd680ce3fd8e-audit.json +20 -0
- package/logs/.2a9889afd071f77f41f5170d08703a0afca866b7-audit.json +20 -0
- package/logs/.2acec3d1940a2bbed487528b703ee5948959a599-audit.json +20 -0
- package/logs/.2fb60ff326338c02bfedbcd0e936444e4a216750-audit.json +20 -0
- package/logs/.318fc7a19530d76a345f030f7cad00dda15300e7-audit.json +20 -0
- package/logs/.3cf27043e19085f908cedc7701e6d013463208ee-audit.json +25 -0
- package/logs/.3d90d785415817fc443402843b7c95f8371adc9b-audit.json +20 -0
- package/logs/.4074bca620375f72966fc52dfd439577727671e5-audit.json +20 -0
- package/logs/.40eecf018417ea80a70ea8ec7a3cc9406bc6334b-audit.json +20 -0
- package/logs/.50e974f1ef7c365fca6a1251b2e2c2252914cb5e-audit.json +20 -0
- package/logs/.52cb7d9e4223cf26ba36006ac26b949a97c7923c-audit.json +20 -0
- package/logs/.54befcdb84c15aad980705a31bcc9f555c3577ab-audit.json +20 -0
- package/logs/.57dfb70e22eddb84db2e3c0ceeefac5c0b9baffa-audit.json +20 -0
- package/logs/.5f0b24705a1eaad4eca4968f2d86f91b3f9be683-audit.json +20 -0
- package/logs/.61ba98fdda7db58576b382fee07904e5db1169d6-audit.json +20 -0
- package/logs/.6235017727ef6b199d569a99d6aa8c8e80a1b475-audit.json +20 -0
- package/logs/.63db16193699219489d218a1ddea5dde3750cae4-audit.json +20 -0
- package/logs/.64fb67dfe14149c9eef728d79bf30a54da809c60-audit.json +20 -0
- package/logs/.669137453368987c1f311b5345342527afb54e50-audit.json +20 -0
- package/logs/.7a71f8c89ea28ae266d356aeff6306e876a30fbb-audit.json +20 -0
- package/logs/.7afbaa90fe9dc3a7d682676f9bb79f9a1b1fd9a6-audit.json +20 -0
- package/logs/.7ca29e322cd05327035de850099e7610864f2347-audit.json +20 -0
- package/logs/.83335ab3347e449dae03455a110aaf7f120d4802-audit.json +20 -0
- package/logs/.8c2487b5fd445d2c8e5c483c80b9fa99bbf1ca58-audit.json +20 -0
- package/logs/.8c8b9dc386922c9f3b4c13251af7052aac1d24c0-audit.json +20 -0
- package/logs/.8d6155d94640c4863301ae0fee5e4e7372a21446-audit.json +20 -0
- package/logs/.944a3119a243deea7c8270d5d9e582bb1d0eaa10-audit.json +20 -0
- package/logs/.9816a845c30fb2909f3b26a23eeb3538ebcad5db-audit.json +20 -0
- package/logs/.9dc08784e38b865488177c26d4af5934555e0323-audit.json +20 -0
- package/logs/.9dd27d2e0e454ac0a37600206d1cac5493b0d7ee-audit.json +20 -0
- package/logs/.a3d486feeac7654c59b547de96600e8849a06d4f-audit.json +20 -0
- package/logs/.a5b811f4def22250f86cc18870d7c4573625df22-audit.json +20 -0
- package/logs/.a61648eb5f830e0b6f508ac35e4f8f629d2ad4c7-audit.json +20 -0
- package/logs/.a89016d507045771b4b5a65656944a9c0f1e528b-audit.json +20 -0
- package/logs/.a99bee160a1c590be959af46bacc02724803f691-audit.json +20 -0
- package/logs/.ada7906d6243fd7da802f03d86c4ae5dd9df6236-audit.json +20 -0
- package/logs/.b518339ee942143b6af983af167f5bbb6983b4de-audit.json +20 -0
- package/logs/.b51b124b166d53c9519017856ea610d61d65fabe-audit.json +20 -0
- package/logs/.b7a6aee19f58e55633d5e4a3709041c47dfff975-audit.json +20 -0
- package/logs/.bd7a8a6ba9c55d557a4867ab53f02e3ec2e1553d-audit.json +20 -0
- package/logs/.c1435dafe453b169d6392b25065f3cf4ab6fbb21-audit.json +20 -0
- package/logs/.c17e1ce043109f77dc2f0e2aa290a9d1ed842c03-audit.json +20 -0
- package/logs/.ca62637ce9540e5a38a2fbedb2115febb6ad308a-audit.json +15 -0
- package/logs/.ccee67b9c176967f8977071409a41f5cb5cd6ad4-audit.json +20 -0
- package/logs/.db24043417ea79a6f14cd947476399e53930b48d-audit.json +20 -0
- package/logs/.e0f12acccb57829f5f33712bb2e2607ecd808147-audit.json +20 -0
- package/logs/.e9b6cc33d0bbd2e644c4e2bf44d177f850016557-audit.json +20 -0
- package/logs/.f15291d434808e3bdca7963ccd2e73893be027e6-audit.json +20 -0
- package/logs/.f4bdf9e21ef84f8a3fae3ffb32bbc39275991351-audit.json +15 -0
- package/logs/.fbac3aefac1e81b4230df5aa50667cb90d51024f-audit.json +20 -0
- package/logs/.fcfd495c0a9169db243f4a4f21878ee02b76413c-audit.json +20 -0
- package/logs/admin-2025-09-12.log +580 -0
- package/logs/admin-2025-09-15.log +283 -0
- package/logs/admin-error-2025-09-12.log +22 -0
- package/logs/admin-error-2025-09-15.log +10 -0
- package/logs/api_key_manager-2025-09-12.log +658 -0
- package/logs/api_key_manager-2025-09-15.log +295 -0
- package/logs/api_key_manager-error-2025-09-12.log +0 -0
- package/logs/api_key_manager-error-2025-09-15.log +0 -0
- package/logs/auth_manager-2025-09-12.log +4432 -0
- package/logs/auth_manager-2025-09-15.log +2000 -0
- package/logs/auth_manager-error-2025-09-12.log +11 -0
- package/logs/auth_manager-error-2025-09-15.log +5 -0
- package/logs/auto_index_manager-2025-09-12.log +84 -0
- package/logs/auto_index_manager-2025-09-15.log +45 -0
- package/logs/auto_index_manager-error-2025-09-12.log +6 -0
- package/logs/auto_index_manager-error-2025-09-15.log +0 -0
- package/logs/backup_manager-2025-09-12.log +198 -0
- package/logs/backup_manager-2025-09-15.log +90 -0
- package/logs/backup_manager-error-2025-09-12.log +198 -0
- package/logs/backup_manager-error-2025-09-15.log +90 -0
- package/logs/bulk_write-2025-09-12.log +66 -0
- package/logs/bulk_write-2025-09-15.log +38 -0
- package/logs/bulk_write-error-2025-09-12.log +0 -0
- package/logs/bulk_write-error-2025-09-15.log +0 -0
- package/logs/connection_manager-2025-09-12.log +2412 -0
- package/logs/connection_manager-2025-09-15.log +1132 -0
- package/logs/connection_manager-error-2025-09-12.log +0 -0
- package/logs/connection_manager-error-2025-09-15.log +0 -0
- package/logs/create_index-2025-09-12.log +302 -0
- package/logs/create_index-2025-09-15.log +158 -0
- package/logs/create_index-error-2025-09-12.log +30 -0
- package/logs/create_index-error-2025-09-15.log +13 -0
- package/logs/delete_one-2025-09-12.log +73 -0
- package/logs/delete_one-2025-09-15.log +43 -0
- package/logs/delete_one-error-2025-09-12.log +0 -0
- package/logs/delete_one-error-2025-09-15.log +0 -0
- package/logs/disk_utils-2025-09-12.log +4954 -0
- package/logs/disk_utils-2025-09-15.log +2446 -0
- package/logs/disk_utils-error-2025-09-12.log +0 -0
- package/logs/disk_utils-error-2025-09-15.log +0 -0
- package/logs/drop_index-2025-09-12.log +41 -0
- package/logs/drop_index-2025-09-15.log +23 -0
- package/logs/drop_index-error-2025-09-12.log +11 -0
- package/logs/drop_index-error-2025-09-15.log +5 -0
- package/logs/find-2025-09-12.log +1050 -0
- package/logs/find-2025-09-15.log +592 -0
- package/logs/find-error-2025-09-12.log +1 -0
- package/logs/find-error-2025-09-15.log +0 -0
- package/logs/find_one-2025-09-12.log +425 -0
- package/logs/find_one-2025-09-15.log +264 -0
- package/logs/find_one-error-2025-09-12.log +5 -0
- package/logs/find_one-error-2025-09-15.log +0 -0
- package/logs/get_indexes-2025-09-12.log +84 -0
- package/logs/get_indexes-2025-09-15.log +56 -0
- package/logs/get_indexes-error-2025-09-12.log +6 -0
- package/logs/get_indexes-error-2025-09-15.log +0 -0
- package/logs/http_server-2025-09-12.log +2772 -0
- package/logs/http_server-2025-09-15.log +1276 -0
- package/logs/http_server-error-2025-09-12.log +212 -0
- package/logs/http_server-error-2025-09-15.log +44 -0
- package/logs/index_manager-2025-09-12.log +5031 -0
- package/logs/index_manager-2025-09-15.log +2909 -0
- package/logs/index_manager-error-2025-09-12.log +80 -0
- package/logs/index_manager-error-2025-09-15.log +38 -0
- package/logs/insert_one-2025-09-12.log +2181 -0
- package/logs/insert_one-2025-09-15.log +1293 -0
- package/logs/insert_one-error-2025-09-12.log +0 -0
- package/logs/insert_one-error-2025-09-15.log +0 -0
- package/logs/master-2025-09-12.log +1882 -0
- package/logs/master-2025-09-15.log +910 -0
- package/logs/master-error-2025-09-12.log +80 -0
- package/logs/master-error-2025-09-15.log +0 -0
- package/logs/operation_dispatcher-2025-09-12.log +751 -0
- package/logs/operation_dispatcher-2025-09-15.log +359 -0
- package/logs/operation_dispatcher-error-2025-09-12.log +33 -0
- package/logs/operation_dispatcher-error-2025-09-15.log +11 -0
- package/logs/performance_monitor-2025-09-12.log +14889 -0
- package/logs/performance_monitor-2025-09-15.log +6803 -0
- package/logs/performance_monitor-error-2025-09-12.log +0 -0
- package/logs/performance_monitor-error-2025-09-15.log +0 -0
- package/logs/query_engine-2025-09-12.log +5310 -0
- package/logs/query_engine-2025-09-15.log +2639 -0
- package/logs/query_engine-error-2025-09-12.log +0 -0
- package/logs/query_engine-error-2025-09-15.log +0 -0
- package/logs/recovery_manager-2025-09-12.log +462 -0
- package/logs/recovery_manager-2025-09-15.log +210 -0
- package/logs/recovery_manager-error-2025-09-12.log +22 -0
- package/logs/recovery_manager-error-2025-09-15.log +10 -0
- package/logs/replication-2025-09-12.log +1923 -0
- package/logs/replication-2025-09-15.log +917 -0
- package/logs/replication-error-2025-09-12.log +33 -0
- package/logs/replication-error-2025-09-15.log +15 -0
- package/logs/server-2025-09-12.log +2601 -0
- package/logs/server-2025-09-15.log +1191 -0
- package/logs/server-error-2025-09-12.log +0 -0
- package/logs/server-error-2025-09-15.log +0 -0
- package/logs/tcp_protocol-2025-09-12.log +22 -0
- package/logs/tcp_protocol-2025-09-15.log +10 -0
- package/logs/tcp_protocol-error-2025-09-12.log +22 -0
- package/logs/tcp_protocol-error-2025-09-15.log +10 -0
- package/logs/test-2025-09-12.log +0 -0
- package/logs/test-2025-09-15.log +0 -0
- package/logs/test-error-2025-09-12.log +0 -0
- package/logs/test-error-2025-09-15.log +0 -0
- package/logs/update_one-2025-09-12.log +173 -0
- package/logs/update_one-2025-09-15.log +118 -0
- package/logs/update_one-error-2025-09-12.log +0 -0
- package/logs/update_one-error-2025-09-15.log +0 -0
- package/logs/worker-2025-09-12.log +1457 -0
- package/logs/worker-2025-09-15.log +695 -0
- package/logs/worker-error-2025-09-12.log +0 -0
- package/logs/worker-error-2025-09-15.log +0 -0
- package/logs/write_forwarder-2025-09-12.log +1956 -0
- package/logs/write_forwarder-2025-09-15.log +932 -0
- package/logs/write_forwarder-error-2025-09-12.log +66 -0
- package/logs/write_forwarder-error-2025-09-15.log +30 -0
- package/logs/write_queue-2025-09-12.log +612 -0
- package/logs/write_queue-2025-09-15.log +301 -0
- package/logs/write_queue-error-2025-09-12.log +184 -0
- package/logs/write_queue-error-2025-09-15.log +83 -0
- package/package.json +48 -0
- package/prompts/01-core-infrastructure.md +56 -0
- package/prompts/02-secondary-indexing.md +65 -0
- package/prompts/03-write-serialization.md +63 -0
- package/prompts/04-enhanced-authentication.md +75 -0
- package/prompts/05-comprehensive-admin-operations.md +75 -0
- package/prompts/06-backup-and-restore-system.md +106 -0
- package/prompts/07-production-safety-features.md +107 -0
- package/prompts/08-tcp-client-library.md +121 -0
- package/prompts/09-api-method-chaining.md +134 -0
- package/prompts/10-automatic-index-creation.md +223 -0
- package/prompts/11-operation-naming-consistency.md +268 -0
- package/prompts/12-tcp-replication-system.md +333 -0
- package/prompts/13-master-read-write-operations.md +57 -0
- package/prompts/14-index-upsert-operations.md +68 -0
- package/prompts/15-client-api-return-types.md +81 -0
- package/prompts/16-server-setup-ui.md +97 -0
- package/prompts/17-emergency-password-change.md +108 -0
- package/prompts/18-joystick-framework-integration.md +116 -0
- package/prompts/19-api-key-authentication-system.md +137 -0
- package/prompts/20-configurable-server-port.md +105 -0
- package/prompts/21-multi-database-support.md +161 -0
- package/prompts/FULL_TEXT_SEARCH.md +293 -0
- package/prompts/PROMPTS.md +158 -0
- package/prompts/README.md +221 -0
- package/prompts/TYPESCRIPT_GENERATION.md +179 -0
- package/src/client/database.js +166 -0
- package/src/client/index.js +752 -0
- package/src/server/cluster/index.js +53 -0
- package/src/server/cluster/master.js +774 -0
- package/src/server/cluster/worker.js +537 -0
- package/src/server/index.js +540 -0
- package/src/server/lib/api_key_manager.js +473 -0
- package/src/server/lib/auth_manager.js +375 -0
- package/src/server/lib/auto_index_manager.js +681 -0
- package/src/server/lib/backup_manager.js +650 -0
- package/src/server/lib/connection_manager.js +218 -0
- package/src/server/lib/disk_utils.js +118 -0
- package/src/server/lib/http_server.js +1165 -0
- package/src/server/lib/index_manager.js +756 -0
- package/src/server/lib/load_settings.js +143 -0
- package/src/server/lib/logger.js +135 -0
- package/src/server/lib/op_types.js +29 -0
- package/src/server/lib/operation_dispatcher.js +268 -0
- package/src/server/lib/operations/admin.js +808 -0
- package/src/server/lib/operations/bulk_write.js +367 -0
- package/src/server/lib/operations/create_index.js +68 -0
- package/src/server/lib/operations/delete_one.js +114 -0
- package/src/server/lib/operations/drop_index.js +58 -0
- package/src/server/lib/operations/find.js +340 -0
- package/src/server/lib/operations/find_one.js +319 -0
- package/src/server/lib/operations/get_indexes.js +52 -0
- package/src/server/lib/operations/insert_one.js +113 -0
- package/src/server/lib/operations/update_one.js +225 -0
- package/src/server/lib/performance_monitor.js +313 -0
- package/src/server/lib/query_engine.js +243 -0
- package/src/server/lib/recovery_manager.js +388 -0
- package/src/server/lib/replication_manager.js +727 -0
- package/src/server/lib/safe_json_parse.js +21 -0
- package/src/server/lib/send_response.js +47 -0
- package/src/server/lib/tcp_protocol.js +130 -0
- package/src/server/lib/write_forwarder.js +636 -0
- package/src/server/lib/write_queue.js +335 -0
- package/test_data/data.mdb +0 -0
- package/test_data/lock.mdb +0 -0
- package/tests/client/index.test.js +1232 -0
- package/tests/server/cluster/cluster.test.js +248 -0
- package/tests/server/cluster/master_read_write_operations.test.js +577 -0
- package/tests/server/index.test.js +651 -0
- package/tests/server/integration/authentication_integration.test.js +294 -0
- package/tests/server/integration/auto_indexing_integration.test.js +268 -0
- package/tests/server/integration/backup_integration.test.js +513 -0
- package/tests/server/integration/indexing_integration.test.js +126 -0
- package/tests/server/integration/production_safety_integration.test.js +358 -0
- package/tests/server/integration/replication_integration.test.js +227 -0
- package/tests/server/integration/write_serialization_integration.test.js +246 -0
- package/tests/server/lib/api_key_manager.test.js +516 -0
- package/tests/server/lib/auth_manager.test.js +317 -0
- package/tests/server/lib/auto_index_manager.test.js +275 -0
- package/tests/server/lib/backup_manager.test.js +238 -0
- package/tests/server/lib/connection_manager.test.js +221 -0
- package/tests/server/lib/disk_utils.test.js +63 -0
- package/tests/server/lib/http_server.test.js +389 -0
- package/tests/server/lib/index_manager.test.js +301 -0
- package/tests/server/lib/load_settings.test.js +107 -0
- package/tests/server/lib/load_settings_port_config.test.js +243 -0
- package/tests/server/lib/logger.test.js +282 -0
- package/tests/server/lib/operations/admin.test.js +638 -0
- package/tests/server/lib/operations/bulk_write.test.js +128 -0
- package/tests/server/lib/operations/create_index.test.js +138 -0
- package/tests/server/lib/operations/delete_one.test.js +52 -0
- package/tests/server/lib/operations/drop_index.test.js +72 -0
- package/tests/server/lib/operations/find.test.js +93 -0
- package/tests/server/lib/operations/find_one.test.js +91 -0
- package/tests/server/lib/operations/get_indexes.test.js +87 -0
- package/tests/server/lib/operations/insert_one.test.js +42 -0
- package/tests/server/lib/operations/update_one.test.js +89 -0
- package/tests/server/lib/performance_monitor.test.js +185 -0
- package/tests/server/lib/query_engine.test.js +46 -0
- package/tests/server/lib/recovery_manager.test.js +414 -0
- package/tests/server/lib/replication_manager.test.js +202 -0
- package/tests/server/lib/safe_json_parse.test.js +45 -0
- package/tests/server/lib/send_response.test.js +155 -0
- package/tests/server/lib/tcp_protocol.test.js +169 -0
- package/tests/server/lib/write_forwarder.test.js +258 -0
- package/tests/server/lib/write_queue.test.js +255 -0
- package/tsconfig.json +30 -0
- package/types/client/index.d.ts +447 -0
- package/types/server/cluster/index.d.ts +28 -0
- package/types/server/cluster/master.d.ts +115 -0
- package/types/server/cluster/worker.d.ts +1 -0
- package/types/server/lib/auth_manager.d.ts +13 -0
- package/types/server/lib/backup_manager.d.ts +43 -0
- package/types/server/lib/connection_manager.d.ts +15 -0
- package/types/server/lib/disk_utils.d.ts +3 -0
- package/types/server/lib/index_manager.d.ts +24 -0
- package/types/server/lib/load_settings.d.ts +4 -0
- package/types/server/lib/logger.d.ts +44 -0
- package/types/server/lib/op_types.d.ts +6 -0
- package/types/server/lib/performance_monitor.d.ts +68 -0
- package/types/server/lib/query_engine.d.ts +10 -0
- package/types/server/lib/safe_json_parse.d.ts +7 -0
- package/types/server/lib/send_response.d.ts +3 -0
- package/types/server/lib/tcp_protocol.d.ts +12 -0
- package/types/server/lib/write_queue.d.ts +2 -0
|
@@ -0,0 +1,808 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Comprehensive admin operations for database management, monitoring, and maintenance.
|
|
3
|
+
*
|
|
4
|
+
* Provides extensive administrative functionality including statistics collection, collection management,
|
|
5
|
+
* document operations, backup/restore, replication management, index operations, and performance monitoring.
|
|
6
|
+
* Supports both legacy and modern admin operation formats with comprehensive error handling.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { get_database } from '../query_engine.js';
|
|
10
|
+
import { get_settings } from '../load_settings.js';
|
|
11
|
+
import { get_write_queue } from '../write_queue.js';
|
|
12
|
+
import { get_auth_stats } from '../auth_manager.js';
|
|
13
|
+
import {
|
|
14
|
+
get_query_statistics,
|
|
15
|
+
get_auto_index_statistics,
|
|
16
|
+
force_index_evaluation,
|
|
17
|
+
remove_automatic_indexes
|
|
18
|
+
} from '../auto_index_manager.js';
|
|
19
|
+
import {
|
|
20
|
+
create_index,
|
|
21
|
+
drop_index,
|
|
22
|
+
get_indexes
|
|
23
|
+
} from '../index_manager.js';
|
|
24
|
+
import {
|
|
25
|
+
test_s3_connection,
|
|
26
|
+
create_backup,
|
|
27
|
+
list_backups,
|
|
28
|
+
restore_backup,
|
|
29
|
+
cleanup_old_backups
|
|
30
|
+
} from '../backup_manager.js';
|
|
31
|
+
import { get_replication_manager } from '../replication_manager.js';
|
|
32
|
+
import { get_write_forwarder } from '../write_forwarder.js';
|
|
33
|
+
import create_logger from '../logger.js';
|
|
34
|
+
import { performance_monitor } from '../performance_monitor.js';
|
|
35
|
+
|
|
36
|
+
const { create_context_logger } = create_logger('admin');
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Gets comprehensive server and database statistics.
|
|
40
|
+
* @returns {Object} Enhanced statistics including server, database, and performance metrics
|
|
41
|
+
* @throws {Error} When stats collection fails
|
|
42
|
+
*/
|
|
43
|
+
const get_enhanced_stats = () => {
|
|
44
|
+
const log = create_context_logger();
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const db = get_database();
|
|
48
|
+
|
|
49
|
+
// Handle settings gracefully - use defaults if not loaded
|
|
50
|
+
let settings;
|
|
51
|
+
try {
|
|
52
|
+
settings = get_settings();
|
|
53
|
+
} catch (settings_error) {
|
|
54
|
+
settings = { port: 1983 };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Safely get database stats
|
|
58
|
+
let db_stats = {};
|
|
59
|
+
try {
|
|
60
|
+
const raw_stats = db.getStats ? db.getStats() : {};
|
|
61
|
+
db_stats = {
|
|
62
|
+
pageSize: raw_stats.pageSize || 0,
|
|
63
|
+
treeDepth: raw_stats.treeDepth || 0,
|
|
64
|
+
treeBranchPages: raw_stats.treeBranchPages || 0,
|
|
65
|
+
treeLeafPages: raw_stats.treeLeafPages || 0,
|
|
66
|
+
entryCount: raw_stats.entryCount || 0,
|
|
67
|
+
mapSize: raw_stats.mapSize || 0,
|
|
68
|
+
lastPageNumber: raw_stats.lastPageNumber || 0
|
|
69
|
+
};
|
|
70
|
+
} catch (stats_error) {
|
|
71
|
+
db_stats = { error: 'Could not retrieve database stats' };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Count collections and documents safely
|
|
75
|
+
const collections = {};
|
|
76
|
+
let total_documents = 0;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
for (const { key } of db.getRange()) {
|
|
80
|
+
if (typeof key === 'string' && key.includes(':') && !key.startsWith('_')) {
|
|
81
|
+
const collection_name = key.split(':')[0];
|
|
82
|
+
collections[collection_name] = (collections[collection_name] || 0) + 1;
|
|
83
|
+
total_documents++;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch (range_error) {
|
|
87
|
+
log.warn('Could not iterate database range for stats', { error: range_error.message });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Calculate memory usage percentage
|
|
91
|
+
const memory_usage = process.memoryUsage();
|
|
92
|
+
const memory_usage_mb = {
|
|
93
|
+
rss: Math.round(memory_usage.rss / 1024 / 1024),
|
|
94
|
+
heapTotal: Math.round(memory_usage.heapTotal / 1024 / 1024),
|
|
95
|
+
heapUsed: Math.round(memory_usage.heapUsed / 1024 / 1024),
|
|
96
|
+
external: Math.round(memory_usage.external / 1024 / 1024)
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Calculate database size percentage
|
|
100
|
+
const map_size_usage_percent = db_stats.mapSize > 0
|
|
101
|
+
? Math.round((db_stats.lastPageNumber * db_stats.pageSize / db_stats.mapSize) * 100)
|
|
102
|
+
: 0;
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
server: {
|
|
106
|
+
uptime: Math.floor(process.uptime()),
|
|
107
|
+
uptime_formatted: format_uptime(process.uptime()),
|
|
108
|
+
memory_usage: memory_usage_mb,
|
|
109
|
+
memory_usage_raw: memory_usage,
|
|
110
|
+
node_version: process.version,
|
|
111
|
+
platform: process.platform,
|
|
112
|
+
arch: process.arch,
|
|
113
|
+
pid: process.pid,
|
|
114
|
+
cpu_usage: process.cpuUsage()
|
|
115
|
+
},
|
|
116
|
+
database: {
|
|
117
|
+
total_documents,
|
|
118
|
+
total_collections: Object.keys(collections).length,
|
|
119
|
+
collections,
|
|
120
|
+
stats: db_stats,
|
|
121
|
+
map_size_usage_percent,
|
|
122
|
+
disk_usage: {
|
|
123
|
+
map_size_mb: Math.round((db_stats.mapSize || 0) / 1024 / 1024),
|
|
124
|
+
used_space_mb: Math.round(((db_stats.lastPageNumber || 0) * (db_stats.pageSize || 0)) / 1024 / 1024)
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
performance: {
|
|
128
|
+
ops_per_second: calculate_ops_per_second(),
|
|
129
|
+
avg_response_time_ms: calculate_avg_response_time()
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
} catch (error) {
|
|
133
|
+
log.error('Failed to get enhanced stats', { error: error.message });
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Formats uptime seconds into human-readable string.
|
|
140
|
+
* @param {number} seconds - Uptime in seconds
|
|
141
|
+
* @returns {string} Formatted uptime string (e.g., "2d 3h 45m 12s")
|
|
142
|
+
*/
|
|
143
|
+
const format_uptime = (seconds) => {
|
|
144
|
+
const days = Math.floor(seconds / 86400);
|
|
145
|
+
const hours = Math.floor((seconds % 86400) / 3600);
|
|
146
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
147
|
+
const secs = Math.floor(seconds % 60);
|
|
148
|
+
|
|
149
|
+
if (days > 0) {
|
|
150
|
+
return `${days}d ${hours}h ${minutes}m ${secs}s`;
|
|
151
|
+
} else if (hours > 0) {
|
|
152
|
+
return `${hours}h ${minutes}m ${secs}s`;
|
|
153
|
+
} else if (minutes > 0) {
|
|
154
|
+
return `${minutes}m ${secs}s`;
|
|
155
|
+
} else {
|
|
156
|
+
return `${secs}s`;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Simple performance tracking (in production, this would be more sophisticated)
|
|
161
|
+
let operation_count = 0;
|
|
162
|
+
let total_response_time = 0;
|
|
163
|
+
let start_time = Date.now();
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Calculates operations per second based on tracked operations.
|
|
167
|
+
* @returns {number} Operations per second
|
|
168
|
+
*/
|
|
169
|
+
const calculate_ops_per_second = () => {
|
|
170
|
+
const elapsed_seconds = (Date.now() - start_time) / 1000;
|
|
171
|
+
return elapsed_seconds > 0 ? Math.round(operation_count / elapsed_seconds) : 0;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Calculates average response time based on tracked operations.
|
|
176
|
+
* @returns {number} Average response time in milliseconds
|
|
177
|
+
*/
|
|
178
|
+
const calculate_avg_response_time = () => {
|
|
179
|
+
return operation_count > 0 ? Math.round(total_response_time / operation_count) : 0;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Tracks an operation for performance monitoring.
|
|
184
|
+
* @param {number} duration_ms - Operation duration in milliseconds
|
|
185
|
+
*/
|
|
186
|
+
export const track_operation = (duration_ms) => {
|
|
187
|
+
operation_count++;
|
|
188
|
+
total_response_time += duration_ms;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Lists all collections in the database with metadata.
|
|
193
|
+
* @param {string} [database_name='default'] - Database name to list collections from
|
|
194
|
+
* @returns {Object} Object containing collections array, total counts
|
|
195
|
+
* @throws {Error} When collection listing fails
|
|
196
|
+
*/
|
|
197
|
+
const list_collections = (database_name = 'default') => {
|
|
198
|
+
const log = create_context_logger();
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const db = get_database();
|
|
202
|
+
const collections_map = {};
|
|
203
|
+
let total_documents = 0;
|
|
204
|
+
|
|
205
|
+
// Look for database:collection:document_id format
|
|
206
|
+
try {
|
|
207
|
+
for (const { key } of db.getRange()) {
|
|
208
|
+
if (typeof key === 'string' && key.includes(':') && !key.startsWith('_')) {
|
|
209
|
+
const parts = key.split(':');
|
|
210
|
+
if (parts.length >= 3) {
|
|
211
|
+
const key_database = parts[0];
|
|
212
|
+
const collection_name = parts[1];
|
|
213
|
+
|
|
214
|
+
// Only count collections from the specified database
|
|
215
|
+
if (key_database === database_name) {
|
|
216
|
+
if (!collections_map[collection_name]) {
|
|
217
|
+
collections_map[collection_name] = {
|
|
218
|
+
name: collection_name,
|
|
219
|
+
document_count: 0,
|
|
220
|
+
indexes: [],
|
|
221
|
+
estimated_size_bytes: 0
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
collections_map[collection_name].document_count++;
|
|
225
|
+
total_documents++;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
} catch (range_error) {
|
|
231
|
+
log.warn('Could not iterate database range for collections', { error: range_error.message });
|
|
232
|
+
|
|
233
|
+
// Fallback: try to find collections by checking for database-specific patterns
|
|
234
|
+
const potential_collections = [
|
|
235
|
+
'admin_test', 'test_collection', 'queue_test', 'users', 'products',
|
|
236
|
+
'orders', 'sessions', 'logs', 'analytics', 'settings', 'another_collection',
|
|
237
|
+
'list_test', 'pagination_test', 'get_test', 'query_test', 'admin_insert_test',
|
|
238
|
+
'admin_update_test', 'admin_delete_test'
|
|
239
|
+
];
|
|
240
|
+
|
|
241
|
+
for (const collection_name of potential_collections) {
|
|
242
|
+
try {
|
|
243
|
+
// Check if this collection has any documents in the specified database
|
|
244
|
+
const prefix = `${database_name}:${collection_name}:`;
|
|
245
|
+
const range = db.getRange({ start: prefix, end: prefix + '\xFF' });
|
|
246
|
+
|
|
247
|
+
let document_count = 0;
|
|
248
|
+
for (const entry of range) {
|
|
249
|
+
document_count++;
|
|
250
|
+
total_documents++;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (document_count > 0) {
|
|
254
|
+
collections_map[collection_name] = {
|
|
255
|
+
name: collection_name,
|
|
256
|
+
document_count,
|
|
257
|
+
indexes: [],
|
|
258
|
+
estimated_size_bytes: document_count * 100 // Rough estimate
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
} catch (collection_error) {
|
|
262
|
+
// Skip collections that can't be accessed
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Get index information for each collection safely
|
|
269
|
+
try {
|
|
270
|
+
const index_prefix = `index:${database_name}:`;
|
|
271
|
+
const index_range = db.getRange({ start: index_prefix, end: index_prefix + '\xFF' });
|
|
272
|
+
for (const { key, value } of index_range) {
|
|
273
|
+
if (typeof key === 'string' && key.startsWith(index_prefix)) {
|
|
274
|
+
const remaining_key = key.substring(index_prefix.length);
|
|
275
|
+
const collection_name = remaining_key.split(':')[0];
|
|
276
|
+
if (collections_map[collection_name]) {
|
|
277
|
+
if (!collections_map[collection_name].indexes.includes(remaining_key.split(':')[1])) {
|
|
278
|
+
collections_map[collection_name].indexes.push(remaining_key.split(':')[1]);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
} catch (index_range_error) {
|
|
284
|
+
log.warn('Could not iterate index range', { error: index_range_error.message });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const collections_array = Object.values(collections_map);
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
collections: collections_array,
|
|
291
|
+
total_collections: collections_array.length,
|
|
292
|
+
total_documents
|
|
293
|
+
};
|
|
294
|
+
} catch (error) {
|
|
295
|
+
log.error('Failed to list collections', { error: error.message });
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Lists documents in a collection with pagination and sorting.
|
|
302
|
+
* @param {string} collection - Collection name
|
|
303
|
+
* @param {Object} [options={}] - List options
|
|
304
|
+
* @param {number} [options.limit=50] - Maximum documents to return
|
|
305
|
+
* @param {number} [options.skip=0] - Number of documents to skip
|
|
306
|
+
* @param {string} [options.sort_field] - Field to sort by
|
|
307
|
+
* @param {string} [options.sort_order='asc'] - Sort order ('asc' or 'desc')
|
|
308
|
+
* @param {string} [options.database='default'] - Database name
|
|
309
|
+
* @returns {Object} Object containing documents array and pagination info
|
|
310
|
+
* @throws {Error} When collection name is missing or listing fails
|
|
311
|
+
*/
|
|
312
|
+
const list_documents = (collection, options = {}) => {
|
|
313
|
+
const log = create_context_logger();
|
|
314
|
+
|
|
315
|
+
if (!collection) {
|
|
316
|
+
throw new Error('Collection name is required');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
const db = get_database();
|
|
321
|
+
const { limit = 50, skip = 0, sort_field, sort_order = 'asc', database = 'default' } = options;
|
|
322
|
+
|
|
323
|
+
const documents = [];
|
|
324
|
+
const prefix = `${database}:${collection}:`;
|
|
325
|
+
let count = 0;
|
|
326
|
+
let skipped = 0;
|
|
327
|
+
|
|
328
|
+
for (const { key, value } of db.getRange({ start: prefix, end: prefix + '\xFF' })) {
|
|
329
|
+
if (typeof key === 'string' && key.startsWith(prefix)) {
|
|
330
|
+
if (skipped < skip) {
|
|
331
|
+
skipped++;
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (count >= limit) {
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
const document = JSON.parse(value);
|
|
341
|
+
const document_id = key.substring(prefix.length);
|
|
342
|
+
documents.push({ _id: document_id, ...document });
|
|
343
|
+
count++;
|
|
344
|
+
} catch (parse_error) {
|
|
345
|
+
log.warn('Could not parse document', {
|
|
346
|
+
collection,
|
|
347
|
+
key,
|
|
348
|
+
error: parse_error.message
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Simple sorting if requested
|
|
355
|
+
if (sort_field && documents.length > 0) {
|
|
356
|
+
documents.sort((a, b) => {
|
|
357
|
+
const a_val = a[sort_field];
|
|
358
|
+
const b_val = b[sort_field];
|
|
359
|
+
|
|
360
|
+
if (sort_order === 'desc') {
|
|
361
|
+
return b_val > a_val ? 1 : b_val < a_val ? -1 : 0;
|
|
362
|
+
} else {
|
|
363
|
+
return a_val > b_val ? 1 : a_val < b_val ? -1 : 0;
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return {
|
|
369
|
+
collection,
|
|
370
|
+
documents,
|
|
371
|
+
count: documents.length,
|
|
372
|
+
skip,
|
|
373
|
+
limit,
|
|
374
|
+
has_more: count === limit
|
|
375
|
+
};
|
|
376
|
+
} catch (error) {
|
|
377
|
+
log.error('Failed to list documents', { collection, error: error.message });
|
|
378
|
+
throw error;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Gets a specific document by ID from a collection.
|
|
384
|
+
* @param {string} collection - Collection name
|
|
385
|
+
* @param {string} document_id - Document ID
|
|
386
|
+
* @param {string} [database='default'] - Database name
|
|
387
|
+
* @returns {Object} Object containing found status and document data
|
|
388
|
+
* @throws {Error} When collection name or document ID is missing
|
|
389
|
+
*/
|
|
390
|
+
const get_document = (collection, document_id, database = 'default') => {
|
|
391
|
+
const log = create_context_logger();
|
|
392
|
+
|
|
393
|
+
if (!collection || !document_id) {
|
|
394
|
+
throw new Error('Collection name and document ID are required');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
try {
|
|
398
|
+
const db = get_database();
|
|
399
|
+
const key = `${database}:${collection}:${document_id}`;
|
|
400
|
+
const value = db.get(key);
|
|
401
|
+
|
|
402
|
+
if (!value) {
|
|
403
|
+
return { found: false, collection, document_id };
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const document = JSON.parse(value);
|
|
407
|
+
return {
|
|
408
|
+
found: true,
|
|
409
|
+
collection,
|
|
410
|
+
document_id,
|
|
411
|
+
document: { _id: document_id, ...document }
|
|
412
|
+
};
|
|
413
|
+
} catch (error) {
|
|
414
|
+
log.error('Failed to get document', { collection, document_id, error: error.message });
|
|
415
|
+
throw error;
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Queries documents in a collection with filtering and pagination.
|
|
421
|
+
* @param {string} collection - Collection name
|
|
422
|
+
* @param {Object} [filter={}] - Query filter with MongoDB-style operators
|
|
423
|
+
* @param {Object} [options={}] - Query options
|
|
424
|
+
* @param {number} [options.limit=100] - Maximum documents to return
|
|
425
|
+
* @param {number} [options.skip=0] - Number of documents to skip
|
|
426
|
+
* @param {string} [options.database='default'] - Database name
|
|
427
|
+
* @returns {Object} Object containing matching documents and query metadata
|
|
428
|
+
* @throws {Error} When collection name is missing or query fails
|
|
429
|
+
*/
|
|
430
|
+
const query_documents = (collection, filter = {}, options = {}) => {
|
|
431
|
+
const log = create_context_logger();
|
|
432
|
+
|
|
433
|
+
if (!collection) {
|
|
434
|
+
throw new Error('Collection name is required');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
const db = get_database();
|
|
439
|
+
const { limit = 100, skip = 0, database = 'default' } = options;
|
|
440
|
+
|
|
441
|
+
const documents = [];
|
|
442
|
+
const prefix = `${database}:${collection}:`;
|
|
443
|
+
let count = 0;
|
|
444
|
+
let skipped = 0;
|
|
445
|
+
let total_examined = 0;
|
|
446
|
+
|
|
447
|
+
for (const { key, value } of db.getRange({ start: prefix, end: prefix + '\xFF' })) {
|
|
448
|
+
if (typeof key === 'string' && key.startsWith(prefix)) {
|
|
449
|
+
total_examined++;
|
|
450
|
+
|
|
451
|
+
try {
|
|
452
|
+
const document = JSON.parse(value);
|
|
453
|
+
const document_id = key.substring(prefix.length);
|
|
454
|
+
const full_document = { _id: document_id, ...document };
|
|
455
|
+
|
|
456
|
+
// Simple filter matching
|
|
457
|
+
const matches = Object.keys(filter).every(field => {
|
|
458
|
+
const filter_value = filter[field];
|
|
459
|
+
const doc_value = full_document[field];
|
|
460
|
+
|
|
461
|
+
if (typeof filter_value === 'object' && filter_value !== null) {
|
|
462
|
+
// Handle operators like { $gt: 5 }, { $regex: "pattern" }
|
|
463
|
+
return Object.keys(filter_value).every(operator => {
|
|
464
|
+
const op_value = filter_value[operator];
|
|
465
|
+
switch (operator) {
|
|
466
|
+
case '$gt': return doc_value > op_value;
|
|
467
|
+
case '$gte': return doc_value >= op_value;
|
|
468
|
+
case '$lt': return doc_value < op_value;
|
|
469
|
+
case '$lte': return doc_value <= op_value;
|
|
470
|
+
case '$ne': return doc_value !== op_value;
|
|
471
|
+
case '$in': return Array.isArray(op_value) && op_value.includes(doc_value);
|
|
472
|
+
case '$regex': return new RegExp(op_value).test(String(doc_value));
|
|
473
|
+
default: return doc_value === filter_value;
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
} else {
|
|
477
|
+
return doc_value === filter_value;
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
if (matches) {
|
|
482
|
+
if (skipped < skip) {
|
|
483
|
+
skipped++;
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (count >= limit) {
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
documents.push(full_document);
|
|
492
|
+
count++;
|
|
493
|
+
}
|
|
494
|
+
} catch (parse_error) {
|
|
495
|
+
log.warn('Could not parse document during query', {
|
|
496
|
+
collection,
|
|
497
|
+
key,
|
|
498
|
+
error: parse_error.message
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
collection,
|
|
506
|
+
filter,
|
|
507
|
+
documents,
|
|
508
|
+
count: documents.length,
|
|
509
|
+
total_examined,
|
|
510
|
+
skip,
|
|
511
|
+
limit,
|
|
512
|
+
has_more: count === limit
|
|
513
|
+
};
|
|
514
|
+
} catch (error) {
|
|
515
|
+
log.error('Failed to query documents', { collection, filter, error: error.message });
|
|
516
|
+
throw error;
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Inserts a document via admin interface (delegates to insert_one operation).
|
|
522
|
+
* @param {string} database_name - Database name
|
|
523
|
+
* @param {string} collection - Collection name
|
|
524
|
+
* @param {Object} document - Document to insert
|
|
525
|
+
* @param {Object} [options={}] - Insert options
|
|
526
|
+
* @returns {Promise<Object>} Insert result
|
|
527
|
+
*/
|
|
528
|
+
const insert_document = async (database_name, collection, document, options = {}) => {
|
|
529
|
+
const insert_one_module = await import('./insert_one.js');
|
|
530
|
+
return await insert_one_module.default(database_name, collection, document, options);
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Updates a document via admin interface (delegates to update_one operation).
|
|
535
|
+
* @param {string} database_name - Database name
|
|
536
|
+
* @param {string} collection - Collection name
|
|
537
|
+
* @param {Object} filter - Filter criteria
|
|
538
|
+
* @param {Object} update - Update operations
|
|
539
|
+
* @param {Object} [options={}] - Update options
|
|
540
|
+
* @returns {Promise<Object>} Update result
|
|
541
|
+
*/
|
|
542
|
+
const update_document = async (database_name, collection, filter, update, options = {}) => {
|
|
543
|
+
const update_one_module = await import('./update_one.js');
|
|
544
|
+
return await update_one_module.default(database_name, collection, filter, update, options);
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Deletes a document via admin interface (delegates to delete_one operation).
|
|
549
|
+
* @param {string} database_name - Database name
|
|
550
|
+
* @param {string} collection - Collection name
|
|
551
|
+
* @param {Object} filter - Filter criteria
|
|
552
|
+
* @param {Object} [options={}] - Delete options
|
|
553
|
+
* @returns {Promise<Object>} Delete result
|
|
554
|
+
*/
|
|
555
|
+
const delete_document = async (database_name, collection, filter, options = {}) => {
|
|
556
|
+
const delete_one_module = await import('./delete_one.js');
|
|
557
|
+
return await delete_one_module.default(database_name, collection, filter, options);
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Main admin operation handler supporting various administrative actions.
|
|
562
|
+
* @param {string} admin_action - Admin action to perform
|
|
563
|
+
* @param {Object} [data={}] - Action-specific data
|
|
564
|
+
* @param {Object} connection_manager - Connection manager instance
|
|
565
|
+
* @param {Set} authenticated_clients - Set of authenticated client IDs
|
|
566
|
+
* @returns {Promise<Object>} Action result
|
|
567
|
+
* @throws {Error} When admin action fails
|
|
568
|
+
*/
|
|
569
|
+
export default async (admin_action, data = {}, connection_manager, authenticated_clients) => {
|
|
570
|
+
const log = create_context_logger();
|
|
571
|
+
const start_time = Date.now();
|
|
572
|
+
|
|
573
|
+
try {
|
|
574
|
+
let result;
|
|
575
|
+
|
|
576
|
+
switch (admin_action) {
|
|
577
|
+
case 'stats':
|
|
578
|
+
result = {
|
|
579
|
+
server: {
|
|
580
|
+
uptime: Math.floor(process.uptime()),
|
|
581
|
+
uptime_formatted: format_uptime(process.uptime()),
|
|
582
|
+
node_version: process.version,
|
|
583
|
+
platform: process.platform,
|
|
584
|
+
arch: process.arch,
|
|
585
|
+
pid: process.pid
|
|
586
|
+
},
|
|
587
|
+
memory: performance_monitor.get_memory_stats(),
|
|
588
|
+
database: {
|
|
589
|
+
...performance_monitor.get_database_stats(),
|
|
590
|
+
map_size_mb: Math.round((performance_monitor.get_database_stats()?.map_size || 0) / 1024 / 1024),
|
|
591
|
+
used_space_mb: Math.round((performance_monitor.get_database_stats()?.used_space || 0) / 1024 / 1024),
|
|
592
|
+
usage_percent: performance_monitor.get_database_stats()?.usage_percent || 0
|
|
593
|
+
},
|
|
594
|
+
performance: {
|
|
595
|
+
ops_per_second: calculate_ops_per_second(),
|
|
596
|
+
avg_response_time_ms: calculate_avg_response_time()
|
|
597
|
+
},
|
|
598
|
+
system: performance_monitor.get_system_stats(),
|
|
599
|
+
connections: connection_manager?.get_stats() || {},
|
|
600
|
+
write_queue: get_write_queue()?.get_stats() || {},
|
|
601
|
+
authentication: {
|
|
602
|
+
authenticated_clients: authenticated_clients?.size || 0,
|
|
603
|
+
...get_auth_stats()
|
|
604
|
+
},
|
|
605
|
+
settings: (() => {
|
|
606
|
+
try {
|
|
607
|
+
const settings = get_settings();
|
|
608
|
+
return {
|
|
609
|
+
port: settings.port || 1983
|
|
610
|
+
};
|
|
611
|
+
} catch (error) {
|
|
612
|
+
return { port: 1983 };
|
|
613
|
+
}
|
|
614
|
+
})()
|
|
615
|
+
};
|
|
616
|
+
break;
|
|
617
|
+
|
|
618
|
+
case 'list_collections':
|
|
619
|
+
result = list_collections();
|
|
620
|
+
break;
|
|
621
|
+
|
|
622
|
+
case 'list_documents':
|
|
623
|
+
result = list_documents(data.collection, {
|
|
624
|
+
limit: data.limit,
|
|
625
|
+
skip: data.skip,
|
|
626
|
+
sort_field: data.sort_field,
|
|
627
|
+
sort_order: data.sort_order
|
|
628
|
+
});
|
|
629
|
+
break;
|
|
630
|
+
|
|
631
|
+
case 'get_document':
|
|
632
|
+
result = get_document(data.collection, data.document_id);
|
|
633
|
+
break;
|
|
634
|
+
|
|
635
|
+
case 'query_documents':
|
|
636
|
+
result = query_documents(data.collection, data.filter, {
|
|
637
|
+
limit: data.limit,
|
|
638
|
+
skip: data.skip
|
|
639
|
+
});
|
|
640
|
+
break;
|
|
641
|
+
|
|
642
|
+
case 'insert_document':
|
|
643
|
+
result = await insert_document(data.database || 'default', data.collection, data.document, data.options);
|
|
644
|
+
break;
|
|
645
|
+
|
|
646
|
+
case 'update_document':
|
|
647
|
+
// If document_id is provided, use it as a filter, otherwise use the provided filter
|
|
648
|
+
const final_update_filter = data.document_id ? { _id: data.document_id } : data.filter;
|
|
649
|
+
result = await update_document(data.database || 'default', data.collection, final_update_filter, data.update, data.options);
|
|
650
|
+
break;
|
|
651
|
+
|
|
652
|
+
case 'delete_document':
|
|
653
|
+
// If document_id is provided, use it as a filter, otherwise use the provided filter
|
|
654
|
+
const final_delete_filter = data.document_id ? { _id: data.document_id } : data.filter;
|
|
655
|
+
result = await delete_document(data.database || 'default', data.collection, final_delete_filter, data.options);
|
|
656
|
+
break;
|
|
657
|
+
|
|
658
|
+
case 'test_s3_connection':
|
|
659
|
+
result = await test_s3_connection();
|
|
660
|
+
break;
|
|
661
|
+
|
|
662
|
+
case 'backup_now':
|
|
663
|
+
result = await create_backup();
|
|
664
|
+
break;
|
|
665
|
+
|
|
666
|
+
case 'list_backups':
|
|
667
|
+
result = await list_backups();
|
|
668
|
+
break;
|
|
669
|
+
|
|
670
|
+
case 'restore_backup':
|
|
671
|
+
if (!data.backup_filename) {
|
|
672
|
+
throw new Error('backup_filename is required for restore operation');
|
|
673
|
+
}
|
|
674
|
+
result = await restore_backup(data.backup_filename);
|
|
675
|
+
break;
|
|
676
|
+
|
|
677
|
+
case 'cleanup_backups':
|
|
678
|
+
result = await cleanup_old_backups();
|
|
679
|
+
break;
|
|
680
|
+
|
|
681
|
+
case 'get_auto_index_stats':
|
|
682
|
+
result = get_auto_index_statistics();
|
|
683
|
+
break;
|
|
684
|
+
|
|
685
|
+
case 'get_query_stats':
|
|
686
|
+
result = get_query_statistics(data.collection);
|
|
687
|
+
break;
|
|
688
|
+
|
|
689
|
+
case 'evaluate_auto_indexes':
|
|
690
|
+
result = await force_index_evaluation(data.collection);
|
|
691
|
+
break;
|
|
692
|
+
|
|
693
|
+
case 'remove_auto_indexes':
|
|
694
|
+
if (!data.collection) {
|
|
695
|
+
throw new Error('collection is required for remove_auto_indexes operation');
|
|
696
|
+
}
|
|
697
|
+
result = await remove_automatic_indexes(data.collection, data.field_names);
|
|
698
|
+
break;
|
|
699
|
+
|
|
700
|
+
case 'create_index':
|
|
701
|
+
if (!data.collection || !data.field) {
|
|
702
|
+
throw new Error('collection and field are required for create_index operation');
|
|
703
|
+
}
|
|
704
|
+
result = await create_index(data.database || 'default', data.collection, data.field, data.options);
|
|
705
|
+
break;
|
|
706
|
+
|
|
707
|
+
case 'drop_index':
|
|
708
|
+
if (!data.collection || !data.field) {
|
|
709
|
+
throw new Error('collection and field are required for drop_index operation');
|
|
710
|
+
}
|
|
711
|
+
result = await drop_index(data.database || 'default', data.collection, data.field);
|
|
712
|
+
break;
|
|
713
|
+
|
|
714
|
+
case 'get_indexes':
|
|
715
|
+
if (!data.collection) {
|
|
716
|
+
throw new Error('collection is required for get_indexes operation');
|
|
717
|
+
}
|
|
718
|
+
result = { indexes: get_indexes(data.database || 'default', data.collection) };
|
|
719
|
+
break;
|
|
720
|
+
|
|
721
|
+
case 'get_replication_status':
|
|
722
|
+
const replication_manager = get_replication_manager();
|
|
723
|
+
result = replication_manager.get_replication_status();
|
|
724
|
+
break;
|
|
725
|
+
|
|
726
|
+
case 'add_secondary':
|
|
727
|
+
if (!data.id || !data.ip || !data.port || !data.private_key) {
|
|
728
|
+
throw new Error('id, ip, port, and private_key are required for add_secondary operation');
|
|
729
|
+
}
|
|
730
|
+
const add_replication_manager = get_replication_manager();
|
|
731
|
+
result = await add_replication_manager.add_secondary({
|
|
732
|
+
id: data.id,
|
|
733
|
+
ip: data.ip,
|
|
734
|
+
port: data.port,
|
|
735
|
+
private_key: data.private_key,
|
|
736
|
+
enabled: true
|
|
737
|
+
});
|
|
738
|
+
break;
|
|
739
|
+
|
|
740
|
+
case 'remove_secondary':
|
|
741
|
+
if (!data.secondary_id) {
|
|
742
|
+
throw new Error('secondary_id is required for remove_secondary operation');
|
|
743
|
+
}
|
|
744
|
+
const remove_replication_manager = get_replication_manager();
|
|
745
|
+
result = remove_replication_manager.remove_secondary(data.secondary_id);
|
|
746
|
+
break;
|
|
747
|
+
|
|
748
|
+
case 'sync_secondaries':
|
|
749
|
+
const sync_replication_manager = get_replication_manager();
|
|
750
|
+
result = await sync_replication_manager.sync_secondaries();
|
|
751
|
+
break;
|
|
752
|
+
|
|
753
|
+
case 'get_secondary_health':
|
|
754
|
+
const health_replication_manager = get_replication_manager();
|
|
755
|
+
result = health_replication_manager.get_secondary_health();
|
|
756
|
+
break;
|
|
757
|
+
|
|
758
|
+
case 'get_forwarder_status':
|
|
759
|
+
const write_forwarder = get_write_forwarder();
|
|
760
|
+
result = write_forwarder.get_forwarder_status();
|
|
761
|
+
break;
|
|
762
|
+
|
|
763
|
+
default:
|
|
764
|
+
// Default admin action (backward compatibility)
|
|
765
|
+
result = {
|
|
766
|
+
...get_enhanced_stats(),
|
|
767
|
+
connections: connection_manager?.get_stats() || {},
|
|
768
|
+
write_queue: get_write_queue()?.get_stats() || {},
|
|
769
|
+
authentication: {
|
|
770
|
+
authenticated_clients: authenticated_clients?.size || 0,
|
|
771
|
+
...get_auth_stats()
|
|
772
|
+
},
|
|
773
|
+
settings: (() => {
|
|
774
|
+
try {
|
|
775
|
+
const settings = get_settings();
|
|
776
|
+
return {
|
|
777
|
+
port: settings.port || 1983
|
|
778
|
+
};
|
|
779
|
+
} catch (error) {
|
|
780
|
+
return { port: 1983 };
|
|
781
|
+
}
|
|
782
|
+
})()
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
const duration_ms = Date.now() - start_time;
|
|
787
|
+
track_operation(duration_ms);
|
|
788
|
+
|
|
789
|
+
log.info('Admin operation completed', {
|
|
790
|
+
admin_action: admin_action || 'default',
|
|
791
|
+
duration_ms,
|
|
792
|
+
status: 'success'
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
return result;
|
|
796
|
+
} catch (error) {
|
|
797
|
+
const duration_ms = Date.now() - start_time;
|
|
798
|
+
|
|
799
|
+
log.error('Admin operation failed', {
|
|
800
|
+
admin_action: admin_action || 'default',
|
|
801
|
+
duration_ms,
|
|
802
|
+
status: 'error',
|
|
803
|
+
error: error.message
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
throw error;
|
|
807
|
+
}
|
|
808
|
+
};
|