@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,473 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview API key management for JoystickDB authentication system.
|
|
3
|
+
*
|
|
4
|
+
* Handles API key file generation, validation, and user management operations.
|
|
5
|
+
* Provides secure API key-based authentication for database setup and user management.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
|
|
9
|
+
import crypto from 'crypto';
|
|
10
|
+
import bcrypt from 'bcrypt';
|
|
11
|
+
import create_logger from './logger.js';
|
|
12
|
+
import { get_database, build_collection_key, generate_document_id } from './query_engine.js';
|
|
13
|
+
|
|
14
|
+
const { create_context_logger } = create_logger('api_key_manager');
|
|
15
|
+
const log = create_context_logger();
|
|
16
|
+
|
|
17
|
+
/** @type {string} Path to API key file */
|
|
18
|
+
const API_KEY_FILE_PATH = './API_KEY';
|
|
19
|
+
|
|
20
|
+
/** @type {string} Collection name for storing users */
|
|
21
|
+
const USERS_COLLECTION = '_users';
|
|
22
|
+
|
|
23
|
+
/** @type {number} BCrypt salt rounds for password hashing */
|
|
24
|
+
const SALT_ROUNDS = 12;
|
|
25
|
+
|
|
26
|
+
/** @type {string|null} Cached API key */
|
|
27
|
+
let cached_api_key = null;
|
|
28
|
+
|
|
29
|
+
/** @type {boolean} Flag to track if admin user exists */
|
|
30
|
+
let admin_user_exists = false;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generates a cryptographically secure 32-character alphanumeric API key.
|
|
34
|
+
* @returns {string} 32-character alphanumeric API key
|
|
35
|
+
*/
|
|
36
|
+
const generate_api_key = () => {
|
|
37
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
38
|
+
let result = '';
|
|
39
|
+
const bytes = crypto.randomBytes(32);
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < 32; i++) {
|
|
42
|
+
result += chars[bytes[i] % chars.length];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Loads API key from file or generates new one if it doesn't exist.
|
|
50
|
+
* @returns {string} API key from file or newly generated key
|
|
51
|
+
*/
|
|
52
|
+
const load_or_generate_api_key = () => {
|
|
53
|
+
if (cached_api_key) {
|
|
54
|
+
return cached_api_key;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (existsSync(API_KEY_FILE_PATH)) {
|
|
58
|
+
try {
|
|
59
|
+
cached_api_key = readFileSync(API_KEY_FILE_PATH, 'utf8').trim();
|
|
60
|
+
log.info('API key loaded from file');
|
|
61
|
+
return cached_api_key;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
log.error('Failed to read API key file', { error: error.message });
|
|
64
|
+
throw new Error(`Failed to read API key file: ${error.message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Generate new API key
|
|
69
|
+
const api_key = generate_api_key();
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
writeFileSync(API_KEY_FILE_PATH, api_key, { mode: 0o600 });
|
|
73
|
+
cached_api_key = api_key;
|
|
74
|
+
|
|
75
|
+
log.info('New API key generated and saved', { file_path: API_KEY_FILE_PATH });
|
|
76
|
+
|
|
77
|
+
// Display startup message
|
|
78
|
+
display_startup_message(api_key);
|
|
79
|
+
|
|
80
|
+
return api_key;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
log.error('Failed to save API key file', { error: error.message });
|
|
83
|
+
throw new Error(`Failed to save API key file: ${error.message}`);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Displays the startup message with API key and setup instructions.
|
|
89
|
+
* @param {string} api_key - Generated API key to display
|
|
90
|
+
*/
|
|
91
|
+
const display_startup_message = (api_key) => {
|
|
92
|
+
const port = process.env.JOYSTICK_DB_PORT || '1983';
|
|
93
|
+
const http_port = process.env.JOYSTICK_DB_HTTP_PORT || '1984';
|
|
94
|
+
|
|
95
|
+
console.log('\nJoystickDB Setup\n');
|
|
96
|
+
console.log('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.\n');
|
|
97
|
+
console.log('=== STORE THIS KEY SECURELY -- IT WILL NOT BE DISPLAYED AGAIN ===');
|
|
98
|
+
console.log(api_key);
|
|
99
|
+
console.log('===\n');
|
|
100
|
+
console.log('To create a user, send a POST request to the server:\n');
|
|
101
|
+
console.log(`fetch('http://localhost:${http_port}/api/users', {`);
|
|
102
|
+
console.log(' method: \'POST\',');
|
|
103
|
+
console.log(' headers: {');
|
|
104
|
+
console.log(' \'Content-Type\': \'application/json\',');
|
|
105
|
+
console.log(` 'x-joystick-db-api-key': '${api_key}'`);
|
|
106
|
+
console.log(' },');
|
|
107
|
+
console.log(' body: JSON.stringify({');
|
|
108
|
+
console.log(' username: \'admin\',');
|
|
109
|
+
console.log(' password: \'your_secure_password\',');
|
|
110
|
+
console.log(' role: \'read_write\'');
|
|
111
|
+
console.log(' })');
|
|
112
|
+
console.log('});\n');
|
|
113
|
+
console.log('==!==');
|
|
114
|
+
console.log('Store this key securely. It will not be displayed again.');
|
|
115
|
+
console.log('==!==\n');
|
|
116
|
+
console.log(`API key saved to: ${API_KEY_FILE_PATH}\n`);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Validates an API key against the stored key.
|
|
121
|
+
* @param {string} provided_key - API key to validate
|
|
122
|
+
* @returns {boolean} True if API key is valid
|
|
123
|
+
*/
|
|
124
|
+
const validate_api_key = (provided_key) => {
|
|
125
|
+
if (!provided_key) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const stored_key = load_or_generate_api_key();
|
|
130
|
+
return provided_key === stored_key;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Validates username format (alphanumeric, 3-50 characters).
|
|
135
|
+
* @param {string} username - Username to validate
|
|
136
|
+
* @returns {boolean} True if username is valid
|
|
137
|
+
*/
|
|
138
|
+
const validate_username = (username) => {
|
|
139
|
+
if (!username || typeof username !== 'string') {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return /^[a-zA-Z0-9]{3,50}$/.test(username);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Validates password complexity requirements.
|
|
148
|
+
* @param {string} password - Password to validate
|
|
149
|
+
* @returns {Object} Validation result
|
|
150
|
+
* @returns {boolean} returns.valid - Whether password is valid
|
|
151
|
+
* @returns {string} returns.error - Error message if invalid
|
|
152
|
+
*/
|
|
153
|
+
const validate_password = (password) => {
|
|
154
|
+
if (!password || typeof password !== 'string') {
|
|
155
|
+
return { valid: false, error: 'Password is required' };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (password.length < 8) {
|
|
159
|
+
return { valid: false, error: 'Password must be at least 8 characters long' };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (password.length > 128) {
|
|
163
|
+
return { valid: false, error: 'Password must be no more than 128 characters long' };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { valid: true };
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Validates role value against allowed options.
|
|
171
|
+
* @param {string} role - Role to validate
|
|
172
|
+
* @returns {boolean} True if role is valid
|
|
173
|
+
*/
|
|
174
|
+
const validate_role = (role) => {
|
|
175
|
+
const allowed_roles = ['read', 'write', 'read_write'];
|
|
176
|
+
return allowed_roles.includes(role);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Creates a new user in the database.
|
|
181
|
+
* @param {Object} user_data - User data
|
|
182
|
+
* @param {string} user_data.username - Username
|
|
183
|
+
* @param {string} user_data.password - Plain text password
|
|
184
|
+
* @param {string} user_data.role - User role
|
|
185
|
+
* @returns {Promise<Object>} Created user data
|
|
186
|
+
* @throws {Error} When validation fails or user already exists
|
|
187
|
+
*/
|
|
188
|
+
const create_user = async (user_data) => {
|
|
189
|
+
const { username, password, role } = user_data;
|
|
190
|
+
|
|
191
|
+
// Validate input
|
|
192
|
+
if (!validate_username(username)) {
|
|
193
|
+
throw new Error('Username must be alphanumeric and 3-50 characters long');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const password_validation = validate_password(password);
|
|
197
|
+
if (!password_validation.valid) {
|
|
198
|
+
throw new Error(password_validation.error);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!validate_role(role)) {
|
|
202
|
+
throw new Error('Role must be one of: read, write, read_write');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const db = get_database();
|
|
206
|
+
|
|
207
|
+
// Hash password
|
|
208
|
+
const password_hash = await bcrypt.hash(password, SALT_ROUNDS);
|
|
209
|
+
|
|
210
|
+
// Create user document
|
|
211
|
+
const user_document = {
|
|
212
|
+
username,
|
|
213
|
+
password_hash,
|
|
214
|
+
role,
|
|
215
|
+
created_at: new Date().toISOString(),
|
|
216
|
+
updated_at: new Date().toISOString()
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Store user in database using transaction
|
|
220
|
+
const user_key = build_collection_key(USERS_COLLECTION, username);
|
|
221
|
+
let stored_user = null;
|
|
222
|
+
|
|
223
|
+
await db.transaction(() => {
|
|
224
|
+
const existing_user = db.get(user_key);
|
|
225
|
+
|
|
226
|
+
if (existing_user) {
|
|
227
|
+
throw new Error('Username already exists');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
db.put(user_key, JSON.stringify(user_document));
|
|
231
|
+
stored_user = user_document;
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Check if this is the first admin user
|
|
235
|
+
if (role === 'read_write') {
|
|
236
|
+
admin_user_exists = true;
|
|
237
|
+
log.info('Admin user created, API key requirement relaxed for authenticated operations');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
log.info('User created successfully', { username, role });
|
|
241
|
+
|
|
242
|
+
// Return user data without password hash
|
|
243
|
+
return {
|
|
244
|
+
username: user_document.username,
|
|
245
|
+
role: user_document.role,
|
|
246
|
+
created_at: user_document.created_at
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Gets all users from the database.
|
|
252
|
+
* @returns {Array<Object>} Array of user objects without password hashes
|
|
253
|
+
*/
|
|
254
|
+
const get_all_users = () => {
|
|
255
|
+
const db = get_database();
|
|
256
|
+
const users = [];
|
|
257
|
+
|
|
258
|
+
for (const { key, value } of db.getRange({ start: `${USERS_COLLECTION}:`, end: `${USERS_COLLECTION}:\xFF` })) {
|
|
259
|
+
const parsed_user = JSON.parse(value);
|
|
260
|
+
const user_data = {
|
|
261
|
+
username: parsed_user.username,
|
|
262
|
+
role: parsed_user.role,
|
|
263
|
+
created_at: parsed_user.created_at,
|
|
264
|
+
updated_at: parsed_user.updated_at
|
|
265
|
+
};
|
|
266
|
+
users.push(user_data);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return users;
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Gets a user by username.
|
|
274
|
+
* @param {string} username - Username to find
|
|
275
|
+
* @returns {Object|null} User object without password hash, or null if not found
|
|
276
|
+
*/
|
|
277
|
+
const get_user = (username) => {
|
|
278
|
+
const db = get_database();
|
|
279
|
+
const user_key = build_collection_key(USERS_COLLECTION, username);
|
|
280
|
+
const user_data = db.get(user_key);
|
|
281
|
+
|
|
282
|
+
if (!user_data) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const user = JSON.parse(user_data);
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
username: user.username,
|
|
290
|
+
role: user.role,
|
|
291
|
+
created_at: user.created_at,
|
|
292
|
+
updated_at: user.updated_at
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Updates a user's role or password.
|
|
298
|
+
* @param {string} username - Username to update
|
|
299
|
+
* @param {Object} updates - Updates to apply
|
|
300
|
+
* @param {string} [updates.password] - New password
|
|
301
|
+
* @param {string} [updates.role] - New role
|
|
302
|
+
* @returns {Promise<Object>} Updated user data
|
|
303
|
+
* @throws {Error} When user not found or validation fails
|
|
304
|
+
*/
|
|
305
|
+
const update_user = async (username, updates) => {
|
|
306
|
+
const db = get_database();
|
|
307
|
+
const user_key = build_collection_key(USERS_COLLECTION, username);
|
|
308
|
+
|
|
309
|
+
// Validate password first if provided (before transaction)
|
|
310
|
+
let password_hash = null;
|
|
311
|
+
if (updates.password) {
|
|
312
|
+
const password_validation = validate_password(updates.password);
|
|
313
|
+
if (!password_validation.valid) {
|
|
314
|
+
throw new Error(password_validation.error);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
password_hash = await bcrypt.hash(updates.password, SALT_ROUNDS);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Validate role if provided (before transaction)
|
|
321
|
+
if (updates.role && !validate_role(updates.role)) {
|
|
322
|
+
throw new Error('Role must be one of: read, write, read_write');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Check if user exists first
|
|
326
|
+
const existing_user_data = db.get(user_key);
|
|
327
|
+
if (!existing_user_data) {
|
|
328
|
+
throw new Error('User not found');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const existing_user = JSON.parse(existing_user_data);
|
|
332
|
+
const user_to_update = { ...existing_user };
|
|
333
|
+
|
|
334
|
+
if (updates.role) {
|
|
335
|
+
user_to_update.role = updates.role;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (password_hash) {
|
|
339
|
+
user_to_update.password_hash = password_hash;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
user_to_update.updated_at = new Date().toISOString();
|
|
343
|
+
|
|
344
|
+
// Update using putSync for immediate persistence
|
|
345
|
+
db.putSync(user_key, JSON.stringify(user_to_update));
|
|
346
|
+
|
|
347
|
+
log.info('User updated successfully', { username, updates: Object.keys(updates) });
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
username: user_to_update.username,
|
|
351
|
+
role: user_to_update.role,
|
|
352
|
+
created_at: user_to_update.created_at,
|
|
353
|
+
updated_at: user_to_update.updated_at
|
|
354
|
+
};
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Deletes a user from the database.
|
|
359
|
+
* @param {string} username - Username to delete
|
|
360
|
+
* @returns {boolean} True if user was deleted
|
|
361
|
+
* @throws {Error} When user not found
|
|
362
|
+
*/
|
|
363
|
+
const delete_user = (username) => {
|
|
364
|
+
const db = get_database();
|
|
365
|
+
const user_key = build_collection_key(USERS_COLLECTION, username);
|
|
366
|
+
|
|
367
|
+
// Check if user exists first
|
|
368
|
+
const existing_user = db.get(user_key);
|
|
369
|
+
if (!existing_user) {
|
|
370
|
+
throw new Error('User not found');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Delete using removeSync (synchronous remove)
|
|
374
|
+
const result = db.removeSync(user_key);
|
|
375
|
+
|
|
376
|
+
log.info('User deleted successfully', { username });
|
|
377
|
+
return true;
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Verifies a user's password.
|
|
382
|
+
* @param {string} username - Username
|
|
383
|
+
* @param {string} password - Plain text password
|
|
384
|
+
* @returns {Promise<Object|null>} User object if password is correct, null otherwise
|
|
385
|
+
*/
|
|
386
|
+
const verify_user_password = async (username, password) => {
|
|
387
|
+
const db = get_database();
|
|
388
|
+
const user_key = build_collection_key(USERS_COLLECTION, username);
|
|
389
|
+
const user_data = db.get(user_key);
|
|
390
|
+
|
|
391
|
+
if (!user_data) {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const user = JSON.parse(user_data);
|
|
396
|
+
const is_valid = await bcrypt.compare(password, user.password_hash);
|
|
397
|
+
|
|
398
|
+
if (is_valid) {
|
|
399
|
+
return {
|
|
400
|
+
username: user.username,
|
|
401
|
+
role: user.role,
|
|
402
|
+
created_at: user.created_at,
|
|
403
|
+
updated_at: user.updated_at
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return null;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Checks if an admin user exists in the database.
|
|
412
|
+
* @returns {boolean} True if admin user exists
|
|
413
|
+
*/
|
|
414
|
+
const check_admin_user_exists = () => {
|
|
415
|
+
if (admin_user_exists) {
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const db = get_database();
|
|
420
|
+
|
|
421
|
+
for (const { value } of db.getRange({ start: `${USERS_COLLECTION}:`, end: `${USERS_COLLECTION}:\xFF` })) {
|
|
422
|
+
const parsed_user = JSON.parse(value);
|
|
423
|
+
if (parsed_user.role === 'read_write') {
|
|
424
|
+
admin_user_exists = true;
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return false;
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Initializes the API key manager.
|
|
434
|
+
*/
|
|
435
|
+
const initialize_api_key_manager = () => {
|
|
436
|
+
load_or_generate_api_key();
|
|
437
|
+
admin_user_exists = check_admin_user_exists();
|
|
438
|
+
|
|
439
|
+
if (admin_user_exists) {
|
|
440
|
+
log.info('Admin user detected, API key requirement relaxed for authenticated operations');
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Resets API key manager state (for testing).
|
|
446
|
+
*/
|
|
447
|
+
const reset_api_key_state = () => {
|
|
448
|
+
cached_api_key = null;
|
|
449
|
+
admin_user_exists = false;
|
|
450
|
+
|
|
451
|
+
// Remove API key file if it exists
|
|
452
|
+
if (existsSync(API_KEY_FILE_PATH)) {
|
|
453
|
+
try {
|
|
454
|
+
unlinkSync(API_KEY_FILE_PATH);
|
|
455
|
+
} catch (error) {
|
|
456
|
+
// Ignore errors when removing file
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
export {
|
|
462
|
+
load_or_generate_api_key,
|
|
463
|
+
validate_api_key,
|
|
464
|
+
create_user,
|
|
465
|
+
get_all_users,
|
|
466
|
+
get_user,
|
|
467
|
+
update_user,
|
|
468
|
+
delete_user,
|
|
469
|
+
verify_user_password,
|
|
470
|
+
check_admin_user_exists,
|
|
471
|
+
initialize_api_key_manager,
|
|
472
|
+
reset_api_key_state
|
|
473
|
+
};
|