@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
package/README.md
ADDED
|
@@ -0,0 +1,1821 @@
|
|
|
1
|
+
# JoystickDB
|
|
2
|
+
|
|
3
|
+
A high-performance, production-ready TCP-based database server built on LMDB with comprehensive client libraries, multi-database support, replication, and enterprise features for Node.js applications.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Features](#features)
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Quick Start](#quick-start)
|
|
11
|
+
- [Server Setup](#server-setup)
|
|
12
|
+
- [Client Usage](#client-usage)
|
|
13
|
+
- [Multi-Database Operations](#multi-database-operations)
|
|
14
|
+
- [Database Operations](#database-operations)
|
|
15
|
+
- [Indexing](#indexing)
|
|
16
|
+
- [HTTP API](#http-api)
|
|
17
|
+
- [Replication](#replication)
|
|
18
|
+
- [Administration](#administration)
|
|
19
|
+
- [Backup & Restore](#backup--restore)
|
|
20
|
+
- [Configuration](#configuration)
|
|
21
|
+
- [Production Deployment](#production-deployment)
|
|
22
|
+
- [API Reference](#api-reference)
|
|
23
|
+
|
|
24
|
+
## Overview
|
|
25
|
+
|
|
26
|
+
JoystickDB is a lightweight, blazing-fast database server that provides MongoDB-like operations over TCP using MessagePack for efficient data serialization. Built on top of LMDB (Lightning Memory-Mapped Database), it offers ACID transactions, high performance, minimal resource usage, and enterprise-grade features like multi-database support, replication, and automatic backups.
|
|
27
|
+
|
|
28
|
+
**Think of it as**: A simpler, faster alternative to MongoDB that's perfect for applications that need reliable data storage without the complexity of a full database cluster, now with full multi-database support for better data organization.
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
### Core Database Features
|
|
33
|
+
- **Multi-Database Support**: Organize data across multiple isolated databases with MongoDB-like chaining API
|
|
34
|
+
- **High Performance**: Built on LMDB with MessagePack serialization for maximum speed
|
|
35
|
+
- **MongoDB-like API**: Familiar CRUD operations and query syntax - easy to learn
|
|
36
|
+
- **ACID Transactions**: Your data is always consistent and safe
|
|
37
|
+
- **TCP Protocol**: Efficient binary communication over the network
|
|
38
|
+
- **Automatic Indexing**: Smart index creation based on your query patterns
|
|
39
|
+
- **Secondary Indexes**: Create custom indexes for lightning-fast queries
|
|
40
|
+
- **Database Isolation**: Complete data separation between databases with proper key namespacing
|
|
41
|
+
|
|
42
|
+
### Production & Enterprise Features
|
|
43
|
+
- **API Key Authentication**: Secure API key-based authentication system with user management
|
|
44
|
+
- **HTTP API**: RESTful HTTP endpoints for user management and external integrations
|
|
45
|
+
- **TCP Replication**: Master-slave replication for high availability and read scaling
|
|
46
|
+
- **Password Authentication**: Secure bcrypt password hashing with rate limiting
|
|
47
|
+
- **Role-Based Access Control**: Read, write, and read_write user roles
|
|
48
|
+
- **Clustering**: Multi-process clustering support for better performance
|
|
49
|
+
- **Backup & Restore**: Automatic S3-compatible backup system with scheduling
|
|
50
|
+
- **Admin Interface**: Comprehensive administrative operations and monitoring
|
|
51
|
+
- **Connection Pooling**: Efficient connection management and resource usage
|
|
52
|
+
- **Health Monitoring**: Built-in performance metrics and health checks
|
|
53
|
+
- **Write Serialization**: Ensures data consistency under high concurrent load
|
|
54
|
+
|
|
55
|
+
### Developer Experience
|
|
56
|
+
- **Fluent API**: Chain methods for intuitive database operations
|
|
57
|
+
- **Multi-Database Chaining**: `client.db('database').collection('collection')` API pattern
|
|
58
|
+
- **Backward Compatibility**: Existing single-database API still supported
|
|
59
|
+
- **Auto-Reconnection**: Client automatically reconnects on network issues
|
|
60
|
+
- **TypeScript Support**: Full TypeScript definitions included
|
|
61
|
+
- **Comprehensive Logging**: Detailed logs for debugging and monitoring
|
|
62
|
+
- **Easy Setup**: Get started in minutes with minimal configuration
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install joystick-db
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Quick Start
|
|
71
|
+
|
|
72
|
+
### 1. Start the Server
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Start with default settings (port 1983)
|
|
76
|
+
npm start
|
|
77
|
+
|
|
78
|
+
# Or with custom port
|
|
79
|
+
PORT=3000 npm start
|
|
80
|
+
|
|
81
|
+
# With clustering (uses all CPU cores)
|
|
82
|
+
WORKER_COUNT=4 npm start
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 2. Setup Authentication (One-time)
|
|
86
|
+
|
|
87
|
+
When you first start JoystickDB, it automatically generates a secure API key and displays setup instructions:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
JoystickDB Setup
|
|
91
|
+
|
|
92
|
+
To finish setting up your database and enable operations for authenticated users, you will need to create an admin user via the database's admin API using the API key displayed below.
|
|
93
|
+
|
|
94
|
+
=== STORE THIS KEY SECURELY -- IT WILL NOT BE DISPLAYED AGAIN ===
|
|
95
|
+
USGwrwK36RM97xiWs6Df1yFuxPLfo4aY
|
|
96
|
+
===
|
|
97
|
+
|
|
98
|
+
To create a user, send a POST request to the server:
|
|
99
|
+
|
|
100
|
+
fetch('http://localhost:1984/api/users', {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers: {
|
|
103
|
+
'Content-Type': 'application/json',
|
|
104
|
+
'x-joystick-db-api-key': 'USGwrwK36RM97xiWs6Df1yFuxPLfo4aY'
|
|
105
|
+
},
|
|
106
|
+
body: JSON.stringify({
|
|
107
|
+
username: 'admin',
|
|
108
|
+
password: 'your_secure_password',
|
|
109
|
+
role: 'read_write'
|
|
110
|
+
})
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Important**: Save the API key securely - it will not be displayed again and is required for user management operations.
|
|
115
|
+
|
|
116
|
+
### 3. Create Your First Admin User
|
|
117
|
+
|
|
118
|
+
Use the API key to create an admin user via HTTP:
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
// Create admin user via HTTP API
|
|
122
|
+
const response = await fetch('http://localhost:1983/api/users', {
|
|
123
|
+
method: 'POST',
|
|
124
|
+
headers: {
|
|
125
|
+
'Content-Type': 'application/json',
|
|
126
|
+
'x-joystick-db-api-key': 'your-api-key-from-step-2'
|
|
127
|
+
},
|
|
128
|
+
body: JSON.stringify({
|
|
129
|
+
username: 'admin',
|
|
130
|
+
password: 'your_secure_password',
|
|
131
|
+
role: 'read_write'
|
|
132
|
+
})
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const result = await response.json();
|
|
136
|
+
console.log('✅ Admin user created:', result);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 4. Connect and Use Your Database
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
import joystickdb from 'joystick-db';
|
|
143
|
+
|
|
144
|
+
const client = joystickdb.client({
|
|
145
|
+
port: 1983,
|
|
146
|
+
authentication: {
|
|
147
|
+
username: 'admin',
|
|
148
|
+
password: 'your_secure_password'
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
client.on('authenticated', async () => {
|
|
153
|
+
console.log('🎉 Connected to JoystickDB!');
|
|
154
|
+
|
|
155
|
+
// NEW: Multi-database support with MongoDB-like chaining
|
|
156
|
+
const users_db = client.db('user_management');
|
|
157
|
+
const users = users_db.collection('users');
|
|
158
|
+
|
|
159
|
+
// Insert a document
|
|
160
|
+
const result = await users.insert_one({
|
|
161
|
+
name: 'John Doe',
|
|
162
|
+
email: 'john@example.com',
|
|
163
|
+
age: 30,
|
|
164
|
+
hobbies: ['reading', 'coding', 'gaming']
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
console.log('✅ User created with ID:', result.inserted_id);
|
|
168
|
+
|
|
169
|
+
// Find the document
|
|
170
|
+
const user = await users.find_one({ name: 'John Doe' });
|
|
171
|
+
console.log('👤 Found user:', user);
|
|
172
|
+
|
|
173
|
+
// Update the document
|
|
174
|
+
await users.update_one(
|
|
175
|
+
{ name: 'John Doe' },
|
|
176
|
+
{ $set: { age: 31 }, $push: { hobbies: 'photography' } }
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
console.log('✏️ User updated!');
|
|
180
|
+
|
|
181
|
+
// Work with different databases
|
|
182
|
+
const inventory_db = client.db('inventory');
|
|
183
|
+
const products = inventory_db.collection('products');
|
|
184
|
+
|
|
185
|
+
await products.insert_one({
|
|
186
|
+
name: 'Laptop',
|
|
187
|
+
price: 999.99,
|
|
188
|
+
category: 'electronics'
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
console.log('📦 Product added to inventory database!');
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Server Setup
|
|
196
|
+
|
|
197
|
+
### Basic Server Configuration
|
|
198
|
+
|
|
199
|
+
JoystickDB uses the `JOYSTICK_DB_SETTINGS` environment variable for configuration. Set this to a JSON string containing your settings:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
export JOYSTICK_DB_SETTINGS='{
|
|
203
|
+
"port": 1983,
|
|
204
|
+
"database": {
|
|
205
|
+
"path": "./data",
|
|
206
|
+
"auto_map_size": true
|
|
207
|
+
},
|
|
208
|
+
"authentication": {
|
|
209
|
+
"rate_limit": {
|
|
210
|
+
"max_attempts": 5,
|
|
211
|
+
"window_ms": 300000
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
"replication": {
|
|
215
|
+
"enabled": false,
|
|
216
|
+
"mode": "async",
|
|
217
|
+
"secondaries": []
|
|
218
|
+
},
|
|
219
|
+
"backup": {
|
|
220
|
+
"enabled": true,
|
|
221
|
+
"schedule": "0 2 * * *",
|
|
222
|
+
"retention_days": 30
|
|
223
|
+
},
|
|
224
|
+
"s3": {
|
|
225
|
+
"region": "us-east-1",
|
|
226
|
+
"bucket": "my-database-backups"
|
|
227
|
+
}
|
|
228
|
+
}'
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Environment Variables
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
# Core server configuration via JSON
|
|
235
|
+
JOYSTICK_DB_SETTINGS='{"port": 1983, "database": {"path": "./data"}}'
|
|
236
|
+
|
|
237
|
+
# Additional server options
|
|
238
|
+
WORKER_COUNT=4 # Number of worker processes
|
|
239
|
+
NODE_ENV=production # Environment mode
|
|
240
|
+
|
|
241
|
+
# AWS S3 for backups (optional)
|
|
242
|
+
AWS_REGION=us-east-1
|
|
243
|
+
AWS_ACCESS_KEY_ID=your-access-key
|
|
244
|
+
AWS_SECRET_ACCESS_KEY=your-secret-key
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Programmatic Server Creation
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
250
|
+
import { create_server } from 'joystick-db/server';
|
|
251
|
+
|
|
252
|
+
const server = await create_server();
|
|
253
|
+
|
|
254
|
+
server.listen(1983, () => {
|
|
255
|
+
console.log('🚀 JoystickDB server running on port 1983');
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Graceful shutdown
|
|
259
|
+
process.on('SIGTERM', async () => {
|
|
260
|
+
console.log('🛑 Shutting down gracefully...');
|
|
261
|
+
await server.cleanup();
|
|
262
|
+
server.close();
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Client Usage
|
|
267
|
+
|
|
268
|
+
### Connection Options
|
|
269
|
+
|
|
270
|
+
```javascript
|
|
271
|
+
const client = joystickdb.client({
|
|
272
|
+
host: 'localhost', // Server hostname
|
|
273
|
+
port: 1983, // Server port
|
|
274
|
+
timeout: 5000, // Request timeout (5 seconds)
|
|
275
|
+
reconnect: true, // Auto-reconnect on disconnect
|
|
276
|
+
max_reconnect_attempts: 10, // Max reconnection attempts
|
|
277
|
+
reconnect_delay: 1000, // Initial reconnect delay (1 second)
|
|
278
|
+
authentication: {
|
|
279
|
+
username: 'your-username', // Your database username
|
|
280
|
+
password: 'your-password' // Your database password
|
|
281
|
+
},
|
|
282
|
+
auto_connect: true // Connect automatically when created
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Event Handling
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
client.on('connect', () => {
|
|
290
|
+
console.log('🔌 Connected to JoystickDB');
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
client.on('authenticated', () => {
|
|
294
|
+
console.log('🔐 Authentication successful - ready to use!');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
client.on('error', (error) => {
|
|
298
|
+
console.error('❌ Client error:', error.message);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
client.on('disconnect', () => {
|
|
302
|
+
console.log('📡 Disconnected from server');
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
client.on('reconnecting', ({ attempt, delay }) => {
|
|
306
|
+
console.log(`🔄 Reconnecting... attempt ${attempt}, delay ${delay}ms`);
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Multi-Database Operations
|
|
311
|
+
|
|
312
|
+
JoystickDB now supports multiple isolated databases, allowing you to organize your data more effectively. Each database maintains complete isolation with separate collections, indexes, and storage.
|
|
313
|
+
|
|
314
|
+
### Multi-Database API
|
|
315
|
+
|
|
316
|
+
JoystickDB uses MongoDB's familiar chaining pattern for multi-database operations:
|
|
317
|
+
|
|
318
|
+
```javascript
|
|
319
|
+
// Connect to different databases
|
|
320
|
+
const user_db = client.db('user_management');
|
|
321
|
+
const inventory_db = client.db('inventory');
|
|
322
|
+
const analytics_db = client.db('analytics');
|
|
323
|
+
|
|
324
|
+
// Each database has its own collections
|
|
325
|
+
const users = user_db.collection('users');
|
|
326
|
+
const profiles = user_db.collection('profiles');
|
|
327
|
+
|
|
328
|
+
const products = inventory_db.collection('products');
|
|
329
|
+
const categories = inventory_db.collection('categories');
|
|
330
|
+
|
|
331
|
+
const events = analytics_db.collection('events');
|
|
332
|
+
const reports = analytics_db.collection('reports');
|
|
333
|
+
|
|
334
|
+
// Operations are completely isolated between databases
|
|
335
|
+
await users.insert_one({ name: 'John', role: 'admin' });
|
|
336
|
+
await products.insert_one({ name: 'Laptop', price: 999 });
|
|
337
|
+
await events.insert_one({ type: 'login', user_id: 'john123' });
|
|
338
|
+
|
|
339
|
+
// Each database maintains separate indexes and statistics
|
|
340
|
+
await users.create_index('email');
|
|
341
|
+
await products.create_index('category');
|
|
342
|
+
await events.create_index('timestamp');
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Database Management Operations
|
|
346
|
+
|
|
347
|
+
```javascript
|
|
348
|
+
// List all databases on the server
|
|
349
|
+
const databases = await client.list_databases();
|
|
350
|
+
console.log('📚 Available databases:', databases.databases);
|
|
351
|
+
|
|
352
|
+
// Get database-specific statistics
|
|
353
|
+
const user_db = client.db('user_management');
|
|
354
|
+
const stats = await user_db.get_stats();
|
|
355
|
+
console.log('📊 User DB stats:', stats);
|
|
356
|
+
|
|
357
|
+
// List collections in a specific database
|
|
358
|
+
const collections = await user_db.list_collections();
|
|
359
|
+
console.log('📋 Collections in user_management:', collections.collections);
|
|
360
|
+
|
|
361
|
+
// Create a collection explicitly (optional - collections are created automatically)
|
|
362
|
+
await user_db.create_collection('audit_logs', {
|
|
363
|
+
// Collection options can be specified here
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Drop an entire database (admin operation - use with caution!)
|
|
367
|
+
await user_db.drop_database();
|
|
368
|
+
console.log('🗑️ Database dropped');
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Database Naming Rules
|
|
372
|
+
|
|
373
|
+
Database names must follow these rules:
|
|
374
|
+
|
|
375
|
+
- Must be 1-64 characters long
|
|
376
|
+
- Can contain letters, numbers, underscores, and hyphens
|
|
377
|
+
- Cannot start with a number
|
|
378
|
+
- Cannot be reserved names: `admin`, `config`, `local`
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
// Valid database names
|
|
382
|
+
const app_db = client.db('my_app');
|
|
383
|
+
const user_data = client.db('user-data');
|
|
384
|
+
const analytics2024 = client.db('analytics_2024');
|
|
385
|
+
|
|
386
|
+
// Invalid database names (will throw errors)
|
|
387
|
+
// client.db('admin'); // Reserved name
|
|
388
|
+
// client.db('123invalid'); // Starts with number
|
|
389
|
+
// client.db(''); // Empty name
|
|
390
|
+
// client.db('a'.repeat(65)); // Too long
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
## Database Operations
|
|
395
|
+
|
|
396
|
+
JoystickDB provides a clean, MongoDB-like chaining API for multi-database operations.
|
|
397
|
+
|
|
398
|
+
### Multi-Database Chaining API
|
|
399
|
+
|
|
400
|
+
Work with JoystickDB across multiple databases using the intuitive chaining pattern:
|
|
401
|
+
|
|
402
|
+
```javascript
|
|
403
|
+
// Get database and collection references
|
|
404
|
+
const user_db = client.db('user_management');
|
|
405
|
+
const inventory_db = client.db('inventory');
|
|
406
|
+
|
|
407
|
+
const users = user_db.collection('users');
|
|
408
|
+
const products = inventory_db.collection('products');
|
|
409
|
+
|
|
410
|
+
// Insert documents into different databases
|
|
411
|
+
const user = await users.insert_one({
|
|
412
|
+
name: 'Alice Smith',
|
|
413
|
+
email: 'alice@example.com',
|
|
414
|
+
age: 28,
|
|
415
|
+
tags: ['developer', 'javascript'],
|
|
416
|
+
created_at: new Date()
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const product = await products.insert_one({
|
|
420
|
+
name: 'Wireless Mouse',
|
|
421
|
+
price: 29.99,
|
|
422
|
+
category: 'electronics',
|
|
423
|
+
stock: 150
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
console.log('👤 Created user in user_management DB:', user.inserted_id);
|
|
427
|
+
console.log('📦 Created product in inventory DB:', product.inserted_id);
|
|
428
|
+
|
|
429
|
+
// Bulk operations across databases
|
|
430
|
+
const user_bulk = await users.bulk_write([
|
|
431
|
+
{ insert_one: { document: { name: 'Bob', age: 25, role: 'designer' } } },
|
|
432
|
+
{ insert_one: { document: { name: 'Carol', age: 30, role: 'manager' } } },
|
|
433
|
+
{ insert_one: { document: { name: 'Dave', age: 35, role: 'developer' } } }
|
|
434
|
+
]);
|
|
435
|
+
|
|
436
|
+
const product_bulk = await products.bulk_write([
|
|
437
|
+
{ insert_one: { document: { name: 'Keyboard', price: 79.99, category: 'electronics' } } },
|
|
438
|
+
{ insert_one: { document: { name: 'Monitor', price: 299.99, category: 'electronics' } } }
|
|
439
|
+
]);
|
|
440
|
+
|
|
441
|
+
console.log('👥 Created users:', user_bulk.inserted_count);
|
|
442
|
+
console.log('📦 Created products:', product_bulk.inserted_count);
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### Finding Documents Across Databases
|
|
446
|
+
|
|
447
|
+
```javascript
|
|
448
|
+
const user_db = client.db('user_management');
|
|
449
|
+
const inventory_db = client.db('inventory');
|
|
450
|
+
|
|
451
|
+
const users = user_db.collection('users');
|
|
452
|
+
const products = inventory_db.collection('products');
|
|
453
|
+
|
|
454
|
+
// Find in user database
|
|
455
|
+
const alice = await users.find_one({ name: 'Alice Smith' });
|
|
456
|
+
console.log('Found Alice in user DB:', alice);
|
|
457
|
+
|
|
458
|
+
// Find in inventory database
|
|
459
|
+
const electronics = await products.find({
|
|
460
|
+
category: 'electronics',
|
|
461
|
+
price: { $lte: 100 }
|
|
462
|
+
});
|
|
463
|
+
console.log('Found affordable electronics:', electronics.documents);
|
|
464
|
+
|
|
465
|
+
// Each database maintains separate query performance
|
|
466
|
+
const recent_users = await users.find({}, {
|
|
467
|
+
sort: { created_at: -1 },
|
|
468
|
+
limit: 10
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const expensive_products = await products.find({
|
|
472
|
+
price: { $gte: 200 }
|
|
473
|
+
}, {
|
|
474
|
+
sort: { price: -1 },
|
|
475
|
+
limit: 5
|
|
476
|
+
});
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Updating and Deleting Across Databases
|
|
480
|
+
|
|
481
|
+
```javascript
|
|
482
|
+
const user_db = client.db('user_management');
|
|
483
|
+
const inventory_db = client.db('inventory');
|
|
484
|
+
|
|
485
|
+
const users = user_db.collection('users');
|
|
486
|
+
const products = inventory_db.collection('products');
|
|
487
|
+
|
|
488
|
+
// Update in user database
|
|
489
|
+
await users.update_one(
|
|
490
|
+
{ name: 'Alice Smith' },
|
|
491
|
+
{
|
|
492
|
+
$set: {
|
|
493
|
+
age: 29,
|
|
494
|
+
last_updated: new Date(),
|
|
495
|
+
status: 'active'
|
|
496
|
+
},
|
|
497
|
+
$push: { tags: 'senior' }
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
// Update in inventory database
|
|
502
|
+
await products.update_one(
|
|
503
|
+
{ name: 'Wireless Mouse' },
|
|
504
|
+
{
|
|
505
|
+
$set: { price: 24.99 },
|
|
506
|
+
$inc: { stock: -5 }
|
|
507
|
+
}
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
// Delete from user database
|
|
511
|
+
await users.delete_one({
|
|
512
|
+
status: 'inactive',
|
|
513
|
+
last_login: { $lt: new Date(Date.now() - 365*24*60*60*1000) }
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// Delete from inventory database
|
|
517
|
+
await products.delete_one({
|
|
518
|
+
stock: { $lte: 0 },
|
|
519
|
+
discontinued: true
|
|
520
|
+
});
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
### Query Operators (MongoDB-style)
|
|
525
|
+
|
|
526
|
+
All query operators work consistently across all databases:
|
|
527
|
+
|
|
528
|
+
```javascript
|
|
529
|
+
const user_db = client.db('user_management');
|
|
530
|
+
const inventory_db = client.db('inventory');
|
|
531
|
+
|
|
532
|
+
const users = user_db.collection('users');
|
|
533
|
+
const products = inventory_db.collection('products');
|
|
534
|
+
|
|
535
|
+
// Comparison operators work in any database
|
|
536
|
+
await users.find({ age: { $gt: 25 } });
|
|
537
|
+
await products.find({ price: { $gte: 50, $lte: 200 } });
|
|
538
|
+
|
|
539
|
+
// Text matching with regular expressions
|
|
540
|
+
await users.find({
|
|
541
|
+
name: { $regex: '^John' }
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
await products.find({
|
|
545
|
+
name: { $regex: 'wireless', $options: 'i' } // Case insensitive
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
// Array operations
|
|
549
|
+
await users.find({
|
|
550
|
+
tags: { $in: ['developer', 'designer'] }
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
await products.find({
|
|
554
|
+
categories: { $all: ['electronics', 'accessories'] }
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// Complex queries across databases
|
|
558
|
+
await users.find({
|
|
559
|
+
age: { $gte: 25, $lte: 35 },
|
|
560
|
+
tags: { $in: ['developer', 'designer'] },
|
|
561
|
+
status: 'active',
|
|
562
|
+
name: { $regex: 'Smith$' }
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
await products.find({
|
|
566
|
+
price: { $gte: 100 },
|
|
567
|
+
category: 'electronics',
|
|
568
|
+
stock: { $gt: 0 },
|
|
569
|
+
name: { $regex: 'pro', $options: 'i' }
|
|
570
|
+
});
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
## Indexing
|
|
574
|
+
|
|
575
|
+
Indexes work independently within each database, providing optimal performance for database-specific query patterns.
|
|
576
|
+
|
|
577
|
+
### Automatic Indexing (Per Database)
|
|
578
|
+
|
|
579
|
+
JoystickDB automatically creates indexes based on query patterns within each database:
|
|
580
|
+
|
|
581
|
+
```javascript
|
|
582
|
+
const user_db = client.db('user_management');
|
|
583
|
+
const inventory_db = client.db('inventory');
|
|
584
|
+
|
|
585
|
+
const users = user_db.collection('users');
|
|
586
|
+
const products = inventory_db.collection('products');
|
|
587
|
+
|
|
588
|
+
// First query in user database - might be slow
|
|
589
|
+
const user1 = await users.find_one({ email: 'john@example.com' });
|
|
590
|
+
|
|
591
|
+
// JoystickDB creates index for user database email queries
|
|
592
|
+
const user2 = await users.find_one({ email: 'jane@example.com' }); // Much faster!
|
|
593
|
+
|
|
594
|
+
// Separate automatic indexing for inventory database
|
|
595
|
+
const product1 = await products.find_one({ sku: 'MOUSE001' });
|
|
596
|
+
const product2 = await products.find_one({ sku: 'KEYBOARD002' }); // Auto-indexed!
|
|
597
|
+
|
|
598
|
+
// Each database maintains its own automatic indexing statistics
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### Manual Index Management (Per Database)
|
|
602
|
+
|
|
603
|
+
Create and manage indexes independently for each database:
|
|
604
|
+
|
|
605
|
+
```javascript
|
|
606
|
+
const user_db = client.db('user_management');
|
|
607
|
+
const inventory_db = client.db('inventory');
|
|
608
|
+
|
|
609
|
+
const users = user_db.collection('users');
|
|
610
|
+
const products = inventory_db.collection('products');
|
|
611
|
+
|
|
612
|
+
// Create indexes in user database
|
|
613
|
+
await users.create_index('email');
|
|
614
|
+
await users.create_index('username', { unique: true });
|
|
615
|
+
console.log('📇 User database indexes created');
|
|
616
|
+
|
|
617
|
+
// Create indexes in inventory database
|
|
618
|
+
await products.create_index('sku', { unique: true });
|
|
619
|
+
await products.create_index('category');
|
|
620
|
+
await products.create_index('price');
|
|
621
|
+
console.log('📇 Inventory database indexes created');
|
|
622
|
+
|
|
623
|
+
// List indexes for each database separately
|
|
624
|
+
const user_indexes = await users.get_indexes();
|
|
625
|
+
const product_indexes = await products.get_indexes();
|
|
626
|
+
|
|
627
|
+
console.log('📋 User DB indexes:', user_indexes.indexes);
|
|
628
|
+
console.log('📋 Inventory DB indexes:', product_indexes.indexes);
|
|
629
|
+
|
|
630
|
+
// Drop indexes independently
|
|
631
|
+
await users.drop_index('email');
|
|
632
|
+
await products.drop_index('category');
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### Cross-Database Index Isolation
|
|
636
|
+
|
|
637
|
+
Indexes are completely isolated between databases:
|
|
638
|
+
|
|
639
|
+
```javascript
|
|
640
|
+
const db1 = client.db('database1');
|
|
641
|
+
const db2 = client.db('database2');
|
|
642
|
+
|
|
643
|
+
const collection1 = db1.collection('items');
|
|
644
|
+
const collection2 = db2.collection('items');
|
|
645
|
+
|
|
646
|
+
// Create index in database1 only
|
|
647
|
+
await collection1.create_index('name');
|
|
648
|
+
|
|
649
|
+
// This collection in database2 has no indexes yet
|
|
650
|
+
const items1 = await collection1.find({ name: 'test' }); // Uses index
|
|
651
|
+
const items2 = await collection2.find({ name: 'test' }); // No index, full scan
|
|
652
|
+
|
|
653
|
+
// Create separate index in database2
|
|
654
|
+
await collection2.create_index('name');
|
|
655
|
+
|
|
656
|
+
// Now both have independent indexes
|
|
657
|
+
const items1_fast = await collection1.find({ name: 'test' }); // Uses db1 index
|
|
658
|
+
const items2_fast = await collection2.find({ name: 'test' }); // Uses db2 index
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### Index Performance Benefits
|
|
662
|
+
|
|
663
|
+
```javascript
|
|
664
|
+
const inventory_db = client.db('inventory');
|
|
665
|
+
const products = inventory_db.collection('products');
|
|
666
|
+
|
|
667
|
+
// Without index: Searches through ALL products (slow for large datasets)
|
|
668
|
+
const expensive_products = await products.find({ price: { $gte: 1000 } });
|
|
669
|
+
|
|
670
|
+
// Create index on price
|
|
671
|
+
await products.create_index('price');
|
|
672
|
+
|
|
673
|
+
// With index: Jumps directly to expensive products (super fast!)
|
|
674
|
+
const expensive_products_fast = await products.find({ price: { $gte: 1000 } });
|
|
675
|
+
|
|
676
|
+
// Compound queries benefit from multiple indexes
|
|
677
|
+
await products.create_index('category');
|
|
678
|
+
const gaming_laptops = await products.find({
|
|
679
|
+
category: 'laptops',
|
|
680
|
+
price: { $gte: 800 }
|
|
681
|
+
}); // Uses both category and price indexes
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
## HTTP API
|
|
685
|
+
|
|
686
|
+
JoystickDB provides RESTful HTTP endpoints for user management and external integrations. The HTTP API uses API key authentication for secure access.
|
|
687
|
+
|
|
688
|
+
### Authentication
|
|
689
|
+
|
|
690
|
+
All HTTP API requests require an API key in the `x-joystick-db-api-key` header:
|
|
691
|
+
|
|
692
|
+
```javascript
|
|
693
|
+
const headers = {
|
|
694
|
+
'Content-Type': 'application/json',
|
|
695
|
+
'x-joystick-db-api-key': 'your-api-key-here'
|
|
696
|
+
};
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### User Management Endpoints
|
|
700
|
+
|
|
701
|
+
#### Create User
|
|
702
|
+
|
|
703
|
+
Create a new user with username, password, and role.
|
|
704
|
+
|
|
705
|
+
```http
|
|
706
|
+
POST /api/users
|
|
707
|
+
Content-Type: application/json
|
|
708
|
+
x-joystick-db-api-key: your-api-key
|
|
709
|
+
|
|
710
|
+
{
|
|
711
|
+
"username": "john_doe",
|
|
712
|
+
"password": "secure_password123",
|
|
713
|
+
"role": "read_write"
|
|
714
|
+
}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
**Response:**
|
|
718
|
+
```json
|
|
719
|
+
{
|
|
720
|
+
"success": true,
|
|
721
|
+
"message": "User created successfully",
|
|
722
|
+
"username": "john_doe",
|
|
723
|
+
"role": "read_write"
|
|
724
|
+
}
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
**Roles:**
|
|
728
|
+
- `read` - Can only read data from all databases
|
|
729
|
+
- `write` - Can only write data to all databases
|
|
730
|
+
- `read_write` - Can read and write data to all databases (admin privileges)
|
|
731
|
+
|
|
732
|
+
#### Get User
|
|
733
|
+
|
|
734
|
+
Retrieve user information by username.
|
|
735
|
+
|
|
736
|
+
```http
|
|
737
|
+
GET /api/users?username=john_doe
|
|
738
|
+
x-joystick-db-api-key: your-api-key
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
**Response:**
|
|
742
|
+
```json
|
|
743
|
+
{
|
|
744
|
+
"success": true,
|
|
745
|
+
"user": {
|
|
746
|
+
"username": "john_doe",
|
|
747
|
+
"role": "read_write",
|
|
748
|
+
"created_at": "2024-01-15T10:30:00.000Z"
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
#### Update User
|
|
754
|
+
|
|
755
|
+
Update user password or role.
|
|
756
|
+
|
|
757
|
+
```http
|
|
758
|
+
PUT /api/users
|
|
759
|
+
Content-Type: application/json
|
|
760
|
+
x-joystick-db-api-key: your-api-key
|
|
761
|
+
|
|
762
|
+
{
|
|
763
|
+
"username": "john_doe",
|
|
764
|
+
"password": "new_secure_password456",
|
|
765
|
+
"role": "read"
|
|
766
|
+
}
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
**Response:**
|
|
770
|
+
```json
|
|
771
|
+
{
|
|
772
|
+
"success": true,
|
|
773
|
+
"message": "User updated successfully",
|
|
774
|
+
"username": "john_doe",
|
|
775
|
+
"updates": ["password", "role"]
|
|
776
|
+
}
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
#### Delete User
|
|
780
|
+
|
|
781
|
+
Remove a user from the system.
|
|
782
|
+
|
|
783
|
+
```http
|
|
784
|
+
DELETE /api/users?username=john_doe
|
|
785
|
+
x-joystick-db-api-key: your-api-key
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
**Response:**
|
|
789
|
+
```json
|
|
790
|
+
{
|
|
791
|
+
"success": true,
|
|
792
|
+
"message": "User deleted successfully",
|
|
793
|
+
"username": "john_doe"
|
|
794
|
+
}
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### HTTP API Examples
|
|
798
|
+
|
|
799
|
+
#### JavaScript/Node.js
|
|
800
|
+
|
|
801
|
+
```javascript
|
|
802
|
+
const API_KEY = 'your-api-key-here';
|
|
803
|
+
const BASE_URL = 'http://localhost:1983';
|
|
804
|
+
|
|
805
|
+
// Create user
|
|
806
|
+
const create_user = async (username, password, role) => {
|
|
807
|
+
const response = await fetch(`${BASE_URL}/api/users`, {
|
|
808
|
+
method: 'POST',
|
|
809
|
+
headers: {
|
|
810
|
+
'Content-Type': 'application/json',
|
|
811
|
+
'x-joystick-db-api-key': API_KEY
|
|
812
|
+
},
|
|
813
|
+
body: JSON.stringify({ username, password, role })
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
return await response.json();
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
// Get user
|
|
820
|
+
const get_user = async (username) => {
|
|
821
|
+
const response = await fetch(`${BASE_URL}/api/users?username=${username}`, {
|
|
822
|
+
headers: {
|
|
823
|
+
'x-joystick-db-api-key': API_KEY
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
return await response.json();
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
// Update user
|
|
831
|
+
const update_user = async (username, updates) => {
|
|
832
|
+
const response = await fetch(`${BASE_URL}/api/users`, {
|
|
833
|
+
method: 'PUT',
|
|
834
|
+
headers: {
|
|
835
|
+
'Content-Type': 'application/json',
|
|
836
|
+
'x-joystick-db-api-key': API_KEY
|
|
837
|
+
},
|
|
838
|
+
body: JSON.stringify({ username, ...updates })
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
return await response.json();
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
// Delete user
|
|
845
|
+
const delete_user = async (username) => {
|
|
846
|
+
const response = await fetch(`${BASE_URL}/api/users?username=${username}`, {
|
|
847
|
+
method: 'DELETE',
|
|
848
|
+
headers: {
|
|
849
|
+
'x-joystick-db-api-key': API_KEY
|
|
850
|
+
}
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
return await response.json();
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
// Usage examples
|
|
857
|
+
const demo = async () => {
|
|
858
|
+
// Create a new user
|
|
859
|
+
const create_result = await create_user('alice', 'secure123', 'read_write');
|
|
860
|
+
console.log('User created:', create_result);
|
|
861
|
+
|
|
862
|
+
// Get user info
|
|
863
|
+
const user_info = await get_user('alice');
|
|
864
|
+
console.log('User info:', user_info);
|
|
865
|
+
|
|
866
|
+
// Update user role
|
|
867
|
+
const update_result = await update_user('alice', { role: 'read' });
|
|
868
|
+
console.log('User updated:', update_result);
|
|
869
|
+
|
|
870
|
+
// Delete user
|
|
871
|
+
const delete_result = await delete_user('alice');
|
|
872
|
+
console.log('User deleted:', delete_result);
|
|
873
|
+
};
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
#### cURL Examples
|
|
877
|
+
|
|
878
|
+
```bash
|
|
879
|
+
# Create user
|
|
880
|
+
curl -X POST http://localhost:1983/api/users \
|
|
881
|
+
-H "Content-Type: application/json" \
|
|
882
|
+
-H "x-joystick-db-api-key: your-api-key-here" \
|
|
883
|
+
-d '{"username": "alice", "password": "secure123", "role": "read_write"}'
|
|
884
|
+
|
|
885
|
+
# Get user
|
|
886
|
+
curl -X GET "http://localhost:1983/api/users?username=alice" \
|
|
887
|
+
-H "x-joystick-db-api-key: your-api-key-here"
|
|
888
|
+
|
|
889
|
+
# Update user
|
|
890
|
+
curl -X PUT http://localhost:1983/api/users \
|
|
891
|
+
-H "Content-Type: application/json" \
|
|
892
|
+
-H "x-joystick-db-api-key: your-api-key-here" \
|
|
893
|
+
-d '{"username": "alice", "role": "read"}'
|
|
894
|
+
|
|
895
|
+
# Delete user
|
|
896
|
+
curl -X DELETE "http://localhost:1983/api/users?username=alice" \
|
|
897
|
+
-H "x-joystick-db-api-key: your-api-key-here"
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
### Error Responses
|
|
901
|
+
|
|
902
|
+
The HTTP API returns consistent error responses:
|
|
903
|
+
|
|
904
|
+
```json
|
|
905
|
+
{
|
|
906
|
+
"success": false,
|
|
907
|
+
"error": "Invalid API key"
|
|
908
|
+
}
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
**Common Error Codes:**
|
|
912
|
+
- `400` - Bad Request (invalid data)
|
|
913
|
+
- `401` - Unauthorized (invalid API key)
|
|
914
|
+
- `404` - Not Found (user doesn't exist)
|
|
915
|
+
- `409` - Conflict (user already exists)
|
|
916
|
+
- `500` - Internal Server Error
|
|
917
|
+
|
|
918
|
+
## Replication
|
|
919
|
+
|
|
920
|
+
Replication creates copies of your database on multiple servers for high availability and better performance. Multi-database support is fully compatible with replication.
|
|
921
|
+
|
|
922
|
+
### How Replication Works
|
|
923
|
+
|
|
924
|
+
- **Primary Node**: The main database that handles all writes across all databases
|
|
925
|
+
- **Secondary Nodes**: Read-only copies that stay in sync with the primary for all databases
|
|
926
|
+
- **Write Forwarding**: Secondary nodes can forward write requests to the primary
|
|
927
|
+
- **Automatic Sync**: Changes on primary are automatically copied to secondaries for all databases
|
|
928
|
+
- **Database Isolation**: Replication maintains database separation on all nodes
|
|
929
|
+
|
|
930
|
+
### Setting Up Replication
|
|
931
|
+
|
|
932
|
+
#### 1. Configure Primary Node
|
|
933
|
+
|
|
934
|
+
```bash
|
|
935
|
+
# Primary server configuration via JOYSTICK_DB_SETTINGS
|
|
936
|
+
export JOYSTICK_DB_SETTINGS='{
|
|
937
|
+
"port": 1983,
|
|
938
|
+
"replication": {
|
|
939
|
+
"enabled": true,
|
|
940
|
+
"mode": "async",
|
|
941
|
+
"timeout_ms": 5000,
|
|
942
|
+
"batch_size": 100,
|
|
943
|
+
"secondaries": [
|
|
944
|
+
{
|
|
945
|
+
"id": "secondary-1",
|
|
946
|
+
"ip": "192.168.1.100",
|
|
947
|
+
"port": 1984,
|
|
948
|
+
"private_key": "base64-encoded-private-key",
|
|
949
|
+
"enabled": true
|
|
950
|
+
},
|
|
951
|
+
{
|
|
952
|
+
"id": "secondary-2",
|
|
953
|
+
"ip": "192.168.1.101",
|
|
954
|
+
"port": 1984,
|
|
955
|
+
"private_key": "base64-encoded-private-key",
|
|
956
|
+
"enabled": true
|
|
957
|
+
}
|
|
958
|
+
]
|
|
959
|
+
}
|
|
960
|
+
}'
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
#### 2. Configure Secondary Nodes
|
|
964
|
+
|
|
965
|
+
```bash
|
|
966
|
+
# Secondary server configuration via JOYSTICK_DB_SETTINGS
|
|
967
|
+
export JOYSTICK_DB_SETTINGS='{
|
|
968
|
+
"port": 1984,
|
|
969
|
+
"write_forwarder": {
|
|
970
|
+
"enabled": true,
|
|
971
|
+
"primary": {
|
|
972
|
+
"ip": "192.168.1.10",
|
|
973
|
+
"port": 1983,
|
|
974
|
+
"private_key": "base64-encoded-private-key"
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}'
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
### Managing Replication
|
|
981
|
+
|
|
982
|
+
```javascript
|
|
983
|
+
// Connect to primary node
|
|
984
|
+
const client = joystickdb.client({
|
|
985
|
+
port: 1983,
|
|
986
|
+
authentication: {
|
|
987
|
+
username: 'admin',
|
|
988
|
+
password: 'your-password'
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
// Check replication status
|
|
993
|
+
const status = await client.get_replication_status();
|
|
994
|
+
console.log('🔄 Replication enabled:', status.enabled);
|
|
995
|
+
console.log('📡 Connected secondaries:', status.connected_secondaries);
|
|
996
|
+
console.log('📊 Queue length:', status.queue_length);
|
|
997
|
+
|
|
998
|
+
// Add a new secondary node dynamically
|
|
999
|
+
await client.add_secondary({
|
|
1000
|
+
id: 'secondary-3',
|
|
1001
|
+
ip: '192.168.1.102',
|
|
1002
|
+
port: 1984,
|
|
1003
|
+
private_key: 'base64-encoded-private-key'
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
// Check health of secondary nodes
|
|
1007
|
+
const health = await client.get_secondary_health();
|
|
1008
|
+
console.log('💚 Healthy secondaries:', health.healthy_secondaries);
|
|
1009
|
+
console.log('📈 Total secondaries:', health.total_secondaries);
|
|
1010
|
+
|
|
1011
|
+
// Force synchronization with all secondaries
|
|
1012
|
+
await client.sync_secondaries();
|
|
1013
|
+
console.log('🔄 Forced sync completed');
|
|
1014
|
+
|
|
1015
|
+
// Remove a secondary node
|
|
1016
|
+
await client.remove_secondary('secondary-1');
|
|
1017
|
+
console.log('➖ Secondary removed');
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
### Using Replication for Read Scaling
|
|
1021
|
+
|
|
1022
|
+
```javascript
|
|
1023
|
+
// Connect to primary for writes
|
|
1024
|
+
const primary = joystickdb.client({
|
|
1025
|
+
host: '192.168.1.10',
|
|
1026
|
+
port: 1983,
|
|
1027
|
+
authentication: {
|
|
1028
|
+
username: 'admin',
|
|
1029
|
+
password: 'your-password'
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
// Connect to secondary for reads (better performance)
|
|
1034
|
+
const secondary = joystickdb.client({
|
|
1035
|
+
host: '192.168.1.100',
|
|
1036
|
+
port: 1984,
|
|
1037
|
+
authentication: {
|
|
1038
|
+
username: 'admin',
|
|
1039
|
+
password: 'your-password'
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
// Write to primary (all databases replicated)
|
|
1044
|
+
const user_db = primary.db('user_management');
|
|
1045
|
+
const inventory_db = primary.db('inventory');
|
|
1046
|
+
|
|
1047
|
+
await user_db.collection('users').insert_one({
|
|
1048
|
+
name: 'New User',
|
|
1049
|
+
email: 'new@example.com'
|
|
1050
|
+
});
|
|
1051
|
+
|
|
1052
|
+
await inventory_db.collection('products').insert_one({
|
|
1053
|
+
name: 'New Product',
|
|
1054
|
+
price: 99.99
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
// Read from secondary (reduces load on primary, all databases available)
|
|
1058
|
+
const secondary_user_db = secondary.db('user_management');
|
|
1059
|
+
const secondary_inventory_db = secondary.db('inventory');
|
|
1060
|
+
|
|
1061
|
+
const users = await secondary_user_db.collection('users').find({});
|
|
1062
|
+
const products = await secondary_inventory_db.collection('products').find({});
|
|
1063
|
+
|
|
1064
|
+
console.log('👥 Users from secondary:', users.documents.length);
|
|
1065
|
+
console.log('📦 Products from secondary:', products.documents.length);
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
## Administration
|
|
1069
|
+
|
|
1070
|
+
### Database Statistics and Monitoring
|
|
1071
|
+
|
|
1072
|
+
```javascript
|
|
1073
|
+
// Get comprehensive server statistics
|
|
1074
|
+
const stats = await client.get_stats();
|
|
1075
|
+
|
|
1076
|
+
console.log('🖥️ Server Info:');
|
|
1077
|
+
console.log(' Uptime:', stats.server.uptime_formatted);
|
|
1078
|
+
console.log(' Version:', stats.server.version);
|
|
1079
|
+
console.log(' Node.js:', stats.server.node_version);
|
|
1080
|
+
|
|
1081
|
+
console.log('💾 Memory Usage:');
|
|
1082
|
+
console.log(' Heap Used:', Math.round(stats.memory.heapUsed / 1024 / 1024), 'MB');
|
|
1083
|
+
console.log(' Heap Total:', Math.round(stats.memory.heapTotal / 1024 / 1024), 'MB');
|
|
1084
|
+
|
|
1085
|
+
console.log('🗄️ Database:');
|
|
1086
|
+
console.log(' Size:', stats.database.used_space_mb, 'MB');
|
|
1087
|
+
console.log(' Databases:', stats.database.databases);
|
|
1088
|
+
console.log(' Collections:', stats.database.collections);
|
|
1089
|
+
|
|
1090
|
+
console.log('⚡ Performance:');
|
|
1091
|
+
console.log(' Operations/sec:', stats.performance.ops_per_second);
|
|
1092
|
+
console.log(' Avg Response Time:', stats.performance.avg_response_time_ms, 'ms');
|
|
1093
|
+
|
|
1094
|
+
console.log('🔌 Connections:');
|
|
1095
|
+
console.log(' Active:', stats.connections.active);
|
|
1096
|
+
console.log(' Total:', stats.connections.total);
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
### Multi-Database Administration
|
|
1100
|
+
|
|
1101
|
+
```javascript
|
|
1102
|
+
// List all databases on the server
|
|
1103
|
+
const databases = await client.list_databases();
|
|
1104
|
+
console.log('📚 Available databases:', databases.databases);
|
|
1105
|
+
|
|
1106
|
+
// Get statistics for a specific database
|
|
1107
|
+
const user_db = client.db('user_management');
|
|
1108
|
+
const user_db_stats = await user_db.get_stats();
|
|
1109
|
+
console.log('📊 User management DB stats:', user_db_stats);
|
|
1110
|
+
|
|
1111
|
+
// List collections in each database
|
|
1112
|
+
for (const db_name of databases.databases) {
|
|
1113
|
+
const db = client.db(db_name);
|
|
1114
|
+
const collections = await db.list_collections();
|
|
1115
|
+
console.log(`📋 Collections in ${db_name}:`, collections.collections);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// Create collections explicitly in different databases
|
|
1119
|
+
const analytics_db = client.db('analytics');
|
|
1120
|
+
await analytics_db.create_collection('events');
|
|
1121
|
+
await analytics_db.create_collection('reports');
|
|
1122
|
+
|
|
1123
|
+
const inventory_db = client.db('inventory');
|
|
1124
|
+
await inventory_db.create_collection('products');
|
|
1125
|
+
await inventory_db.create_collection('categories');
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
### Collection and Document Management
|
|
1129
|
+
|
|
1130
|
+
```javascript
|
|
1131
|
+
// List documents across different databases
|
|
1132
|
+
const user_db = client.db('user_management');
|
|
1133
|
+
const inventory_db = client.db('inventory');
|
|
1134
|
+
|
|
1135
|
+
// List documents in user management database
|
|
1136
|
+
const user_docs = await client.list_documents('users', {
|
|
1137
|
+
database: 'user_management',
|
|
1138
|
+
limit: 50,
|
|
1139
|
+
skip: 0,
|
|
1140
|
+
sort_field: 'created_at',
|
|
1141
|
+
sort_order: 'desc'
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
// List documents in inventory database
|
|
1145
|
+
const product_docs = await client.list_documents('products', {
|
|
1146
|
+
database: 'inventory',
|
|
1147
|
+
limit: 50,
|
|
1148
|
+
skip: 0,
|
|
1149
|
+
sort_field: 'price',
|
|
1150
|
+
sort_order: 'asc'
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
console.log('📄 User documents:', user_docs.documents.length);
|
|
1154
|
+
console.log('📄 Product documents:', product_docs.documents.length);
|
|
1155
|
+
|
|
1156
|
+
// Get specific documents by ID from different databases
|
|
1157
|
+
const user_doc = await client.get_document('users', 'user-id-here', 'user_management');
|
|
1158
|
+
const product_doc = await client.get_document('products', 'product-id-here', 'inventory');
|
|
1159
|
+
|
|
1160
|
+
// Advanced document querying across databases
|
|
1161
|
+
const active_users = await client.query_documents('users', {
|
|
1162
|
+
status: 'active',
|
|
1163
|
+
last_login: { $gte: new Date(Date.now() - 30*24*60*60*1000) }
|
|
1164
|
+
}, {
|
|
1165
|
+
database: 'user_management',
|
|
1166
|
+
limit: 100,
|
|
1167
|
+
sort: { last_login: -1 }
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
const expensive_products = await client.query_documents('products', {
|
|
1171
|
+
price: { $gte: 500 },
|
|
1172
|
+
stock: { $gt: 0 }
|
|
1173
|
+
}, {
|
|
1174
|
+
database: 'inventory',
|
|
1175
|
+
limit: 50,
|
|
1176
|
+
sort: { price: -1 }
|
|
1177
|
+
});
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
### Health Checks and Monitoring
|
|
1181
|
+
|
|
1182
|
+
```javascript
|
|
1183
|
+
// Simple health check
|
|
1184
|
+
const ping_result = await client.ping();
|
|
1185
|
+
if (ping_result.ok === 1) {
|
|
1186
|
+
console.log('✅ Database is healthy');
|
|
1187
|
+
} else {
|
|
1188
|
+
console.log('❌ Database is not responding');
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
// Reload server configuration without restart
|
|
1192
|
+
const reload_result = await client.reload();
|
|
1193
|
+
console.log('🔄 Configuration reloaded:', reload_result.message);
|
|
1194
|
+
|
|
1195
|
+
// Monitor performance over time
|
|
1196
|
+
setInterval(async () => {
|
|
1197
|
+
const stats = await client.get_stats();
|
|
1198
|
+
console.log(`📊 ${new Date().toISOString()}: ${stats.performance.ops_per_second} ops/sec, ${stats.connections.active} connections, ${stats.database.databases} databases`);
|
|
1199
|
+
}, 30000); // Every 30 seconds
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
## Backup & Restore
|
|
1203
|
+
|
|
1204
|
+
JoystickDB includes automatic backup capabilities with S3 integration. Backups include all databases and maintain database isolation during restore operations.
|
|
1205
|
+
|
|
1206
|
+
### Automatic Backups
|
|
1207
|
+
|
|
1208
|
+
Configure automatic backups via the `JOYSTICK_DB_SETTINGS` environment variable:
|
|
1209
|
+
|
|
1210
|
+
```bash
|
|
1211
|
+
export JOYSTICK_DB_SETTINGS='{
|
|
1212
|
+
"backup": {
|
|
1213
|
+
"enabled": true,
|
|
1214
|
+
"schedule": "0 2 * * *",
|
|
1215
|
+
"retention_days": 30,
|
|
1216
|
+
"compression": true
|
|
1217
|
+
},
|
|
1218
|
+
"s3": {
|
|
1219
|
+
"region": "us-east-1",
|
|
1220
|
+
"bucket": "my-database-backups",
|
|
1221
|
+
"access_key_id": "your-access-key",
|
|
1222
|
+
"secret_access_key": "your-secret-key"
|
|
1223
|
+
}
|
|
1224
|
+
}'
|
|
1225
|
+
```
|
|
1226
|
+
|
|
1227
|
+
### Manual Backup Operations
|
|
1228
|
+
|
|
1229
|
+
```javascript
|
|
1230
|
+
// Create an immediate backup (includes all databases)
|
|
1231
|
+
const backup_result = await client.backup_now();
|
|
1232
|
+
console.log('💾 Backup created:', backup_result.filename);
|
|
1233
|
+
console.log('📊 Backup size:', backup_result.size_mb, 'MB');
|
|
1234
|
+
console.log('⏱️ Time taken:', backup_result.duration_ms, 'ms');
|
|
1235
|
+
console.log('🗄️ Databases backed up:', backup_result.databases_count);
|
|
1236
|
+
|
|
1237
|
+
// List all available backups
|
|
1238
|
+
const backups = await client.list_backups();
|
|
1239
|
+
console.log('📋 Available backups:');
|
|
1240
|
+
backups.backups.forEach(backup => {
|
|
1241
|
+
console.log(` 📦 ${backup.filename} (${backup.size_mb} MB) - ${backup.created_at}`);
|
|
1242
|
+
console.log(` Databases: ${backup.databases_count}, Collections: ${backup.collections_count}`);
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
// Restore from a specific backup (restores all databases)
|
|
1246
|
+
const restore_result = await client.restore_backup('backup-2024-01-15-02-00-00.tar.gz');
|
|
1247
|
+
console.log('🔄 Restore completed in:', restore_result.duration_ms, 'ms');
|
|
1248
|
+
console.log('🗄️ Restored databases:', restore_result.databases_restored);
|
|
1249
|
+
console.log('📊 Restored collections:', restore_result.collections_restored);
|
|
1250
|
+
```
|
|
1251
|
+
|
|
1252
|
+
### Backup Best Practices
|
|
1253
|
+
|
|
1254
|
+
```javascript
|
|
1255
|
+
// Test your backups regularly
|
|
1256
|
+
const test_backup = async () => {
|
|
1257
|
+
console.log('🧪 Testing backup system...');
|
|
1258
|
+
|
|
1259
|
+
// Create a test backup
|
|
1260
|
+
const backup = await client.backup_now();
|
|
1261
|
+
console.log('✅ Backup created successfully');
|
|
1262
|
+
|
|
1263
|
+
// Verify backup exists and contains all databases
|
|
1264
|
+
const backups = await client.list_backups();
|
|
1265
|
+
const latest = backups.backups.find(b => b.filename === backup.filename);
|
|
1266
|
+
|
|
1267
|
+
if (latest && latest.size_mb > 0 && latest.databases_count > 0) {
|
|
1268
|
+
console.log('✅ Backup verification passed');
|
|
1269
|
+
console.log(`📊 Backup contains ${latest.databases_count} databases`);
|
|
1270
|
+
} else {
|
|
1271
|
+
console.log('❌ Backup verification failed');
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
|
|
1275
|
+
// Run backup test monthly
|
|
1276
|
+
setInterval(test_backup, 30 * 24 * 60 * 60 * 1000); // 30 days
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
## Configuration
|
|
1280
|
+
|
|
1281
|
+
### Complete Configuration Reference
|
|
1282
|
+
|
|
1283
|
+
```json
|
|
1284
|
+
{
|
|
1285
|
+
"port": 1983,
|
|
1286
|
+
"cluster": true,
|
|
1287
|
+
"worker_count": 4,
|
|
1288
|
+
|
|
1289
|
+
"database": {
|
|
1290
|
+
"path": "./data",
|
|
1291
|
+
"auto_map_size": true,
|
|
1292
|
+
"map_size": 1073741824,
|
|
1293
|
+
"max_dbs": 100
|
|
1294
|
+
},
|
|
1295
|
+
|
|
1296
|
+
"authentication": {
|
|
1297
|
+
"rate_limit": {
|
|
1298
|
+
"max_attempts": 5,
|
|
1299
|
+
"window_ms": 300000,
|
|
1300
|
+
"lockout_duration_ms": 900000
|
|
1301
|
+
}
|
|
1302
|
+
},
|
|
1303
|
+
|
|
1304
|
+
"replication": {
|
|
1305
|
+
"enabled": false,
|
|
1306
|
+
"mode": "async",
|
|
1307
|
+
"timeout_ms": 5000,
|
|
1308
|
+
"retry_attempts": 3,
|
|
1309
|
+
"batch_size": 100,
|
|
1310
|
+
"secondaries": []
|
|
1311
|
+
},
|
|
1312
|
+
|
|
1313
|
+
"write_forwarder": {
|
|
1314
|
+
"enabled": false,
|
|
1315
|
+
"primary": {
|
|
1316
|
+
"ip": "127.0.0.1",
|
|
1317
|
+
"port": 1983,
|
|
1318
|
+
"private_key": "base64-encoded-key"
|
|
1319
|
+
},
|
|
1320
|
+
"timeout_ms": 5000,
|
|
1321
|
+
"retry_attempts": 3
|
|
1322
|
+
},
|
|
1323
|
+
|
|
1324
|
+
"s3": {
|
|
1325
|
+
"region": "us-east-1",
|
|
1326
|
+
"bucket": "my-backups",
|
|
1327
|
+
"access_key_id": "AKIA...",
|
|
1328
|
+
"secret_access_key": "...",
|
|
1329
|
+
"endpoint": "https://s3.amazonaws.com"
|
|
1330
|
+
},
|
|
1331
|
+
|
|
1332
|
+
"backup": {
|
|
1333
|
+
"enabled": true,
|
|
1334
|
+
"schedule": "0 2 * * *",
|
|
1335
|
+
"retention_days": 30,
|
|
1336
|
+
"compression": true
|
|
1337
|
+
},
|
|
1338
|
+
|
|
1339
|
+
"logging": {
|
|
1340
|
+
"level": "info",
|
|
1341
|
+
"file": "./logs/joystickdb.log",
|
|
1342
|
+
"max_size": "20m",
|
|
1343
|
+
"max_files": "14d"
|
|
1344
|
+
},
|
|
1345
|
+
|
|
1346
|
+
"performance": {
|
|
1347
|
+
"connection_pool_size": 1000,
|
|
1348
|
+
"idle_timeout": 600000,
|
|
1349
|
+
"request_timeout": 5000,
|
|
1350
|
+
"auto_index_threshold": 3
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
```
|
|
1354
|
+
|
|
1355
|
+
### Environment Variable Overrides
|
|
1356
|
+
|
|
1357
|
+
```bash
|
|
1358
|
+
# Server settings
|
|
1359
|
+
JOYSTICKDB_PORT=1983
|
|
1360
|
+
JOYSTICKDB_CLUSTER=true
|
|
1361
|
+
JOYSTICKDB_WORKER_COUNT=4
|
|
1362
|
+
|
|
1363
|
+
# Database settings
|
|
1364
|
+
JOYSTICKDB_DATABASE_PATH=./data
|
|
1365
|
+
JOYSTICKDB_DATABASE_AUTO_MAP_SIZE=true
|
|
1366
|
+
JOYSTICKDB_DATABASE_MAX_DBS=100
|
|
1367
|
+
|
|
1368
|
+
# Replication settings
|
|
1369
|
+
JOYSTICKDB_REPLICATION_ENABLED=false
|
|
1370
|
+
JOYSTICKDB_REPLICATION_MODE=async
|
|
1371
|
+
|
|
1372
|
+
# S3 settings
|
|
1373
|
+
JOYSTICKDB_S3_REGION=us-east-1
|
|
1374
|
+
JOYSTICKDB_S3_BUCKET=my-backups
|
|
1375
|
+
AWS_ACCESS_KEY_ID=your-access-key
|
|
1376
|
+
AWS_SECRET_ACCESS_KEY=your-secret-key
|
|
1377
|
+
|
|
1378
|
+
# Backup settings
|
|
1379
|
+
JOYSTICKDB_BACKUP_ENABLED=true
|
|
1380
|
+
JOYSTICKDB_BACKUP_RETENTION_DAYS=30
|
|
1381
|
+
|
|
1382
|
+
# Logging
|
|
1383
|
+
JOYSTICKDB_LOG_LEVEL=info
|
|
1384
|
+
```
|
|
1385
|
+
|
|
1386
|
+
## Production Deployment
|
|
1387
|
+
|
|
1388
|
+
### Docker Deployment
|
|
1389
|
+
|
|
1390
|
+
```dockerfile
|
|
1391
|
+
FROM node:18-alpine
|
|
1392
|
+
|
|
1393
|
+
WORKDIR /app
|
|
1394
|
+
|
|
1395
|
+
# Install dependencies
|
|
1396
|
+
COPY package*.json ./
|
|
1397
|
+
RUN npm ci --only=production
|
|
1398
|
+
|
|
1399
|
+
# Copy application code
|
|
1400
|
+
COPY . .
|
|
1401
|
+
|
|
1402
|
+
# Create data and logs directories
|
|
1403
|
+
RUN mkdir -p data logs
|
|
1404
|
+
|
|
1405
|
+
# Expose port
|
|
1406
|
+
EXPOSE 1983
|
|
1407
|
+
|
|
1408
|
+
# Create volumes for persistent data
|
|
1409
|
+
VOLUME ["/app/data", "/app/logs"]
|
|
1410
|
+
|
|
1411
|
+
# Health check
|
|
1412
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
1413
|
+
CMD node -e "
|
|
1414
|
+
const client = require('./src/client/index.js').default.client({port: 1983, timeout: 2000, reconnect: false});
|
|
1415
|
+
client.ping().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
1416
|
+
"
|
|
1417
|
+
|
|
1418
|
+
# Start the server
|
|
1419
|
+
CMD ["npm", "start"]
|
|
1420
|
+
```
|
|
1421
|
+
|
|
1422
|
+
### Docker Compose
|
|
1423
|
+
|
|
1424
|
+
```yaml
|
|
1425
|
+
version: '3.8'
|
|
1426
|
+
|
|
1427
|
+
services:
|
|
1428
|
+
joystickdb:
|
|
1429
|
+
build: .
|
|
1430
|
+
ports:
|
|
1431
|
+
- "1983:1983"
|
|
1432
|
+
volumes:
|
|
1433
|
+
- ./data:/app/data
|
|
1434
|
+
- ./logs:/app/logs
|
|
1435
|
+
environment:
|
|
1436
|
+
- NODE_ENV=production
|
|
1437
|
+
- WORKER_COUNT=4
|
|
1438
|
+
- JOYSTICKDB_LOG_LEVEL=info
|
|
1439
|
+
- JOYSTICK_DB_SETTINGS={"port": 1983, "database": {"path": "./data", "max_dbs": 100}}
|
|
1440
|
+
restart: unless-stopped
|
|
1441
|
+
healthcheck:
|
|
1442
|
+
test: ["CMD", "npm", "run", "health-check"]
|
|
1443
|
+
interval: 30s
|
|
1444
|
+
timeout: 10s
|
|
1445
|
+
retries: 3
|
|
1446
|
+
start_period: 40s
|
|
1447
|
+
|
|
1448
|
+
# Optional: Secondary node for replication
|
|
1449
|
+
joystickdb-secondary:
|
|
1450
|
+
build: .
|
|
1451
|
+
ports:
|
|
1452
|
+
- "1984:1984"
|
|
1453
|
+
volumes:
|
|
1454
|
+
- ./data-secondary:/app/data
|
|
1455
|
+
- ./logs-secondary:/app/logs
|
|
1456
|
+
environment:
|
|
1457
|
+
- NODE_ENV=production
|
|
1458
|
+
- JOYSTICKDB_PORT=1984
|
|
1459
|
+
- JOYSTICK_DB_SETTINGS={"port": 1984, "database": {"path": "./data", "max_dbs": 100}}
|
|
1460
|
+
restart: unless-stopped
|
|
1461
|
+
depends_on:
|
|
1462
|
+
- joystickdb
|
|
1463
|
+
```
|
|
1464
|
+
|
|
1465
|
+
### Kubernetes Deployment
|
|
1466
|
+
|
|
1467
|
+
```yaml
|
|
1468
|
+
apiVersion: apps/v1
|
|
1469
|
+
kind: Deployment
|
|
1470
|
+
metadata:
|
|
1471
|
+
name: joystickdb
|
|
1472
|
+
labels:
|
|
1473
|
+
app: joystickdb
|
|
1474
|
+
spec:
|
|
1475
|
+
replicas: 3
|
|
1476
|
+
selector:
|
|
1477
|
+
matchLabels:
|
|
1478
|
+
app: joystickdb
|
|
1479
|
+
template:
|
|
1480
|
+
metadata:
|
|
1481
|
+
labels:
|
|
1482
|
+
app: joystickdb
|
|
1483
|
+
spec:
|
|
1484
|
+
containers:
|
|
1485
|
+
- name: joystickdb
|
|
1486
|
+
image: joystickdb:latest
|
|
1487
|
+
ports:
|
|
1488
|
+
- containerPort: 1983
|
|
1489
|
+
env:
|
|
1490
|
+
- name: WORKER_COUNT
|
|
1491
|
+
value: "2"
|
|
1492
|
+
- name: NODE_ENV
|
|
1493
|
+
value: "production"
|
|
1494
|
+
- name: JOYSTICKDB_LOG_LEVEL
|
|
1495
|
+
value: "info"
|
|
1496
|
+
- name: JOYSTICK_DB_SETTINGS
|
|
1497
|
+
valueFrom:
|
|
1498
|
+
configMapKeyRef:
|
|
1499
|
+
name: joystickdb-config
|
|
1500
|
+
key: settings.json
|
|
1501
|
+
resources:
|
|
1502
|
+
requests:
|
|
1503
|
+
memory: "256Mi"
|
|
1504
|
+
cpu: "250m"
|
|
1505
|
+
limits:
|
|
1506
|
+
memory: "512Mi"
|
|
1507
|
+
cpu: "500m"
|
|
1508
|
+
volumeMounts:
|
|
1509
|
+
- name: data
|
|
1510
|
+
mountPath: /app/data
|
|
1511
|
+
livenessProbe:
|
|
1512
|
+
exec:
|
|
1513
|
+
command:
|
|
1514
|
+
- npm
|
|
1515
|
+
- run
|
|
1516
|
+
- health-check
|
|
1517
|
+
initialDelaySeconds: 30
|
|
1518
|
+
periodSeconds: 30
|
|
1519
|
+
readinessProbe:
|
|
1520
|
+
exec:
|
|
1521
|
+
command:
|
|
1522
|
+
- npm
|
|
1523
|
+
- run
|
|
1524
|
+
- health-check
|
|
1525
|
+
initialDelaySeconds: 5
|
|
1526
|
+
periodSeconds: 10
|
|
1527
|
+
volumes:
|
|
1528
|
+
- name: data
|
|
1529
|
+
persistentVolumeClaim:
|
|
1530
|
+
claimName: joystickdb-data
|
|
1531
|
+
---
|
|
1532
|
+
apiVersion: v1
|
|
1533
|
+
kind: ConfigMap
|
|
1534
|
+
metadata:
|
|
1535
|
+
name: joystickdb-config
|
|
1536
|
+
data:
|
|
1537
|
+
settings.json: |
|
|
1538
|
+
{
|
|
1539
|
+
"port": 1983,
|
|
1540
|
+
"database": {
|
|
1541
|
+
"path": "./data",
|
|
1542
|
+
"auto_map_size": true,
|
|
1543
|
+
"max_dbs": 100
|
|
1544
|
+
},
|
|
1545
|
+
"authentication": {
|
|
1546
|
+
"rate_limit": {
|
|
1547
|
+
"max_attempts": 5,
|
|
1548
|
+
"window_ms": 300000
|
|
1549
|
+
}
|
|
1550
|
+
},
|
|
1551
|
+
"backup": {
|
|
1552
|
+
"enabled": true,
|
|
1553
|
+
"schedule": "0 2 * * *",
|
|
1554
|
+
"retention_days": 30
|
|
1555
|
+
},
|
|
1556
|
+
"logging": {
|
|
1557
|
+
"level": "info"
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
---
|
|
1561
|
+
apiVersion: v1
|
|
1562
|
+
kind: Service
|
|
1563
|
+
metadata:
|
|
1564
|
+
name: joystickdb-service
|
|
1565
|
+
spec:
|
|
1566
|
+
selector:
|
|
1567
|
+
app: joystickdb
|
|
1568
|
+
ports:
|
|
1569
|
+
- port: 1983
|
|
1570
|
+
targetPort: 1983
|
|
1571
|
+
type: LoadBalancer
|
|
1572
|
+
```
|
|
1573
|
+
|
|
1574
|
+
### Monitoring and Health Checks
|
|
1575
|
+
|
|
1576
|
+
```javascript
|
|
1577
|
+
// Health check endpoint
|
|
1578
|
+
const health_check = async () => {
|
|
1579
|
+
try {
|
|
1580
|
+
const client = joystickdb.client({
|
|
1581
|
+
port: 1983,
|
|
1582
|
+
timeout: 2000,
|
|
1583
|
+
reconnect: false
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
const result = await client.ping();
|
|
1587
|
+
client.disconnect();
|
|
1588
|
+
|
|
1589
|
+
return result.ok === 1;
|
|
1590
|
+
} catch (error) {
|
|
1591
|
+
return false;
|
|
1592
|
+
}
|
|
1593
|
+
};
|
|
1594
|
+
|
|
1595
|
+
// Performance monitoring
|
|
1596
|
+
const monitor_performance = async () => {
|
|
1597
|
+
const client = joystickdb.client({
|
|
1598
|
+
port: 1983,
|
|
1599
|
+
authentication: {
|
|
1600
|
+
username: 'admin',
|
|
1601
|
+
password: process.env.DB_PASSWORD
|
|
1602
|
+
}
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
const stats = await client.get_stats();
|
|
1606
|
+
|
|
1607
|
+
// Log metrics to your monitoring system
|
|
1608
|
+
console.log('Metrics:', {
|
|
1609
|
+
uptime: stats.server.uptime,
|
|
1610
|
+
memory_usage: stats.memory.heapUsed,
|
|
1611
|
+
ops_per_second: stats.performance.ops_per_second,
|
|
1612
|
+
active_connections: stats.connections.active,
|
|
1613
|
+
database_count: stats.database.databases,
|
|
1614
|
+
total_collections: stats.database.collections,
|
|
1615
|
+
database_size: stats.database.used_space_mb
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
client.disconnect();
|
|
1619
|
+
};
|
|
1620
|
+
```
|
|
1621
|
+
|
|
1622
|
+
## API Reference
|
|
1623
|
+
|
|
1624
|
+
### Client Methods
|
|
1625
|
+
|
|
1626
|
+
#### Connection Management
|
|
1627
|
+
- `client.connect()` - Establish connection to server
|
|
1628
|
+
- `client.disconnect()` - Close connection to server
|
|
1629
|
+
- `client.ping()` - Test server connectivity and responsiveness
|
|
1630
|
+
|
|
1631
|
+
#### Authentication & Setup
|
|
1632
|
+
- `client.setup()` - Initial server setup (generates password) - **DEPRECATED**
|
|
1633
|
+
- `client.authenticate()` - Manual authentication (usually automatic)
|
|
1634
|
+
|
|
1635
|
+
#### Multi-Database Interface
|
|
1636
|
+
- `client.db(database_name)` - Get database interface for method chaining
|
|
1637
|
+
- `client.list_databases()` - List all databases on the server
|
|
1638
|
+
|
|
1639
|
+
#### Database-Level Operations
|
|
1640
|
+
- `database.collection(collection_name)` - Get collection interface within database
|
|
1641
|
+
- `database.list_collections()` - List collections in the database
|
|
1642
|
+
- `database.get_stats()` - Get database-specific statistics
|
|
1643
|
+
- `database.drop_database()` - Drop the entire database (admin operation)
|
|
1644
|
+
- `database.create_collection(collection_name, options)` - Create collection explicitly
|
|
1645
|
+
|
|
1646
|
+
#### CRUD Operations (Collection Chaining API)
|
|
1647
|
+
- `collection.insert_one(document, options)` - Insert single document
|
|
1648
|
+
- `collection.find_one(filter, options)` - Find single document
|
|
1649
|
+
- `collection.find(filter, options)` - Find multiple documents
|
|
1650
|
+
- `collection.update_one(filter, update, options)` - Update single document
|
|
1651
|
+
- `collection.delete_one(filter, options)` - Delete single document
|
|
1652
|
+
- `collection.bulk_write(operations, options)` - Bulk operations
|
|
1653
|
+
|
|
1654
|
+
#### Index Management (Collection Chaining API)
|
|
1655
|
+
- `collection.create_index(field, options)` - Create index on field
|
|
1656
|
+
- `collection.upsert_index(field, options)` - Create or update index (upsert)
|
|
1657
|
+
- `collection.drop_index(field)` - Drop index on field
|
|
1658
|
+
- `collection.get_indexes()` - List all indexes for collection
|
|
1659
|
+
|
|
1660
|
+
#### Auto-Indexing Management
|
|
1661
|
+
- `client.get_auto_index_stats()` - Get automatic indexing statistics
|
|
1662
|
+
|
|
1663
|
+
#### Replication Management
|
|
1664
|
+
- `client.get_replication_status()` - Get replication status and statistics
|
|
1665
|
+
- `client.add_secondary(secondary_config)` - Add new secondary node
|
|
1666
|
+
- `client.remove_secondary(secondary_id)` - Remove secondary node
|
|
1667
|
+
- `client.sync_secondaries()` - Force synchronization with secondaries
|
|
1668
|
+
- `client.get_secondary_health()` - Get health status of secondary nodes
|
|
1669
|
+
- `client.get_forwarder_status()` - Get write forwarder status (for secondary nodes)
|
|
1670
|
+
|
|
1671
|
+
#### Administration
|
|
1672
|
+
- `client.get_stats()` - Get comprehensive server statistics
|
|
1673
|
+
- `client.list_collections()` - List all collections in default database (backward compatible)
|
|
1674
|
+
- `client.list_documents(collection, options)` - List documents with pagination and sorting
|
|
1675
|
+
- `client.get_document(collection, document_id, database)` - Get document by ID
|
|
1676
|
+
- `client.query_documents(collection, filter, options)` - Advanced document query with filtering
|
|
1677
|
+
- `client.insert_document(collection, document)` - Admin insert operation
|
|
1678
|
+
- `client.update_document(collection, document_id, update)` - Admin update operation by ID
|
|
1679
|
+
- `client.delete_document(collection, document_id)` - Admin delete operation by ID
|
|
1680
|
+
|
|
1681
|
+
#### Backup & Restore
|
|
1682
|
+
- `client.backup_now()` - Create immediate backup (includes all databases)
|
|
1683
|
+
- `client.list_backups()` - List all available backups
|
|
1684
|
+
- `client.restore_backup(backup_name)` - Restore from backup (restores all databases)
|
|
1685
|
+
|
|
1686
|
+
#### Server Management
|
|
1687
|
+
- `client.reload()` - Reload server configuration
|
|
1688
|
+
|
|
1689
|
+
### HTTP API Methods
|
|
1690
|
+
|
|
1691
|
+
#### User Management (via HTTP)
|
|
1692
|
+
- `POST /api/users` - Create new user with username, password, and role
|
|
1693
|
+
- `GET /api/users?username=<username>` - Get user information by username
|
|
1694
|
+
- `PUT /api/users` - Update user password or role
|
|
1695
|
+
- `DELETE /api/users?username=<username>` - Delete user by username
|
|
1696
|
+
|
|
1697
|
+
All HTTP API endpoints require the `x-joystick-db-api-key` header for authentication.
|
|
1698
|
+
|
|
1699
|
+
### Events
|
|
1700
|
+
|
|
1701
|
+
The client emits these events that you can listen to:
|
|
1702
|
+
|
|
1703
|
+
- `connect` - Connection established with server
|
|
1704
|
+
- `authenticated` - Authentication successful, ready to use
|
|
1705
|
+
- `error` - Error occurred (connection, authentication, etc.)
|
|
1706
|
+
- `disconnect` - Connection closed
|
|
1707
|
+
- `reconnecting` - Reconnection attempt in progress
|
|
1708
|
+
- `response` - Unsolicited server response received
|
|
1709
|
+
|
|
1710
|
+
### Query Options
|
|
1711
|
+
|
|
1712
|
+
#### Find Options
|
|
1713
|
+
```javascript
|
|
1714
|
+
{
|
|
1715
|
+
limit: 10, // Maximum number of documents to return
|
|
1716
|
+
skip: 0, // Number of documents to skip
|
|
1717
|
+
sort: { field: 1 }, // Sort order (1 = ascending, -1 = descending)
|
|
1718
|
+
projection: { field: 1 } // Fields to include (1) or exclude (0)
|
|
1719
|
+
}
|
|
1720
|
+
```
|
|
1721
|
+
|
|
1722
|
+
#### Update Options
|
|
1723
|
+
```javascript
|
|
1724
|
+
{
|
|
1725
|
+
upsert: false // Create document if it doesn't exist
|
|
1726
|
+
}
|
|
1727
|
+
```
|
|
1728
|
+
|
|
1729
|
+
#### Index Options
|
|
1730
|
+
```javascript
|
|
1731
|
+
{
|
|
1732
|
+
unique: false, // Enforce uniqueness
|
|
1733
|
+
sparse: false // Skip documents missing the indexed field
|
|
1734
|
+
}
|
|
1735
|
+
```
|
|
1736
|
+
|
|
1737
|
+
### Update Operators
|
|
1738
|
+
|
|
1739
|
+
JoystickDB supports these MongoDB-style update operators:
|
|
1740
|
+
|
|
1741
|
+
- `$set` - Set field values
|
|
1742
|
+
- `$unset` - Remove fields
|
|
1743
|
+
- `$inc` - Increment numeric values
|
|
1744
|
+
- `$push` - Add elements to arrays
|
|
1745
|
+
- `$pull` - Remove elements from arrays
|
|
1746
|
+
|
|
1747
|
+
### Query Operators
|
|
1748
|
+
|
|
1749
|
+
JoystickDB supports these MongoDB-style query operators:
|
|
1750
|
+
|
|
1751
|
+
- `$eq` - Equal to
|
|
1752
|
+
- `$ne` - Not equal to
|
|
1753
|
+
- `$gt` - Greater than
|
|
1754
|
+
- `$gte` - Greater than or equal to
|
|
1755
|
+
- `$lt` - Less than
|
|
1756
|
+
- `$lte` - Less than or equal to
|
|
1757
|
+
- `$in` - Value in array
|
|
1758
|
+
- `$nin` - Value not in array
|
|
1759
|
+
- `$exists` - Field exists
|
|
1760
|
+
- `$regex` - Regular expression match
|
|
1761
|
+
|
|
1762
|
+
### Error Handling
|
|
1763
|
+
|
|
1764
|
+
```javascript
|
|
1765
|
+
try {
|
|
1766
|
+
const user_db = client.db('user_management');
|
|
1767
|
+
const users = user_db.collection('users');
|
|
1768
|
+
const result = await users.insert_one({ name: 'John' });
|
|
1769
|
+
} catch (error) {
|
|
1770
|
+
if (error.message.includes('Authentication')) {
|
|
1771
|
+
// Handle authentication error
|
|
1772
|
+
console.error('Please check your username and password');
|
|
1773
|
+
} else if (error.message.includes('timeout')) {
|
|
1774
|
+
// Handle timeout error
|
|
1775
|
+
console.error('Server is not responding');
|
|
1776
|
+
} else if (error.message.includes('Connection')) {
|
|
1777
|
+
// Handle connection error
|
|
1778
|
+
console.error('Cannot connect to server');
|
|
1779
|
+
} else if (error.message.includes('Invalid database name')) {
|
|
1780
|
+
// Handle database naming error
|
|
1781
|
+
console.error('Database name violates naming rules');
|
|
1782
|
+
} else {
|
|
1783
|
+
// Handle other errors
|
|
1784
|
+
console.error('Database error:', error.message);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
```
|
|
1788
|
+
|
|
1789
|
+
### Performance Tips
|
|
1790
|
+
|
|
1791
|
+
1. **Use Indexes**: Create indexes on frequently queried fields in each database
|
|
1792
|
+
2. **Batch Operations**: Use `bulk_write` for multiple operations
|
|
1793
|
+
3. **Limit Results**: Always use `limit` for large result sets
|
|
1794
|
+
4. **Use Projections**: Only fetch fields you need
|
|
1795
|
+
5. **Connection Pooling**: Reuse client connections
|
|
1796
|
+
6. **Monitor Performance**: Use `get_stats()` to track performance
|
|
1797
|
+
7. **Database Organization**: Use separate databases to organize related data
|
|
1798
|
+
8. **Index Per Database**: Create indexes specific to each database's query patterns
|
|
1799
|
+
|
|
1800
|
+
### Best Practices
|
|
1801
|
+
|
|
1802
|
+
1. **Always Handle Errors**: Wrap database calls in try-catch blocks
|
|
1803
|
+
2. **Use Environment Variables**: Store passwords and config in env vars
|
|
1804
|
+
3. **Regular Backups**: Enable automatic backups for production
|
|
1805
|
+
4. **Monitor Health**: Set up health checks and monitoring
|
|
1806
|
+
5. **Use Replication**: Set up replication for high availability
|
|
1807
|
+
6. **Index Strategy**: Create indexes based on your query patterns per database
|
|
1808
|
+
7. **Connection Management**: Properly close connections when done
|
|
1809
|
+
8. **Secure API Keys**: Store API keys securely and rotate them regularly
|
|
1810
|
+
9. **User Management**: Use appropriate roles (read/write/read_write) for users
|
|
1811
|
+
10. **HTTP API Security**: Always use HTTPS in production for HTTP API calls
|
|
1812
|
+
11. **Database Organization**: Use meaningful database names and organize data logically
|
|
1813
|
+
12. **Multi-Database Design**: Separate concerns across databases (users, inventory, analytics, etc.)
|
|
1814
|
+
13. **Database Naming**: Follow naming conventions and avoid reserved names
|
|
1815
|
+
14. **Backward Compatibility**: Gradually migrate from single-database to multi-database API
|
|
1816
|
+
|
|
1817
|
+
---
|
|
1818
|
+
|
|
1819
|
+
For more examples and advanced usage, see the `/test` directory in the repository.
|
|
1820
|
+
|
|
1821
|
+
**Need Help?** Check out the comprehensive test suite for real-world usage examples, or refer to the detailed configuration options above.
|