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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/client/database.js +1 -1
  2. package/dist/client/index.js +1 -1
  3. package/dist/server/cluster/master.js +4 -4
  4. package/dist/server/cluster/worker.js +1 -1
  5. package/dist/server/index.js +1 -1
  6. package/dist/server/lib/auto_index_manager.js +1 -1
  7. package/dist/server/lib/backup_manager.js +1 -1
  8. package/dist/server/lib/index_manager.js +1 -1
  9. package/dist/server/lib/operation_dispatcher.js +1 -1
  10. package/dist/server/lib/operations/admin.js +1 -1
  11. package/dist/server/lib/operations/bulk_write.js +1 -1
  12. package/dist/server/lib/operations/create_index.js +1 -1
  13. package/dist/server/lib/operations/delete_many.js +1 -1
  14. package/dist/server/lib/operations/delete_one.js +1 -1
  15. package/dist/server/lib/operations/find.js +1 -1
  16. package/dist/server/lib/operations/find_one.js +1 -1
  17. package/dist/server/lib/operations/insert_one.js +1 -1
  18. package/dist/server/lib/operations/update_one.js +1 -1
  19. package/dist/server/lib/send_response.js +1 -1
  20. package/dist/server/lib/tcp_protocol.js +1 -1
  21. package/package.json +2 -2
  22. package/src/client/database.js +92 -119
  23. package/src/client/index.js +279 -345
  24. package/src/server/cluster/master.js +265 -156
  25. package/src/server/cluster/worker.js +26 -18
  26. package/src/server/index.js +553 -330
  27. package/src/server/lib/auto_index_manager.js +85 -23
  28. package/src/server/lib/backup_manager.js +117 -70
  29. package/src/server/lib/index_manager.js +63 -25
  30. package/src/server/lib/operation_dispatcher.js +339 -168
  31. package/src/server/lib/operations/admin.js +343 -205
  32. package/src/server/lib/operations/bulk_write.js +458 -194
  33. package/src/server/lib/operations/create_index.js +127 -34
  34. package/src/server/lib/operations/delete_many.js +204 -67
  35. package/src/server/lib/operations/delete_one.js +164 -52
  36. package/src/server/lib/operations/find.js +552 -319
  37. package/src/server/lib/operations/find_one.js +530 -304
  38. package/src/server/lib/operations/insert_one.js +147 -52
  39. package/src/server/lib/operations/update_one.js +334 -93
  40. package/src/server/lib/send_response.js +37 -17
  41. package/src/server/lib/tcp_protocol.js +158 -53
  42. package/test_data_api_key_1758233848259_cglfjzhou/data.mdb +0 -0
  43. package/test_data_api_key_1758233848259_cglfjzhou/lock.mdb +0 -0
  44. package/test_data_api_key_1758233848502_urlje2utd/data.mdb +0 -0
  45. package/test_data_api_key_1758233848502_urlje2utd/lock.mdb +0 -0
  46. package/test_data_api_key_1758233848738_mtcpfe5ns/data.mdb +0 -0
  47. package/test_data_api_key_1758233848738_mtcpfe5ns/lock.mdb +0 -0
  48. package/test_data_api_key_1758233848856_9g97p6gag/data.mdb +0 -0
  49. package/test_data_api_key_1758233848856_9g97p6gag/lock.mdb +0 -0
  50. package/test_data_api_key_1758233857008_0tl9zzhj8/data.mdb +0 -0
  51. package/test_data_api_key_1758233857008_0tl9zzhj8/lock.mdb +0 -0
  52. package/test_data_api_key_1758233857120_60c2f2uhu/data.mdb +0 -0
  53. package/test_data_api_key_1758233857120_60c2f2uhu/lock.mdb +0 -0
  54. package/test_data_api_key_1758233857232_aw7fkqgd9/data.mdb +0 -0
  55. package/test_data_api_key_1758233857232_aw7fkqgd9/lock.mdb +0 -0
  56. package/test_data_api_key_1758234881285_4aeflubjb/data.mdb +0 -0
  57. package/test_data_api_key_1758234881285_4aeflubjb/lock.mdb +0 -0
  58. package/test_data_api_key_1758234881520_kb0amvtqb/data.mdb +0 -0
  59. package/test_data_api_key_1758234881520_kb0amvtqb/lock.mdb +0 -0
  60. package/test_data_api_key_1758234881756_k04gfv2va/data.mdb +0 -0
  61. package/test_data_api_key_1758234881756_k04gfv2va/lock.mdb +0 -0
  62. package/test_data_api_key_1758234881876_wn90dpo1z/data.mdb +0 -0
  63. package/test_data_api_key_1758234881876_wn90dpo1z/lock.mdb +0 -0
  64. package/test_data_api_key_1758234889461_26xz3dmbr/data.mdb +0 -0
  65. package/test_data_api_key_1758234889461_26xz3dmbr/lock.mdb +0 -0
  66. package/test_data_api_key_1758234889572_uziz7e0p5/data.mdb +0 -0
  67. package/test_data_api_key_1758234889572_uziz7e0p5/lock.mdb +0 -0
  68. package/test_data_api_key_1758234889684_5f9wmposh/data.mdb +0 -0
  69. package/test_data_api_key_1758234889684_5f9wmposh/lock.mdb +0 -0
  70. package/test_data_api_key_1758235657729_prwgm6mxr/data.mdb +0 -0
  71. package/test_data_api_key_1758235657729_prwgm6mxr/lock.mdb +0 -0
  72. package/test_data_api_key_1758235657961_rc2da0dc2/data.mdb +0 -0
  73. package/test_data_api_key_1758235657961_rc2da0dc2/lock.mdb +0 -0
  74. package/test_data_api_key_1758235658193_oqqxm0sny/data.mdb +0 -0
  75. package/test_data_api_key_1758235658193_oqqxm0sny/lock.mdb +0 -0
  76. package/test_data_api_key_1758235658309_vggac1pj6/data.mdb +0 -0
  77. package/test_data_api_key_1758235658309_vggac1pj6/lock.mdb +0 -0
  78. package/test_data_api_key_1758235665968_61ko07dd1/data.mdb +0 -0
  79. package/test_data_api_key_1758235665968_61ko07dd1/lock.mdb +0 -0
  80. package/test_data_api_key_1758235666082_50lrt6sq8/data.mdb +0 -0
  81. package/test_data_api_key_1758235666082_50lrt6sq8/lock.mdb +0 -0
  82. package/test_data_api_key_1758235666194_ykvauwlzh/data.mdb +0 -0
  83. package/test_data_api_key_1758235666194_ykvauwlzh/lock.mdb +0 -0
  84. package/test_data_api_key_1758236187207_9c4paeh09/data.mdb +0 -0
  85. package/test_data_api_key_1758236187207_9c4paeh09/lock.mdb +0 -0
  86. package/test_data_api_key_1758236187441_4n3o3gkkl/data.mdb +0 -0
  87. package/test_data_api_key_1758236187441_4n3o3gkkl/lock.mdb +0 -0
  88. package/test_data_api_key_1758236187672_jt6b21ye0/data.mdb +0 -0
  89. package/test_data_api_key_1758236187672_jt6b21ye0/lock.mdb +0 -0
  90. package/test_data_api_key_1758236187788_oo84fz9u6/data.mdb +0 -0
  91. package/test_data_api_key_1758236187788_oo84fz9u6/lock.mdb +0 -0
  92. package/test_data_api_key_1758236195507_o9zeznwlm/data.mdb +0 -0
  93. package/test_data_api_key_1758236195507_o9zeznwlm/lock.mdb +0 -0
  94. package/test_data_api_key_1758236195619_qsqd60y41/data.mdb +0 -0
  95. package/test_data_api_key_1758236195619_qsqd60y41/lock.mdb +0 -0
  96. package/test_data_api_key_1758236195731_im13iq284/data.mdb +0 -0
  97. package/test_data_api_key_1758236195731_im13iq284/lock.mdb +0 -0
@@ -1 +1 @@
1
- class n{constructor(t,e){this.client=t,this.database_name=e}collection(t){const e=this.client.constructor.Collection;return new e(this.client,this.database_name,t)}async list_collections(){return this.client.send_request("admin",{admin_action:"list_collections",database:this.database_name})}async get_stats(){return this.client.send_request("admin",{admin_action:"get_database_stats",database:this.database_name})}async drop_database(){return this.client.send_request("admin",{admin_action:"drop_database",database:this.database_name})}async create_collection(t,e={}){return this.client.send_request("admin",{admin_action:"create_collection",database:this.database_name,collection:t,options:e})}async list_documents(t){return this.client.send_request("admin",{admin_action:"list_documents",database:this.database_name,collection:t})}async get_document(t,e){return this.client.send_request("admin",{admin_action:"get_document",database:this.database_name,collection:t,document_id:e})}async query_documents(t,e){return this.client.send_request("admin",{admin_action:"query_documents",database:this.database_name,collection:t,filter:e})}async insert_document(t,e){return this.client.send_request("admin",{admin_action:"insert_document",database:this.database_name,collection:t,document:e})}async update_document(t,e,a){return this.client.send_request("admin",{admin_action:"update_document",database:this.database_name,collection:t,document_id:e,update:a})}async delete_document(t,e){return this.client.send_request("admin",{admin_action:"delete_document",database:this.database_name,collection:t,document_id:e})}}var i=n;export{i as default};
1
+ const i=(c,t,e={})=>({admin_action:c,database:t,...e}),n=(c,t,e,a={})=>({admin_action:c,database:t,collection:e,...a}),s=async(c,t)=>c.send_request("admin",t);class r{constructor(t,e){this.client=t,this.database_name=e}collection(t){const e=this.client.constructor.Collection;return new e(this.client,this.database_name,t)}async list_collections(){const t=i("list_collections",this.database_name);return s(this.client,t)}async get_stats(){const t=i("get_database_stats",this.database_name);return s(this.client,t)}async drop_database(){const t=i("drop_database",this.database_name);return s(this.client,t)}async create_collection(t,e={}){const a=n("create_collection",this.database_name,t,{options:e});return s(this.client,a)}async list_documents(t){const e=n("list_documents",this.database_name,t);return s(this.client,e)}async get_document(t,e){const a=n("get_document",this.database_name,t,{document_id:e});return s(this.client,a)}async query_documents(t,e){const a=n("query_documents",this.database_name,t,{filter:e});return s(this.client,a)}async insert_document(t,e){const a=n("insert_document",this.database_name,t,{document:e});return s(this.client,a)}async update_document(t,e,a){const d=n("update_document",this.database_name,t,{document_id:e,update:a});return s(this.client,d)}async delete_document(t,e){const a=n("delete_document",this.database_name,t,{document_id:e});return s(this.client,a)}}var _=r;export{_ as default};
@@ -1 +1 @@
1
- import u from"net";import{EventEmitter as h}from"events";import{encode as d,decode as l}from"msgpackr";import m from"./database.js";const p=n=>{const e=d(n,{useFloat32:!1,int64AsType:"number",mapsAsObjects:!0}),t=Buffer.allocUnsafe(4);return t.writeUInt32BE(e.length,0),Buffer.concat([t,e])},f=()=>{let n=Buffer.alloc(0),e=null;return{parse_messages:i=>{n=Buffer.concat([n,i]);const r=[];for(;n.length>0;){if(e===null){if(n.length<4)break;e=n.readUInt32BE(0),n=n.slice(4)}if(n.length<e)break;const a=n.slice(0,e);n=n.slice(e),e=null;try{const c=l(a,{useFloat32:!1,int64AsType:"number",mapsAsObjects:!0});r.push(c)}catch(c){throw new Error(`Invalid message format: ${c.message}`)}}return r},reset:()=>{n=Buffer.alloc(0),e=null}}};class _ extends h{constructor(e={}){super(),this.host=e.host||"localhost",this.port=e.port||1983,this.password=e.password||null,this.timeout=e.timeout||5e3,this.reconnect=e.reconnect!==!1,this.max_reconnect_attempts=e.max_reconnect_attempts||10,this.reconnect_delay=e.reconnect_delay||1e3,this.socket=null,this.message_parser=null,this.is_connected=!1,this.is_authenticated=!1,this.is_connecting=!1,this.reconnect_attempts=0,this.reconnect_timeout=null,this.pending_requests=new Map,this.request_id_counter=0,this.request_queue=[],e.auto_connect!==!1&&this.connect()}connect(){if(this.is_connecting||this.is_connected)return;this.is_connecting=!0,this.socket=new u.Socket,this.message_parser=f();const e=setTimeout(()=>{this.socket&&!this.is_connected&&(this.socket.destroy(),this.handle_connection_error(new Error("Connection timeout")))},this.timeout);this.socket.connect(this.port,this.host,()=>{clearTimeout(e),this.is_connected=!0,this.is_connecting=!1,this.reconnect_attempts=0,this.emit("connect"),this.password?this.authenticate():(this.is_authenticated=!0,this.emit("authenticated"),this.process_request_queue())}),this.socket.on("data",t=>{try{const s=this.message_parser.parse_messages(t);for(const i of s)this.handle_message(i)}catch(s){this.emit("error",new Error(`Message parsing failed: ${s.message}`))}}),this.socket.on("error",t=>{clearTimeout(e),this.handle_connection_error(t)}),this.socket.on("close",()=>{clearTimeout(e),this.handle_disconnect()})}async authenticate(){if(!this.password){this.emit("error",new Error('Password required for authentication. Provide password in client options: joystickdb.client({ password: "your_password" })')),this.disconnect();return}try{if((await this.send_request("authentication",{password:this.password})).ok===1)this.is_authenticated=!0,this.emit("authenticated"),this.process_request_queue();else throw new Error("Authentication failed")}catch(e){this.emit("error",new Error(`Authentication error: ${e.message}`)),this.disconnect()}}handle_message(e){if(this.pending_requests.size>0){const[t,{resolve:s,reject:i,timeout:r}]=this.pending_requests.entries().next().value;if(clearTimeout(r),this.pending_requests.delete(t),e.ok===1||e.ok===!0)s(e);else if(e.ok===0||e.ok===!1){const a=typeof e.error=="string"?e.error:JSON.stringify(e.error)||"Operation failed";i(new Error(a))}else s(e)}else this.emit("response",e)}handle_connection_error(e){this.is_connecting=!1,this.is_connected=!1,this.is_authenticated=!1,this.socket&&(this.socket.removeAllListeners(),this.socket.destroy(),this.socket=null),this.message_parser&&this.message_parser.reset();for(const[t,{reject:s,timeout:i}]of this.pending_requests)clearTimeout(i),s(new Error("Connection lost"));this.pending_requests.clear(),this.emit("error",e),this.reconnect&&this.reconnect_attempts<this.max_reconnect_attempts?this.schedule_reconnect():this.emit("disconnect")}handle_disconnect(){this.is_connected=!1,this.is_authenticated=!1,this.is_connecting=!1,this.socket&&(this.socket.removeAllListeners(),this.socket=null),this.message_parser&&this.message_parser.reset();for(const[e,{reject:t,timeout:s}]of this.pending_requests)clearTimeout(s),t(new Error("Connection closed"));this.pending_requests.clear(),this.reconnect&&this.reconnect_attempts<this.max_reconnect_attempts?this.schedule_reconnect():this.emit("disconnect")}schedule_reconnect(){this.reconnect_attempts++;const e=Math.min(this.reconnect_delay*Math.pow(2,this.reconnect_attempts-1),3e4);this.emit("reconnecting",{attempt:this.reconnect_attempts,delay:e}),this.reconnect_timeout=setTimeout(()=>{this.connect()},e)}send_request(e,t={},s=!0){return new Promise((i,r)=>{const a=++this.request_id_counter,o={message:{op:e,data:t},resolve:i,reject:r,request_id:a};if(!this.is_connected||e!=="authentication"&&e!=="setup"&&e!=="ping"&&!this.is_authenticated)if(s){this.request_queue.push(o);return}else{r(new Error("Not connected or authenticated"));return}this.send_request_now(o)})}send_request_now(e){const{message:t,resolve:s,reject:i,request_id:r}=e,a=setTimeout(()=>{this.pending_requests.delete(r),i(new Error("Request timeout"))},this.timeout);this.pending_requests.set(r,{resolve:s,reject:i,timeout:a});try{const c=p(t);this.socket.write(c)}catch(c){clearTimeout(a),this.pending_requests.delete(r),i(c)}}process_request_queue(){for(;this.request_queue.length>0&&this.is_connected&&this.is_authenticated;){const e=this.request_queue.shift();this.send_request_now(e)}}disconnect(){this.reconnect=!1,this.reconnect_timeout&&(clearTimeout(this.reconnect_timeout),this.reconnect_timeout=null),this.socket&&this.socket.end()}async backup_now(){return this.send_request("admin",{admin_action:"backup_now"})}async list_backups(){return this.send_request("admin",{admin_action:"list_backups"})}async restore_backup(e){return this.send_request("admin",{admin_action:"restore_backup",backup_name:e})}async get_replication_status(){return this.send_request("admin",{admin_action:"get_replication_status"})}async add_secondary(e){return this.send_request("admin",{admin_action:"add_secondary",...e})}async remove_secondary(e){return this.send_request("admin",{admin_action:"remove_secondary",secondary_id:e})}async sync_secondaries(){return this.send_request("admin",{admin_action:"sync_secondaries"})}async get_secondary_health(){return this.send_request("admin",{admin_action:"get_secondary_health"})}async get_forwarder_status(){return this.send_request("admin",{admin_action:"get_forwarder_status"})}async ping(){return this.send_request("ping",{},!1)}async reload(){return this.send_request("reload")}async get_auto_index_stats(){return this.send_request("admin",{admin_action:"get_auto_index_stats"})}async setup(){const e=await this.send_request("setup",{},!1);return e.data&&e.data.instructions&&console.log(e.data.instructions),e}async delete_many(e,t={},s={}){return this.send_request("delete_many",{collection:e,filter:t,options:s})}db(e){return new m(this,e)}async list_databases(){return this.send_request("admin",{admin_action:"list_databases"})}}class q{constructor(e,t,s){this.client=e,this.database_name=t,this.collection_name=s}async insert_one(e,t={}){return this.client.send_request("insert_one",{database:this.database_name,collection:this.collection_name,document:e,options:t})}async find_one(e={},t={}){return(await this.client.send_request("find_one",{database:this.database_name,collection:this.collection_name,filter:e,options:t})).document}async find(e={},t={}){return(await this.client.send_request("find",{database:this.database_name,collection:this.collection_name,filter:e,options:t})).documents||[]}async update_one(e,t,s={}){return this.client.send_request("update_one",{database:this.database_name,collection:this.collection_name,filter:e,update:t,options:s})}async delete_one(e,t={}){return this.client.send_request("delete_one",{database:this.database_name,collection:this.collection_name,filter:e,options:t})}async delete_many(e={},t={}){return this.client.send_request("delete_many",{database:this.database_name,collection:this.collection_name,filter:e,options:t})}async bulk_write(e,t={}){return this.client.send_request("bulk_write",{database:this.database_name,collection:this.collection_name,operations:e,options:t})}async create_index(e,t={}){return this.client.send_request("create_index",{database:this.database_name,collection:this.collection_name,field:e,options:t})}async upsert_index(e,t={}){return this.client.send_request("create_index",{database:this.database_name,collection:this.collection_name,field:e,options:{...t,upsert:!0}})}async drop_index(e){return this.client.send_request("drop_index",{database:this.database_name,collection:this.collection_name,field:e})}async get_indexes(){return this.client.send_request("get_indexes",{database:this.database_name,collection:this.collection_name})}}_.Collection=q;const g={client:n=>new _(n)};var x=g;export{x as default};
1
+ import h from"net";import{EventEmitter as l}from"events";import{encode as m,decode as p}from"msgpackr";import f from"./database.js";const _=()=>({useFloat32:!1,int64AsType:"number",mapsAsObjects:!0}),g=s=>{const e=m(s,_()),t=Buffer.allocUnsafe(4);return t.writeUInt32BE(e.length,0),Buffer.concat([t,e])},q=(s,e)=>{const t=s.slice(0,e),n=s.slice(e);try{return{message:p(t,_()),buffer:n}}catch(i){throw new Error(`Invalid message format: ${i.message}`)}},y=s=>{if(s.length<4)return{expected_length:null,buffer:s};const e=s.readUInt32BE(0),t=s.slice(4);return{expected_length:e,buffer:t}},b=()=>{let s=Buffer.alloc(0),e=null;return{parse_messages:i=>{s=Buffer.concat([s,i]);const r=[];for(;s.length>0;){if(e===null){const a=y(s);if(e=a.expected_length,s=a.buffer,e===null)break}if(s.length<e)break;const c=q(s,e);r.push(c.message),s=c.buffer,e=null}return r},reset:()=>{s=Buffer.alloc(0),e=null}}},w=(s,e)=>Math.min(e*Math.pow(2,s-1),3e4),k=(s={})=>({host:s.host||"localhost",port:s.port||1983,password:s.password||null,timeout:s.timeout||5e3,reconnect:s.reconnect!==!1,max_reconnect_attempts:s.max_reconnect_attempts||10,reconnect_delay:s.reconnect_delay||1e3,auto_connect:s.auto_connect!==!1}),x=(s,e,t)=>setTimeout(()=>{s&&!s.destroyed&&(s.destroy(),e(new Error("Connection timeout")))},t),E=(s,e,t)=>setTimeout(()=>{const n=s.get(e);n&&(s.delete(e),n.reject(new Error("Request timeout")))},t),u=(s,e)=>{for(const[t,{reject:n,timeout:i}]of s)clearTimeout(i),n(new Error(e));s.clear()},T=s=>s.ok===1||s.ok===!0,v=s=>s.ok===0||s.ok===!1,B=s=>typeof s.error=="string"?s.error:JSON.stringify(s.error)||"Operation failed";class d extends l{constructor(e={}){super();const t=k(e);this.host=t.host,this.port=t.port,this.password=t.password,this.timeout=t.timeout,this.reconnect=t.reconnect,this.max_reconnect_attempts=t.max_reconnect_attempts,this.reconnect_delay=t.reconnect_delay,this.socket=null,this.message_parser=null,this.is_connected=!1,this.is_authenticated=!1,this.is_connecting=!1,this.reconnect_attempts=0,this.reconnect_timeout=null,this.pending_requests=new Map,this.request_id_counter=0,this.request_queue=[],t.auto_connect&&this.connect()}connect(){if(this.is_connecting||this.is_connected)return;this.is_connecting=!0,this.socket=new h.Socket,this.message_parser=b();const e=x(this.socket,this.handle_connection_error.bind(this),this.timeout);this.setup_socket_handlers(e),this.socket.connect(this.port,this.host,()=>{this.handle_successful_connection(e)})}setup_socket_handlers(e){this.socket.on("data",t=>{this.handle_incoming_data(t)}),this.socket.on("error",t=>{clearTimeout(e),this.handle_connection_error(t)}),this.socket.on("close",()=>{clearTimeout(e),this.handle_disconnect()})}handle_successful_connection(e){clearTimeout(e),this.is_connected=!0,this.is_connecting=!1,this.reconnect_attempts=0,this.emit("connect"),this.password?this.authenticate():this.handle_authentication_complete()}handle_authentication_complete(){this.is_authenticated=!0,this.emit("authenticated"),this.process_request_queue()}handle_incoming_data(e){try{const t=this.message_parser.parse_messages(e);for(const n of t)this.handle_message(n)}catch(t){this.emit("error",new Error(`Message parsing failed: ${t.message}`))}}async authenticate(){if(!this.password){this.emit("error",new Error('Password required for authentication. Provide password in client options: joystickdb.client({ password: "your_password" })')),this.disconnect();return}try{if((await this.send_request("authentication",{password:this.password})).ok===1)this.handle_authentication_complete();else throw new Error("Authentication failed")}catch(e){this.emit("error",new Error(`Authentication error: ${e.message}`)),this.disconnect()}}handle_message(e){this.pending_requests.size>0?this.handle_pending_request_response(e):this.emit("response",e)}handle_pending_request_response(e){const[t,{resolve:n,reject:i,timeout:r}]=this.pending_requests.entries().next().value;if(clearTimeout(r),this.pending_requests.delete(t),T(e))n(e);else if(v(e)){const c=B(e);i(new Error(c))}else n(e)}handle_connection_error(e){this.reset_connection_state(),u(this.pending_requests,"Connection lost"),this.emit("error",e),this.should_attempt_reconnect()?this.schedule_reconnect():this.emit("disconnect")}handle_disconnect(){this.reset_connection_state(),u(this.pending_requests,"Connection closed"),this.should_attempt_reconnect()?this.schedule_reconnect():this.emit("disconnect")}reset_connection_state(){this.is_connecting=!1,this.is_connected=!1,this.is_authenticated=!1,this.socket&&(this.socket.removeAllListeners(),this.socket.destroy(),this.socket=null),this.message_parser&&this.message_parser.reset()}should_attempt_reconnect(){return this.reconnect&&this.reconnect_attempts<this.max_reconnect_attempts}schedule_reconnect(){this.reconnect_attempts++;const e=w(this.reconnect_attempts,this.reconnect_delay);this.emit("reconnecting",{attempt:this.reconnect_attempts,delay:e}),this.reconnect_timeout=setTimeout(()=>{this.connect()},e)}send_request(e,t={},n=!0){return new Promise((i,r)=>{const c=++this.request_id_counter,o={message:{op:e,data:t},resolve:i,reject:r,request_id:c};if(this.should_queue_request(e,n)){this.request_queue.push(o);return}this.send_request_now(o)})}should_queue_request(e,t){const i=!["authentication","setup","ping"].includes(e);return(!this.is_connected||i&&!this.is_authenticated)&&t}send_request_now(e){const{message:t,resolve:n,reject:i,request_id:r}=e,c=E(this.pending_requests,r,this.timeout);this.pending_requests.set(r,{resolve:n,reject:i,timeout:c});try{const a=g(t);this.socket.write(a)}catch(a){clearTimeout(c),this.pending_requests.delete(r),i(a)}}process_request_queue(){for(;this.request_queue.length>0&&this.is_connected&&this.is_authenticated;){const e=this.request_queue.shift();this.send_request_now(e)}}disconnect(){this.reconnect=!1,this.reconnect_timeout&&(clearTimeout(this.reconnect_timeout),this.reconnect_timeout=null),this.socket&&this.socket.end()}async backup_now(){return this.send_request("admin",{admin_action:"backup_now"})}async list_backups(){return this.send_request("admin",{admin_action:"list_backups"})}async restore_backup(e){return this.send_request("admin",{admin_action:"restore_backup",backup_name:e})}async get_replication_status(){return this.send_request("admin",{admin_action:"get_replication_status"})}async add_secondary(e){return this.send_request("admin",{admin_action:"add_secondary",...e})}async remove_secondary(e){return this.send_request("admin",{admin_action:"remove_secondary",secondary_id:e})}async sync_secondaries(){return this.send_request("admin",{admin_action:"sync_secondaries"})}async get_secondary_health(){return this.send_request("admin",{admin_action:"get_secondary_health"})}async get_forwarder_status(){return this.send_request("admin",{admin_action:"get_forwarder_status"})}async ping(){return this.send_request("ping",{},!1)}async reload(){return this.send_request("reload")}async get_auto_index_stats(){return this.send_request("admin",{admin_action:"get_auto_index_stats"})}async setup(){const e=await this.send_request("setup",{},!1);return e.data&&e.data.instructions&&console.log(e.data.instructions),e}async delete_many(e,t={},n={}){return this.send_request("delete_many",{collection:e,filter:t,options:n})}db(e){return new f(this,e)}async list_databases(){return this.send_request("admin",{admin_action:"list_databases"})}}class j{constructor(e,t,n){this.client=e,this.database_name=t,this.collection_name=n}async insert_one(e,t={}){return this.client.send_request("insert_one",{database:this.database_name,collection:this.collection_name,document:e,options:t})}async find_one(e={},t={}){return(await this.client.send_request("find_one",{database:this.database_name,collection:this.collection_name,filter:e,options:t})).document}async find(e={},t={}){return(await this.client.send_request("find",{database:this.database_name,collection:this.collection_name,filter:e,options:t})).documents||[]}async update_one(e,t,n={}){return this.client.send_request("update_one",{database:this.database_name,collection:this.collection_name,filter:e,update:t,options:n})}async delete_one(e,t={}){return this.client.send_request("delete_one",{database:this.database_name,collection:this.collection_name,filter:e,options:t})}async delete_many(e={},t={}){return this.client.send_request("delete_many",{database:this.database_name,collection:this.collection_name,filter:e,options:t})}async bulk_write(e,t={}){return this.client.send_request("bulk_write",{database:this.database_name,collection:this.collection_name,operations:e,options:t})}async create_index(e,t={}){return this.client.send_request("create_index",{database:this.database_name,collection:this.collection_name,field:e,options:t})}async upsert_index(e,t={}){return this.client.send_request("create_index",{database:this.database_name,collection:this.collection_name,field:e,options:{...t,upsert:!0}})}async drop_index(e){return this.client.send_request("drop_index",{database:this.database_name,collection:this.collection_name,field:e})}async get_indexes(){return this.client.send_request("get_indexes",{database:this.database_name,collection:this.collection_name})}}d.Collection=j;const C={client:s=>new d(s)};var P=C;export{P as default};
@@ -1,10 +1,10 @@
1
- import a from"cluster";import l from"os";import{EventEmitter as u}from"events";import{writeFileSync as p}from"fs";import{load_settings as _,get_settings as w,get_port_configuration as h}from"../lib/load_settings.js";import{restore_backup as g,start_backup_schedule as f,stop_backup_schedule as m}from"../lib/backup_manager.js";import k from"../lib/logger.js";import{initialize_database as y,check_and_grow_map_size as b,cleanup_database as v}from"../lib/query_engine.js";import{setup_authentication as q,verify_password as D,initialize_auth_manager as x}from"../lib/auth_manager.js";import E from"../lib/operations/insert_one.js";import T from"../lib/operations/update_one.js";import S from"../lib/operations/delete_one.js";import W from"../lib/operations/bulk_write.js";import F from"../lib/operations/find_one.js";import N from"../lib/operations/find.js";import P from"../lib/operations/create_index.js";import z from"../lib/operations/drop_index.js";import O from"../lib/operations/get_indexes.js";import{start_http_server as R,stop_http_server as A,is_setup_required as I}from"../lib/http_server.js";import{is_development_mode as j,display_development_startup_message as L}from"../lib/development_mode.js";import{initialize_api_key_manager as M}from"../lib/api_key_manager.js";class $ extends u{constructor(r={}){super(),this.workers=new Map,this.write_queue=[],this.processing_writes=!1,this.authenticated_sessions=new Map,this.worker_count=r.worker_count||l.cpus().length,this.port=r.port||1983,this.settings_file=r.settings_file||"settings.db.json",this.settings=null,this.pending_writes=new Map,this.write_id_counter=0,this.shutting_down=!1,this.master_id=`master_${Date.now()}_${Math.random()}`;const{create_context_logger:e}=k("master");this.log=e({port:this.port,worker_count:this.worker_count,master_id:this.master_id}),this.setup_master()}setup_master(){a.setupPrimary({exec:new URL("./index.js",import.meta.url).pathname,args:[],silent:!1}),a.on("exit",(r,e,o)=>{this.log.warn("Worker died",{worker_pid:r.process.pid,exit_code:e,signal:o}),this.handle_worker_death(r)}),a.on("message",(r,e)=>{this.handle_worker_message(r,e)})}async start(){const r=Date.now();try{this.settings=_(this.settings_file),this.log.info("Settings loaded successfully",{settings_file:this.settings_file});let e="./data";try{const t=w();t?.data_path&&(e=t.data_path)}catch{}if(y(e),x(),await M(),this.log.info("Database and auth manager initialized"),this.settings?.restore_from)try{this.log.info("Startup restore requested",{backup_filename:this.settings.restore_from});const t=await g(this.settings.restore_from);this.log.info("Startup restore completed",{backup_filename:this.settings.restore_from,duration_ms:t.duration_ms});const s={...this.settings};delete s.restore_from,p(this.settings_file,JSON.stringify(s,null,2)),this.settings=_(this.settings_file),this.log.info("Removed restore_from from settings after successful restore")}catch(t){this.log.error("Startup restore failed",{backup_filename:this.settings.restore_from,error:t.message}),this.log.info("Continuing with existing database after restore failure")}if(this.settings?.s3)try{f(),this.log.info("Backup scheduling started")}catch(t){this.log.warn("Failed to start backup scheduling",{error:t.message})}if(I())try{const{http_port:t}=h();await R(t)&&this.log.info("HTTP setup server started",{http_port:t})}catch(t){this.log.warn("Failed to start HTTP setup server",{error:t.message})}for(let t=0;t<this.worker_count;t++)this.spawn_worker();if(j()){const{tcp_port:t,http_port:s}=h();L(t,s)}const o=Date.now()-r;this.log.info("Master process started successfully",{workers_spawned:this.worker_count,startup_duration_ms:o})}catch(e){this.log.error("Failed to start master process",{error:e.message}),process.exit(1)}}spawn_worker(){const r=Date.now();this.log.info("Spawning worker");const e=a.fork({WORKER_PORT:this.port,WORKER_SETTINGS:JSON.stringify(this.settings)});this.workers.set(e.id,{worker:e,connections:0,last_heartbeat:Date.now(),status:"starting"});const o=Date.now()-r;return this.log.info("Worker spawned successfully",{worker_id:e.id,worker_pid:e.process.pid,spawn_duration_ms:o}),e}handle_worker_death(r){this.workers.delete(r.id),this.shutting_down||(this.log.info("Respawning worker after death",{dead_worker_id:r.id,respawn_delay_ms:1e3}),setTimeout(()=>{this.spawn_worker()},1e3))}handle_worker_message(r,e){switch(e.type){case"worker_ready":this.handle_worker_ready_for_config(r,e);break;case"server_ready":this.handle_worker_server_ready(r,e);break;case"write_request":this.handle_write_request(r,e);break;case"auth_request":this.handle_auth_request(r,e);break;case"setup_request":this.handle_setup_request(r,e);break;case"connection_count":this.update_worker_connections(r,e);break;case"heartbeat":this.handle_worker_heartbeat(r,e);break;default:this.log.warn("Unknown message type received",{message_type:e.type,worker_id:r.id})}}handle_worker_ready_for_config(r,e){this.log.info("Worker ready for config, sending configuration",{worker_id:r.id,worker_pid:r.process.pid,master_id:this.master_id}),r.send({type:"config",data:{port:this.port,settings:this.settings,master_id:this.master_id}})}handle_worker_server_ready(r,e){const o=this.workers.get(r.id);o&&(o.status="ready",this.log.info("Worker server ready",{worker_id:r.id,worker_pid:r.process.pid}))}async handle_write_request(r,e){if(this.shutting_down){r.send({type:"write_response",data:{write_id:e.data.write_id,success:!1,error:"Server is shutting down"}});return}const{write_id:o,op_type:t,data:s,socket_id:i}=e.data;try{const n={write_id:o,worker_id:r.id,op_type:t,data:s,socket_id:i,timestamp:Date.now()};this.write_queue.push(n),this.process_write_queue()}catch(n){r.send({type:"write_response",data:{write_id:o,success:!1,error:n.message}})}}async process_write_queue(){if(!(this.processing_writes||this.write_queue.length===0)){for(this.processing_writes=!0;this.write_queue.length>0;){const r=this.write_queue.shift();await this.execute_write_operation(r)}this.processing_writes=!1,this.shutting_down&&this.write_queue.length===0&&this.emit("writes_completed")}}async execute_write_operation(r){const{write_id:e,worker_id:o,op_type:t,data:s,socket_id:i}=r,n=this.workers.get(o);if(!n){this.log.error("Worker not found for write operation",{worker_id:o});return}try{const c=await this.perform_database_operation(t,s);n.worker.send({type:"write_response",data:{write_id:e,success:!0,result:c}}),this.broadcast_write_notification(t,s,o)}catch(c){this.log.error("Write operation failed",{write_id:e,op_type:t,worker_id:o,error_message:c.message}),n.worker.send({type:"write_response",data:{write_id:e,success:!1,error:c.message}})}}async perform_database_operation(r,e){const o=Date.now();this.log.info("Executing database operation",{op_type:r});try{let t;const s=e.database||"default";switch(r){case"insert_one":t=await E(s,e.collection,e.document,e.options);break;case"update_one":t=await T(s,e.collection,e.filter,e.update,e.options);break;case"delete_one":t=await S(s,e.collection,e.filter,e.options);break;case"bulk_write":t=await W(s,e.collection,e.operations,e.options);break;case"find_one":t=await F(s,e.collection,e.filter,e.options);break;case"find":t=await N(s,e.collection,e.filter,e.options);break;case"create_index":t=await P(s,e.collection,e.field,e.options);break;case"drop_index":t=await z(s,e.collection,e.field);break;case"get_indexes":t=await O(s,e.collection);break;default:throw new Error(`Unsupported database operation: ${r}`)}const i=Date.now()-o;return this.log.log_operation(r,i,{result:t}),["find_one","find","get_indexes"].includes(r)||setImmediate(()=>b()),t}catch(t){const s=Date.now()-o;throw this.log.error("Database operation failed",{op_type:r,duration_ms:s,error_message:t.message}),t}}broadcast_write_notification(r,e,o){const t={type:"write_notification",data:{op_type:r,data:e,timestamp:Date.now()}};for(const[s,i]of this.workers)s!==o&&i.status==="ready"&&i.worker.send(t)}async handle_auth_request(r,e){const{auth_id:o,socket_id:t,password:s}=e.data;try{const i=await D(s,"cluster_client");i&&this.authenticated_sessions.set(t,{authenticated_at:Date.now(),worker_id:r.id}),r.send({type:"auth_response",data:{auth_id:o,success:i,message:i?"Authentication successful":"Authentication failed"}})}catch(i){r.send({type:"auth_response",data:{auth_id:o,success:!1,message:`Authentication error: ${i.message}`}})}}handle_setup_request(r,e){const{setup_id:o,socket_id:t}=e.data;try{const s=q(),i=`===
1
+ import n from"cluster";import l from"os";import{EventEmitter as d}from"events";import{writeFileSync as u}from"fs";import{load_settings as c,get_settings as p,get_port_configuration as h}from"../lib/load_settings.js";import{restore_backup as w,start_backup_schedule as g,stop_backup_schedule as f}from"../lib/backup_manager.js";import m from"../lib/logger.js";import{initialize_database as k,check_and_grow_map_size as y,cleanup_database as b}from"../lib/query_engine.js";import{setup_authentication as v,verify_password as q,initialize_auth_manager as D}from"../lib/auth_manager.js";import x from"../lib/operations/insert_one.js";import E from"../lib/operations/update_one.js";import T from"../lib/operations/delete_one.js";import S from"../lib/operations/bulk_write.js";import z from"../lib/operations/find_one.js";import W from"../lib/operations/find.js";import F from"../lib/operations/create_index.js";import N from"../lib/operations/drop_index.js";import P from"../lib/operations/get_indexes.js";import{start_http_server as O,stop_http_server as R,is_setup_required as A}from"../lib/http_server.js";import{is_development_mode as I,display_development_startup_message as j}from"../lib/development_mode.js";import{initialize_api_key_manager as L}from"../lib/api_key_manager.js";class M extends d{constructor(e={}){super(),this.workers=new Map,this.write_queue=[],this.processing_writes=!1,this.authenticated_sessions=new Map,this.worker_count=e.worker_count||l.cpus().length,this.port=e.port||1983,this.settings_file=e.settings_file||"settings.db.json",this.settings=null,this.pending_writes=new Map,this.write_id_counter=0,this.shutting_down=!1,this.master_id=`master_${Date.now()}_${Math.random()}`;const{create_context_logger:t}=m("master");this.log=t({port:this.port,worker_count:this.worker_count,master_id:this.master_id}),this.setup_master()}setup_master(){n.setupPrimary({exec:new URL("./index.js",import.meta.url).pathname,args:[],silent:!1}),n.on("exit",(e,t,s)=>{this.log.warn("Worker died",{worker_pid:e.process.pid,exit_code:t,signal:s}),this.handle_worker_death(e)}),n.on("message",(e,t)=>{this.handle_worker_message(e,t)})}get_database_path(){let e="./data";try{const t=p();t?.data_path&&(e=t.data_path)}catch{}return e}async initialize_core_systems(){const e=this.get_database_path();k(e),D(),await L(),this.log.info("Database and auth manager initialized")}async handle_startup_restore(){if(this.settings?.restore_from)try{this.log.info("Startup restore requested",{backup_filename:this.settings.restore_from});const e=await w(this.settings.restore_from);this.log.info("Startup restore completed",{backup_filename:this.settings.restore_from,duration_ms:e.duration_ms}),this.remove_restore_from_settings()}catch(e){this.log.error("Startup restore failed",{backup_filename:this.settings.restore_from,error:e.message}),this.log.info("Continuing with existing database after restore failure")}}remove_restore_from_settings(){const e={...this.settings};delete e.restore_from,u(this.settings_file,JSON.stringify(e,null,2)),this.settings=c(this.settings_file),this.log.info("Removed restore_from from settings after successful restore")}start_backup_scheduling(){if(this.settings?.s3)try{g(),this.log.info("Backup scheduling started")}catch(e){this.log.warn("Failed to start backup scheduling",{error:e.message})}}async start_setup_server(){if(A())try{const{http_port:e}=h();await O(e)&&this.log.info("HTTP setup server started",{http_port:e})}catch(e){this.log.warn("Failed to start HTTP setup server",{error:e.message})}}spawn_all_workers(){for(let e=0;e<this.worker_count;e++)this.spawn_worker()}display_development_message(){if(I()){const{tcp_port:e,http_port:t}=h();j(e,t)}}async start(){const e=Date.now();try{this.settings=c(this.settings_file),this.log.info("Settings loaded successfully",{settings_file:this.settings_file}),await this.initialize_core_systems(),await this.handle_startup_restore(),this.start_backup_scheduling(),await this.start_setup_server(),this.spawn_all_workers(),this.display_development_message();const t=Date.now()-e;this.log.info("Master process started successfully",{workers_spawned:this.worker_count,startup_duration_ms:t})}catch(t){this.log.error("Failed to start master process",{error:t.message}),process.exit(1)}}spawn_worker(){const e=Date.now();this.log.info("Spawning worker");const t=n.fork({WORKER_PORT:this.port,WORKER_SETTINGS:JSON.stringify(this.settings)});this.workers.set(t.id,{worker:t,connections:0,last_heartbeat:Date.now(),status:"starting"});const s=Date.now()-e;return this.log.info("Worker spawned successfully",{worker_id:t.id,worker_pid:t.process.pid,spawn_duration_ms:s}),t}handle_worker_death(e){this.workers.delete(e.id),this.shutting_down||(this.log.info("Respawning worker after death",{dead_worker_id:e.id,respawn_delay_ms:1e3}),setTimeout(()=>{this.spawn_worker()},1e3))}handle_worker_message(e,t){switch(t.type){case"worker_ready":this.handle_worker_ready_for_config(e,t);break;case"server_ready":this.handle_worker_server_ready(e,t);break;case"write_request":this.handle_write_request(e,t);break;case"auth_request":this.handle_auth_request(e,t);break;case"setup_request":this.handle_setup_request(e,t);break;case"connection_count":this.update_worker_connections(e,t);break;case"heartbeat":this.handle_worker_heartbeat(e,t);break;default:this.log.warn("Unknown message type received",{message_type:t.type,worker_id:e.id})}}handle_worker_ready_for_config(e,t){this.log.info("Worker ready for config, sending configuration",{worker_id:e.id,worker_pid:e.process.pid,master_id:this.master_id}),e.send({type:"config",data:{port:this.port,settings:this.settings,master_id:this.master_id}})}handle_worker_server_ready(e,t){const s=this.workers.get(e.id);s&&(s.status="ready",this.log.info("Worker server ready",{worker_id:e.id,worker_pid:e.process.pid}))}async handle_write_request(e,t){if(this.shutting_down){e.send({type:"write_response",data:{write_id:t.data.write_id,success:!1,error:"Server is shutting down"}});return}const{write_id:s,op_type:r,data:i,socket_id:o}=t.data;try{const a={write_id:s,worker_id:e.id,op_type:r,data:i,socket_id:o,timestamp:Date.now()};this.write_queue.push(a),this.process_write_queue()}catch(a){e.send({type:"write_response",data:{write_id:s,success:!1,error:a.message}})}}async process_write_queue(){if(!(this.processing_writes||this.write_queue.length===0)){for(this.processing_writes=!0;this.write_queue.length>0;){const e=this.write_queue.shift();await this.execute_write_operation(e)}this.processing_writes=!1,this.shutting_down&&this.write_queue.length===0&&this.emit("writes_completed")}}async execute_write_operation(e){const{write_id:t,worker_id:s,op_type:r,data:i,socket_id:o}=e,a=this.workers.get(s);if(!a){this.log.error("Worker not found for write operation",{worker_id:s});return}try{const _=await this.perform_database_operation(r,i);a.worker.send({type:"write_response",data:{write_id:t,success:!0,result:_}}),this.broadcast_write_notification(r,i,s)}catch(_){this.log.error("Write operation failed",{write_id:t,op_type:r,worker_id:s,error_message:_.message}),a.worker.send({type:"write_response",data:{write_id:t,success:!1,error:_.message}})}}async perform_database_operation(e,t){const s=Date.now();this.log.info("Executing database operation",{op_type:e});try{let r;const i=t.database||"default";switch(e){case"insert_one":r=await x(i,t.collection,t.document,t.options);break;case"update_one":r=await E(i,t.collection,t.filter,t.update,t.options);break;case"delete_one":r=await T(i,t.collection,t.filter,t.options);break;case"bulk_write":r=await S(i,t.collection,t.operations,t.options);break;case"find_one":r=await z(i,t.collection,t.filter,t.options);break;case"find":r=await W(i,t.collection,t.filter,t.options);break;case"create_index":r=await F(i,t.collection,t.field,t.options);break;case"drop_index":r=await N(i,t.collection,t.field);break;case"get_indexes":r=await P(i,t.collection);break;default:throw new Error(`Unsupported database operation: ${e}`)}const o=Date.now()-s;return this.log.log_operation(e,o,{result:r}),["find_one","find","get_indexes"].includes(e)||setImmediate(()=>y()),r}catch(r){const i=Date.now()-s;throw this.log.error("Database operation failed",{op_type:e,duration_ms:i,error_message:r.message}),r}}broadcast_write_notification(e,t,s){const r={type:"write_notification",data:{op_type:e,data:t,timestamp:Date.now()}};for(const[i,o]of this.workers)i!==s&&o.status==="ready"&&o.worker.send(r)}async handle_auth_request(e,t){const{auth_id:s,socket_id:r,password:i}=t.data;try{const o=await q(i,"cluster_client");o&&this.authenticated_sessions.set(r,{authenticated_at:Date.now(),worker_id:e.id}),e.send({type:"auth_response",data:{auth_id:s,success:o,message:o?"Authentication successful":"Authentication failed"}})}catch(o){e.send({type:"auth_response",data:{auth_id:s,success:!1,message:`Authentication error: ${o.message}`}})}}handle_setup_request(e,t){const{setup_id:s,socket_id:r}=t.data;try{const i=v(),o=`===
2
2
  JoystickDB Setup
3
3
 
4
4
  Your database has been setup. Follow the instructions below carefully to avoid issues.
5
5
 
6
6
  Password:
7
- ${s}
7
+ ${i}
8
8
 
9
9
  Store this password in your environment settings or another secure location. When connecting a client, make sure to provide this password via the client's options object like this:
10
10
 
@@ -13,8 +13,8 @@ import joystickdb from '@joystickdb/client';
13
13
  const client = joystickdb.client({
14
14
  host: 'localhost',
15
15
  port: 1983,
16
- password: '${s}'
16
+ password: '${i}'
17
17
  });
18
18
 
19
19
  await client.ping();
20
- ===`;r.send({type:"setup_response",data:{setup_id:o,success:!0,password:s,instructions:i,message:"Authentication setup completed successfully"}})}catch(s){r.send({type:"setup_response",data:{setup_id:o,success:!1,error:s.message}})}}update_worker_connections(r,e){const o=this.workers.get(r.id);o&&(o.connections=e.data.count)}handle_worker_heartbeat(r,e){const o=this.workers.get(r.id);o&&(o.last_heartbeat=Date.now())}get_cluster_stats(){const r={master_pid:process.pid,worker_count:this.workers.size,total_connections:0,write_queue_length:this.write_queue.length,authenticated_sessions:this.authenticated_sessions.size,workers:[]};for(const[e,o]of this.workers)r.total_connections+=o.connections,r.workers.push({id:e,pid:o.worker.process.pid,connections:o.connections,status:o.status,last_heartbeat:o.last_heartbeat});return r}async shutdown(){const r=Date.now();this.log.info("Initiating graceful shutdown"),this.shutting_down=!0;try{await A(),this.log.info("HTTP server stopped")}catch(t){this.log.warn("Failed to stop HTTP server",{error:t.message})}try{m(),this.log.info("Backup scheduling stopped")}catch(t){this.log.warn("Failed to stop backup scheduling",{error:t.message})}for(const[t,s]of this.workers)try{s.worker.send({type:"shutdown"})}catch(i){this.log.warn("Error sending shutdown signal to worker",{worker_id:t,error:i.message})}this.write_queue.length>0&&(this.log.info("Waiting for pending writes to complete",{pending_writes:this.write_queue.length}),await new Promise(t=>{const s=setTimeout(()=>{this.log.warn("Timeout waiting for writes to complete, proceeding with shutdown"),t()},process.env.NODE_ENV==="test"?1e3:5e3);this.once("writes_completed",()=>{clearTimeout(s),t()})})),this.log.info("All writes completed, disconnecting workers");for(const[t,s]of this.workers)try{s.worker.disconnect()}catch(i){this.log.warn("Error disconnecting worker",{worker_id:t,error:i.message})}const e=process.env.NODE_ENV==="test"?500:3e3;await new Promise(t=>{const s=setTimeout(()=>{for(const[n,c]of this.workers){this.log.warn("Force killing worker after timeout",{worker_id:n});try{c.worker.kill("SIGKILL")}catch(d){this.log.warn("Error force killing worker",{worker_id:n,error:d.message})}}this.workers.clear(),t()},e),i=()=>{this.workers.size===0?(clearTimeout(s),t()):setTimeout(i,50)};i()});try{v(),this.log.info("Database cleanup completed")}catch(t){this.log.warn("Error during database cleanup",{error:t.message})}if(this.authenticated_sessions.clear(),this.write_queue.length=0,this.pending_writes.clear(),process.env.NODE_ENV==="test")try{for(const t in a.workers){const s=a.workers[t];if(s&&!s.isDead()){this.log.info("Force killing remaining cluster worker",{worker_id:t,worker_pid:s.process.pid});try{s.kill("SIGKILL")}catch(i){this.log.warn("Error force killing remaining worker",{worker_id:t,error:i.message})}}}for(const t in a.workers)delete a.workers[t];a.removeAllListeners(),this.log.info("Aggressive cluster cleanup completed for test environment")}catch(t){this.log.warn("Error during aggressive cluster cleanup",{error:t.message})}const o=Date.now()-r;this.log.info("Shutdown complete",{shutdown_duration_ms:o})}}var de=$;export{de as default};
20
+ ===`;e.send({type:"setup_response",data:{setup_id:s,success:!0,password:i,instructions:o,message:"Authentication setup completed successfully"}})}catch(i){e.send({type:"setup_response",data:{setup_id:s,success:!1,error:i.message}})}}update_worker_connections(e,t){const s=this.workers.get(e.id);s&&(s.connections=t.data.count)}handle_worker_heartbeat(e,t){const s=this.workers.get(e.id);s&&(s.last_heartbeat=Date.now())}get_cluster_stats(){const e={master_pid:process.pid,worker_count:this.workers.size,total_connections:0,write_queue_length:this.write_queue.length,authenticated_sessions:this.authenticated_sessions.size,workers:[]};for(const[t,s]of this.workers)e.total_connections+=s.connections,e.workers.push({id:t,pid:s.worker.process.pid,connections:s.connections,status:s.status,last_heartbeat:s.last_heartbeat});return e}async stop_http_server_gracefully(){try{await R(),this.log.info("HTTP server stopped")}catch(e){this.log.warn("Failed to stop HTTP server",{error:e.message})}}stop_backup_scheduling_gracefully(){try{f(),this.log.info("Backup scheduling stopped")}catch(e){this.log.warn("Failed to stop backup scheduling",{error:e.message})}}send_shutdown_signals(){for(const[e,t]of this.workers)try{t.worker.send({type:"shutdown"})}catch(s){this.log.warn("Error sending shutdown signal to worker",{worker_id:e,error:s.message})}}async wait_for_pending_writes(){this.write_queue.length!==0&&(this.log.info("Waiting for pending writes to complete",{pending_writes:this.write_queue.length}),await new Promise(e=>{const t=setTimeout(()=>{this.log.warn("Timeout waiting for writes to complete, proceeding with shutdown"),e()},process.env.NODE_ENV==="test"?1e3:5e3);this.once("writes_completed",()=>{clearTimeout(t),e()})}))}disconnect_all_workers(){for(const[e,t]of this.workers)try{t.worker.disconnect()}catch(s){this.log.warn("Error disconnecting worker",{worker_id:e,error:s.message})}}force_kill_remaining_workers(){for(const[e,t]of this.workers){this.log.warn("Force killing worker after timeout",{worker_id:e});try{t.worker.kill("SIGKILL")}catch(s){this.log.warn("Error force killing worker",{worker_id:e,error:s.message})}}this.workers.clear()}async wait_for_workers_to_exit(){const e=process.env.NODE_ENV==="test"?500:3e3;await new Promise(t=>{const s=setTimeout(()=>{this.force_kill_remaining_workers(),t()},e),r=()=>{this.workers.size===0?(clearTimeout(s),t()):setTimeout(r,50)};r()})}cleanup_database_connections(){try{b(),this.log.info("Database cleanup completed")}catch(e){this.log.warn("Error during database cleanup",{error:e.message})}}clear_internal_state(){this.authenticated_sessions.clear(),this.write_queue.length=0,this.pending_writes.clear()}perform_test_environment_cleanup(){if(process.env.NODE_ENV==="test")try{for(const e in n.workers){const t=n.workers[e];if(t&&!t.isDead()){this.log.info("Force killing remaining cluster worker",{worker_id:e,worker_pid:t.process.pid});try{t.kill("SIGKILL")}catch(s){this.log.warn("Error force killing remaining worker",{worker_id:e,error:s.message})}}}for(const e in n.workers)delete n.workers[e];n.removeAllListeners(),this.log.info("Aggressive cluster cleanup completed for test environment")}catch(e){this.log.warn("Error during aggressive cluster cleanup",{error:e.message})}}async shutdown(){const e=Date.now();this.log.info("Initiating graceful shutdown"),this.shutting_down=!0,await this.stop_http_server_gracefully(),this.stop_backup_scheduling_gracefully(),this.send_shutdown_signals(),await this.wait_for_pending_writes(),this.log.info("All writes completed, disconnecting workers"),this.disconnect_all_workers(),await this.wait_for_workers_to_exit(),this.cleanup_database_connections(),this.clear_internal_state(),this.perform_test_environment_cleanup();const t=Date.now()-e;this.log.info("Shutdown complete",{shutdown_duration_ms:t})}}var he=M;export{he as default};
@@ -1 +1 @@
1
- import p from"net";import u from"../lib/op_types.js";import{send_success as l,send_error as a,send_message as g}from"../lib/send_response.js";import{shutdown_write_queue as w}from"../lib/write_queue.js";import{create_message_parser as m,encode_message as h}from"../lib/tcp_protocol.js";import f from"../lib/logger.js";import{initialize_database as y,cleanup_database as b}from"../lib/query_engine.js";import{handle_admin_operation as v,handle_ping_operation as k}from"../lib/operation_dispatcher.js";import{get_settings as $}from"../lib/load_settings.js";import{is_development_mode as S}from"../lib/development_mode.js";class D{constructor(){this.server=null,this.connections=new Map,this.connection_count=0,this.settings=null,this.port=null,this.write_id_counter=0,this.pending_writes=new Map,this.authenticated_clients=new Set,this.heartbeat_interval=null;const{create_context_logger:e}=f("worker");this.log=e({worker_pid:process.pid}),this.setup_worker()}setup_worker(){process.on("message",e=>{this.handle_master_message(e)}),process.on("SIGTERM",()=>{this.shutdown()}),process.on("SIGINT",()=>{this.shutdown()}),this.send_heartbeat(),this.heartbeat_interval=setInterval(()=>{this.send_heartbeat()},5e3),process.connected&&process.send({type:"worker_ready"})}handle_master_message(e){switch(e.type){case"config":this.handle_config(e);break;case"write_response":this.handle_write_response(e);break;case"auth_response":this.handle_auth_response(e);break;case"setup_response":this.handle_setup_response(e);break;case"write_notification":this.handle_write_notification(e);break;case"shutdown":this.shutdown();break;default:this.log.warn("Unknown message type received from master",{message_type:e.type})}}handle_config(e){const t=e.data.master_id;if(this.master_id&&this.master_id!==t){this.log.info("Worker already configured by different master, ignoring config message",{current_master_id:this.master_id,incoming_master_id:t,current_port:this.port,new_port:e.data.port});return}if(this.port!==null&&this.master_id===t){this.log.info("Worker already configured by same master, ignoring duplicate config message",{master_id:t,current_port:this.port,new_port:e.data.port});return}this.log.info("Received config message",{port:e.data.port,master_id:t}),this.port=e.data.port,this.settings=e.data.settings,this.master_id=t;try{let s="./data";try{const r=$();r?.data_path&&(s=r.data_path)}catch{}y(s),this.log.info("Database initialized in worker process",{database_path:s})}catch(s){this.log.error("Failed to initialize database in worker process",{error:s.message})}this.log.info("Starting server",{port:this.port}),this.start_server()}start_server(){this.server=p.createServer(e=>{this.handle_connection(e)}),this.server.listen(this.port,()=>{this.log.info("Server listening",{port:this.port}),process.connected&&process.send({type:"server_ready"})}),this.server.on("error",e=>{this.log.error("Server error",{error:e.message})})}handle_connection(e){const t=`${process.pid}_${Date.now()}_${Math.random()}`;e.id=t,e.message_parser=m(),this.connections.set(t,e),this.connection_count++,this.update_connection_count(),e.on("data",s=>{this.handle_socket_data(e,s)}),e.on("end",()=>{this.handle_socket_end(e)}),e.on("error",s=>{this.log.error("Socket error",{socket_id:t,error_message:s.message}),this.handle_socket_end(e)})}handle_socket_data(e,t){try{const s=e.message_parser.parse_messages(t);for(const r of s){const o=r,i=o?.op||null;if(!i){a(e,{message:"Missing operation type"});continue}if(!this.check_op_type(i)){a(e,{message:"Invalid operation type"});continue}this.route_operation(e,i,o?.data||{})}}catch(s){this.log.error("Data parsing error",{socket_id:e.id,error_message:s.message}),a(e,{message:"Invalid data format"})}}handle_socket_end(e){e.id&&(this.connections.delete(e.id),this.authenticated_clients.delete(e.id),this.connection_count--,this.update_connection_count()),this.log.info("Client disconnected",{socket_id:e.id})}check_op_type(e=""){return e?u.includes(e):!1}route_operation(e,t,s){switch(t){case"authentication":this.handle_authentication(e,s);break;case"setup":this.handle_setup(e,s);break;case"find_one":case"find":case"get_indexes":this.handle_read_operation(e,t,s);break;case"create_index":case"drop_index":this.handle_write_operation(e,t,s);break;case"insert_one":case"update_one":case"delete_one":case"delete_many":case"bulk_write":this.handle_write_operation(e,t,s);break;case"ping":k(e);break;case"admin":v(e,s,this.is_authenticated.bind(this));break;default:a(e,{message:`Unsupported operation: ${t}`})}}handle_authentication(e,t){if(this.is_authenticated(e))g(e,"Already authenticated");else{const s=`${e.id}_${Date.now()}`;process.send({type:"auth_request",data:{auth_id:s,socket_id:e.id,password:t.password}}),this.pending_writes.set(s,{socket:e,type:"auth"})}}handle_setup(e,t){const s=`${e.id}_${Date.now()}`;process.send({type:"setup_request",data:{setup_id:s,socket_id:e.id}}),this.pending_writes.set(s,{socket:e,type:"setup"})}handle_read_operation(e,t,s){if(!this.is_authenticated(e)){a(e,{message:"Authentication required"});return}const r=`${e.id}_${++this.write_id_counter}`;process.send({type:"write_request",data:{write_id:r,op_type:t,data:s,socket_id:e.id}}),this.pending_writes.set(r,{socket:e,type:"read",op_type:t})}handle_write_operation(e,t,s){if(!this.is_authenticated(e)){a(e,{message:"Authentication required"});return}const r=`${e.id}_${++this.write_id_counter}`;process.send({type:"write_request",data:{write_id:r,op_type:t,data:s,socket_id:e.id}}),this.pending_writes.set(r,{socket:e,type:"write",op_type:t})}handle_write_response(e){const{write_id:t,success:s,result:r,error:o}=e.data,i=this.pending_writes.get(t);if(!i){this.log.warn("No pending write found",{write_id:t});return}const{socket:n,op_type:d}=i;if(this.pending_writes.delete(t),n.destroyed||!n.writable){this.log.warn("Socket disconnected before response could be sent",{write_id:t});return}try{if(s){let _;d==="find_one"?_={ok:1,document:r}:d==="find"?_={ok:1,documents:r}:_={ok:1,...r};const c=h(_);n.write(c)}else{const c=h({ok:0,error:o});n.write(c)}}catch(_){this.log.error("Error sending response to client",{write_id:t,error:_.message})}}handle_auth_response(e){const{auth_id:t,success:s,message:r}=e.data,o=this.pending_writes.get(t);if(!o){this.log.warn("No pending auth found",{auth_id:t});return}const{socket:i}=o;if(this.pending_writes.delete(t),i.destroyed||!i.writable){this.log.warn("Socket disconnected before auth response could be sent",{auth_id:t});return}try{if(s){this.authenticated_clients.add(i.id);const d=h({ok:1,version:"1.0.0",message:r});i.write(d)}else a(i,{message:r}),i.end()}catch(n){this.log.error("Error sending auth response to client",{auth_id:t,error:n.message})}}handle_setup_response(e){const{setup_id:t,success:s,password:r,message:o,error:i}=e.data,n=this.pending_writes.get(t);if(!n){this.log.warn("No pending setup found",{setup_id:t});return}const{socket:d}=n;this.pending_writes.delete(t),s?l(d,{password:r,message:o}):a(d,{message:i})}handle_write_notification(e){this.log.info("Received write notification",{op_type:e.data.op_type,timestamp:e.data.timestamp})}is_authenticated(e){return S()?!0:this.authenticated_clients.has(e.id)}update_connection_count(){process.connected&&process.send({type:"connection_count",data:{count:this.connection_count}})}send_heartbeat(){if(process.connected)try{process.send({type:"heartbeat",data:{timestamp:Date.now()}})}catch{clearInterval(this.heartbeat_interval)}}async shutdown(){const e=Date.now();this.log.info("Initiating graceful shutdown");try{await w(),this.log.info("Write queue shutdown complete")}catch(s){this.log.error("Error shutting down write queue",{error:s.message})}try{await b(),this.log.info("Database cleanup complete")}catch(s){this.log.error("Error cleaning up database",{error:s.message})}this.server&&this.server.close(()=>{this.log.info("Server closed")});for(const[s,r]of this.connections)r.end();const t=process.env.NODE_ENV==="test"?100:5e3;setTimeout(()=>{const s=Date.now()-e;this.log.info("Worker shutdown complete",{shutdown_duration_ms:s}),process.exit(0)},t)}}const C=new D;
1
+ import p from"net";import u from"../lib/op_types.js";import{send_success as l,send_error as o,send_message as g}from"../lib/send_response.js";import{shutdown_write_queue as m}from"../lib/write_queue.js";import{create_message_parser as w,encode_message as h}from"../lib/tcp_protocol.js";import f from"../lib/logger.js";import{initialize_database as y,cleanup_database as b}from"../lib/query_engine.js";import{handle_admin_operation as v,handle_ping_operation as k}from"../lib/operation_dispatcher.js";import{get_settings as $}from"../lib/load_settings.js";import{is_development_mode as S}from"../lib/development_mode.js";class D{constructor(){this.server=null,this.connections=new Map,this.connection_count=0,this.settings=null,this.port=null,this.write_id_counter=0,this.pending_writes=new Map,this.authenticated_clients=new Set,this.heartbeat_interval=null;const{create_context_logger:e}=f("worker");this.log=e({worker_pid:process.pid}),this.setup_worker()}setup_worker(){process.on("message",e=>{this.handle_master_message(e)}),process.on("SIGTERM",()=>{this.shutdown()}),process.on("SIGINT",()=>{this.shutdown()}),this.send_heartbeat(),this.heartbeat_interval=setInterval(()=>{this.send_heartbeat()},5e3),process.connected&&process.send({type:"worker_ready"})}handle_master_message(e){switch(e.type){case"config":this.handle_config(e);break;case"write_response":this.handle_write_response(e);break;case"auth_response":this.handle_auth_response(e);break;case"setup_response":this.handle_setup_response(e);break;case"write_notification":this.handle_write_notification(e);break;case"shutdown":this.shutdown();break;default:this.log.warn("Unknown message type received from master",{message_type:e.type})}}handle_config(e){const t=e.data.master_id;if(this.master_id&&this.master_id!==t){this.log.info("Worker already configured by different master, ignoring config message",{current_master_id:this.master_id,incoming_master_id:t,current_port:this.port,new_port:e.data.port});return}if(this.port!==null&&this.master_id===t){this.log.info("Worker already configured by same master, ignoring duplicate config message",{master_id:t,current_port:this.port,new_port:e.data.port});return}this.log.info("Received config message",{port:e.data.port,master_id:t}),this.port=e.data.port,this.settings=e.data.settings,this.master_id=t;try{let s="./data";try{const r=$();r?.data_path&&(s=r.data_path)}catch{}y(s),this.log.info("Database initialized in worker process",{database_path:s})}catch(s){this.log.error("Failed to initialize database in worker process",{error:s.message})}this.log.info("Starting server",{port:this.port}),this.start_server()}start_server(){this.server=p.createServer(e=>{this.handle_connection(e)}),this.server.listen(this.port,()=>{this.log.info("Server listening",{port:this.port}),process.connected&&process.send({type:"server_ready"})}),this.server.on("error",e=>{this.log.error("Server error",{error:e.message})})}handle_connection(e){const t=`${process.pid}_${Date.now()}_${Math.random()}`;e.id=t,e.message_parser=w(),this.connections.set(t,e),this.connection_count++,this.update_connection_count(),e.on("data",s=>{this.handle_socket_data(e,s)}),e.on("end",()=>{this.handle_socket_end(e)}),e.on("error",s=>{this.log.error("Socket error",{socket_id:t,error_message:s.message}),this.handle_socket_end(e)})}process_single_message(e,t){const s=t?.op||null;return s?this.check_op_type(s)?(this.route_operation(e,s,t?.data||{}),!0):(o(e,{message:"Invalid operation type"}),!1):(o(e,{message:"Missing operation type"}),!1)}handle_socket_data(e,t){try{const s=e.message_parser.parse_messages(t);for(const r of s)this.process_single_message(e,r)}catch(s){this.log.error("Data parsing error",{socket_id:e.id,error_message:s.message}),o(e,{message:"Invalid data format"})}}handle_socket_end(e){e.id&&(this.connections.delete(e.id),this.authenticated_clients.delete(e.id),this.connection_count--,this.update_connection_count()),this.log.info("Client disconnected",{socket_id:e.id})}check_op_type(e=""){return e?u.includes(e):!1}route_operation(e,t,s){switch(t){case"authentication":this.handle_authentication(e,s);break;case"setup":this.handle_setup(e,s);break;case"find_one":case"find":case"get_indexes":this.handle_read_operation(e,t,s);break;case"create_index":case"drop_index":this.handle_write_operation(e,t,s);break;case"insert_one":case"update_one":case"delete_one":case"delete_many":case"bulk_write":this.handle_write_operation(e,t,s);break;case"ping":k(e);break;case"admin":v(e,s,this.is_authenticated.bind(this));break;default:o(e,{message:`Unsupported operation: ${t}`})}}handle_authentication(e,t){if(this.is_authenticated(e))g(e,"Already authenticated");else{const s=`${e.id}_${Date.now()}`;process.send({type:"auth_request",data:{auth_id:s,socket_id:e.id,password:t.password}}),this.pending_writes.set(s,{socket:e,type:"auth"})}}handle_setup(e,t){const s=`${e.id}_${Date.now()}`;process.send({type:"setup_request",data:{setup_id:s,socket_id:e.id}}),this.pending_writes.set(s,{socket:e,type:"setup"})}handle_read_operation(e,t,s){if(!this.is_authenticated(e)){o(e,{message:"Authentication required"});return}const r=`${e.id}_${++this.write_id_counter}`;process.send({type:"write_request",data:{write_id:r,op_type:t,data:s,socket_id:e.id}}),this.pending_writes.set(r,{socket:e,type:"read",op_type:t})}handle_write_operation(e,t,s){if(!this.is_authenticated(e)){o(e,{message:"Authentication required"});return}const r=`${e.id}_${++this.write_id_counter}`;process.send({type:"write_request",data:{write_id:r,op_type:t,data:s,socket_id:e.id}}),this.pending_writes.set(r,{socket:e,type:"write",op_type:t})}handle_write_response(e){const{write_id:t,success:s,result:r,error:_}=e.data,i=this.pending_writes.get(t);if(!i){this.log.warn("No pending write found",{write_id:t});return}const{socket:n,op_type:a}=i;if(this.pending_writes.delete(t),n.destroyed||!n.writable){this.log.warn("Socket disconnected before response could be sent",{write_id:t});return}try{if(s){let d;a==="find_one"?d={ok:1,document:r}:a==="find"?d={ok:1,documents:r}:d={ok:1,...r};const c=h(d);n.write(c)}else{const c=h({ok:0,error:_});n.write(c)}}catch(d){this.log.error("Error sending response to client",{write_id:t,error:d.message})}}handle_auth_response(e){const{auth_id:t,success:s,message:r}=e.data,_=this.pending_writes.get(t);if(!_){this.log.warn("No pending auth found",{auth_id:t});return}const{socket:i}=_;if(this.pending_writes.delete(t),i.destroyed||!i.writable){this.log.warn("Socket disconnected before auth response could be sent",{auth_id:t});return}try{if(s){this.authenticated_clients.add(i.id);const a=h({ok:1,version:"1.0.0",message:r});i.write(a)}else o(i,{message:r}),i.end()}catch(n){this.log.error("Error sending auth response to client",{auth_id:t,error:n.message})}}handle_setup_response(e){const{setup_id:t,success:s,password:r,message:_,error:i}=e.data,n=this.pending_writes.get(t);if(!n){this.log.warn("No pending setup found",{setup_id:t});return}const{socket:a}=n;this.pending_writes.delete(t),s?l(a,{password:r,message:_}):o(a,{message:i})}handle_write_notification(e){this.log.info("Received write notification",{op_type:e.data.op_type,timestamp:e.data.timestamp})}is_authenticated(e){return S()?!0:this.authenticated_clients.has(e.id)}update_connection_count(){process.connected&&process.send({type:"connection_count",data:{count:this.connection_count}})}send_heartbeat(){if(process.connected)try{process.send({type:"heartbeat",data:{timestamp:Date.now()}})}catch{clearInterval(this.heartbeat_interval)}}async shutdown(){const e=Date.now();this.log.info("Initiating graceful shutdown");try{await m(),this.log.info("Write queue shutdown complete")}catch(s){this.log.error("Error shutting down write queue",{error:s.message})}try{await b(),this.log.info("Database cleanup complete")}catch(s){this.log.error("Error cleaning up database",{error:s.message})}this.server&&this.server.close(()=>{this.log.info("Server closed")});for(const[s,r]of this.connections)r.end();const t=process.env.NODE_ENV==="test"?100:5e3;setTimeout(()=>{const s=Date.now()-e;this.log.info("Worker shutdown complete",{shutdown_duration_ms:s}),process.exit(0)},t)}}const C=new D;
@@ -1 +1 @@
1
- import E from"net";import{decode as q}from"msgpackr";import N from"./lib/op_types.js";import x from"./lib/safe_json_parse.js";import{load_settings as v,get_settings as h,get_port_configuration as b}from"./lib/load_settings.js";import{send_error as f}from"./lib/send_response.js";import{start_cluster as A}from"./cluster/index.js";import T from"./lib/logger.js";import{initialize_database as B,cleanup_database as D}from"./lib/query_engine.js";import{create_message_parser as $,encode_message as c}from"./lib/tcp_protocol.js";import{create_connection_manager as F}from"./lib/connection_manager.js";import{shutdown_write_queue as J}from"./lib/write_queue.js";import{setup_authentication as K,verify_password as P,get_client_ip as j,is_rate_limited as G,initialize_auth_manager as M,reset_auth_state as W}from"./lib/auth_manager.js";import{initialize_api_key_manager as H}from"./lib/api_key_manager.js";import{is_development_mode as O,display_development_startup_message as U,warn_undefined_node_env as V}from"./lib/development_mode.js";import{restore_backup as Y,start_backup_schedule as L,stop_backup_schedule as Q}from"./lib/backup_manager.js";import{initialize_replication_manager as X,shutdown_replication_manager as Z}from"./lib/replication_manager.js";import{initialize_write_forwarder as ee,shutdown_write_forwarder as re}from"./lib/write_forwarder.js";import{handle_database_operation as te,handle_admin_operation as oe,handle_ping_operation as ae}from"./lib/operation_dispatcher.js";import{start_http_server as ne,stop_http_server as se}from"./lib/http_server.js";import{create_recovery_token as ie,initialize_recovery_manager as z,reset_recovery_state as ce}from"./lib/recovery_manager.js";const p=new Set;let i=null;const _e=async(t,r={})=>{if(!r?.password){const n=c({ok:0,error:"Authentication operation requires password to be set in data."});t.write(n),t.end();return}try{const o=j(t);if(G(o)){const a=c({ok:0,error:"Too many failed attempts. Please try again later."});t.write(a),t.end();return}if(!await P(r.password,o)){const a=c({ok:0,error:"Authentication failed"});t.write(a),t.end();return}p.add(t.id);const d=c({ok:1,version:"1.0.0",message:"Authentication successful"});t.write(d)}catch(o){const n={ok:0,error:`Authentication error: ${o.message}`},s=c(n);t.write(s),t.end()}},pe=async(t,r={})=>{try{const n={ok:1,password:K(),message:"Authentication setup completed successfully. Save this password - it will not be shown again."},s=c(n);t.write(s)}catch(o){const n={ok:0,error:`Setup error: ${o.message}`},s=c(n);t.write(s)}},de=(t="")=>{if(!t)throw new Error("Must pass an op type for operation.");return N.includes(t)},De=t=>{try{if(typeof t=="string")return x(t);if(Buffer.isBuffer(t)){const r=q(t);return typeof r=="string"?x(r):r}else return t}catch{return null}},S=t=>O()?!0:p.has(t.id),$e=async()=>{const{create_context_logger:t}=T("server"),r=t();let o=null;try{v(),o=h()}catch{}if(o?.restore_from)try{r.info("Startup restore requested",{backup_filename:o.restore_from});const e=await Y(o.restore_from);r.info("Startup restore completed",{backup_filename:o.restore_from,duration_ms:e.duration_ms});const a={...o};delete a.restore_from,process.env.JOYSTICK_DB_SETTINGS=JSON.stringify(a),v(),o=h(),r.info("Removed restore_from from settings after successful restore")}catch(e){r.error("Startup restore failed",{backup_filename:o.restore_from,error:e.message}),r.info("Continuing with fresh database after restore failure")}let n="./data";o?.data_path&&(n=o.data_path),B(n),M(),await H(),z();try{X(),r.info("Replication manager initialized")}catch(e){r.warn("Failed to initialize replication manager",{error:e.message})}try{ee(),r.info("Write forwarder initialized")}catch(e){r.warn("Failed to initialize write forwarder",{error:e.message})}if(o?.s3)try{L(),r.info("Backup scheduling started")}catch(e){r.warn("Failed to start backup scheduling",{error:e.message})}i=F({max_connections:1e3,idle_timeout:600*1e3,request_timeout:5*1e3});let s=null;try{const{http_port:e}=b();s=await ne(e),s&&r.info("HTTP server started",{http_port:e})}catch(e){r.warn("Failed to start HTTP server",{error:e.message})}if(O()){const{tcp_port:e,http_port:a}=b();U(e,a)}else V();const d=E.createServer((e={})=>{if(!i.add_connection(e))return;const a=$();e.on("data",async g=>{i.update_activity(e.id);try{const w=a.parse_messages(g);for(const I of w){const u=I,l=u?.op||null;if(!l){f(e,{message:"Missing operation type"});continue}if(!de(l)){f(e,{message:"Invalid operation type"});continue}const R=i.create_request_timeout(e.id,l);try{switch(l){case"authentication":await _e(e,u?.data||{});break;case"setup":await pe(e,u?.data||{});break;case"insert_one":case"update_one":case"delete_one":case"delete_many":case"bulk_write":case"find_one":case"find":case"create_index":case"drop_index":case"get_indexes":await te(e,l,u?.data||{},S,g.length,i,p);break;case"ping":ae(e);break;case"admin":await oe(e,u?.data||{},S,i,p);break;case"reload":if(!S(e)){f(e,{message:"Authentication required"});break}try{let _=null;try{_=h()}catch{}let m=null;try{await v(),m=h()}catch{m={port:1983,authentication:{}}}const y={ok:1,status:"success",message:"Configuration reloaded successfully",changes:{port_changed:_?_.port!==m.port:!1,authentication_changed:_?_.authentication?.password_hash!==m.authentication?.password_hash:!1},timestamp:new Date().toISOString()},k=c(y);e.write(k)}catch(_){const m={ok:0,error:`Reload operation failed: ${_.message}`},y=c(m);e.write(y)}break;default:f(e,{message:`Operation ${l} not implemented`})}}finally{clearTimeout(R)}}}catch(w){r.error("Message parsing failed",{client_id:e.id,error:w.message}),f(e,{message:"Invalid message format"}),e.end()}}),e.on("end",()=>{r.info("Client disconnected",{socket_id:e.id}),p.delete(e.id),i.remove_connection(e.id)}),e.on("error",g=>{r.error("Socket error",{socket_id:e.id,error:g.message}),p.delete(e.id),i.remove_connection(e.id)})});return d.cleanup=async()=>{try{await se(),Q(),await Z(),await re(),i&&i.shutdown(),p.clear(),await J(),await new Promise(e=>setTimeout(e,100)),await D(),W(),ce()}catch{}},d};if(import.meta.url===`file://${process.argv[1]}`){const{create_context_logger:t}=T("main"),r=t();if(process.argv.includes("--generate-recovery-token"))try{z();const a=ie();console.log("Emergency Recovery Token Generated"),console.log(`Visit: ${a.url}`),console.log("Token expires in 10 minutes"),r.info("Recovery token generated via CLI",{expires_at:new Date(a.expires_at).toISOString()}),process.exit(0)}catch(a){console.error("Failed to generate recovery token:",a.message),r.error("Recovery token generation failed",{error:a.message}),process.exit(1)}const{tcp_port:o,http_port:n}=b(),s={worker_count:process.env.WORKER_COUNT?parseInt(process.env.WORKER_COUNT):void 0,port:o,environment:process.env.NODE_ENV||"development"},{has_settings:d}=await import("./lib/load_settings.js"),e=d();r.info("Starting JoystickDB server...",{workers:s.worker_count||"auto",tcp_port:o,http_port:n,environment:s.environment,has_settings:e,port_source:e?"JOYSTICK_DB_SETTINGS":"default"}),A(s)}export{_e as authentication,de as check_op_type,$e as create_server,De as parse_data,pe as setup};
1
+ import S from"net";import{decode as k}from"msgpackr";import x from"./lib/op_types.js";import g from"./lib/safe_json_parse.js";import{load_settings as l,get_settings as m,get_port_configuration as p}from"./lib/load_settings.js";import{send_error as c}from"./lib/send_response.js";import{start_cluster as T}from"./cluster/index.js";import h from"./lib/logger.js";import{initialize_database as z,cleanup_database as O}from"./lib/query_engine.js";import{create_message_parser as I,encode_message as w}from"./lib/tcp_protocol.js";import{create_connection_manager as R}from"./lib/connection_manager.js";import{shutdown_write_queue as C}from"./lib/write_queue.js";import{setup_authentication as E,verify_password as q,get_client_ip as N,is_rate_limited as A,initialize_auth_manager as B,reset_auth_state as D}from"./lib/auth_manager.js";import{initialize_api_key_manager as $}from"./lib/api_key_manager.js";import{is_development_mode as y,display_development_startup_message as F,warn_undefined_node_env as J}from"./lib/development_mode.js";import{restore_backup as K,start_backup_schedule as P,stop_backup_schedule as G}from"./lib/backup_manager.js";import{initialize_replication_manager as M,shutdown_replication_manager as W}from"./lib/replication_manager.js";import{initialize_write_forwarder as H,shutdown_write_forwarder as U}from"./lib/write_forwarder.js";import{handle_database_operation as V,handle_admin_operation as Y,handle_ping_operation as j}from"./lib/operation_dispatcher.js";import{start_http_server as L,stop_http_server as Q}from"./lib/http_server.js";import{create_recovery_token as X,initialize_recovery_manager as v,reset_recovery_state as Z}from"./lib/recovery_manager.js";import{has_settings as ee}from"./lib/load_settings.js";const i=new Set;let s=null;const re=e=>e&&e.password,d=e=>({ok:0,error:e}),te=()=>({ok:1,version:"1.0.0",message:"Authentication successful"}),u=(e,r)=>{const t=w(r);e.write(t),e.end()},_=(e,r)=>{const t=w(r);e.write(t)},ne=async(e,r={})=>{if(!re(r)){const t=d("Authentication operation requires password to be set in data.");u(e,t);return}try{const t=N(e);if(A(t)){const a=d("Too many failed attempts. Please try again later.");u(e,a);return}if(!await q(r.password,t)){const a=d("Authentication failed");u(e,a);return}i.add(e.id);const o=te();_(e,o)}catch(t){const n=d(`Authentication error: ${t.message}`);u(e,n)}},oe=e=>({ok:1,password:e,message:"Authentication setup completed successfully. Save this password - it will not be shown again."}),ae=e=>({ok:0,error:`Setup error: ${e}`}),se=async(e,r={})=>{try{const t=E(),n=oe(t);_(e,n)}catch(t){const n=ae(t.message);_(e,n)}},ie=(e="")=>{if(!e)throw new Error("Must pass an op type for operation.");return x.includes(e)},ce=e=>g(e),_e=e=>{try{const r=k(e);return typeof r=="string"?g(r):r}catch{return null}},ar=e=>{try{return typeof e=="string"?ce(e):Buffer.isBuffer(e)?_e(e):e}catch{return null}},f=e=>y()?!0:i.has(e.id),pe=async(e,r)=>{if(e?.restore_from)try{r.info("Startup restore requested",{backup_filename:e.restore_from});const t=await K(e.restore_from);r.info("Startup restore completed",{backup_filename:e.restore_from,duration_ms:t.duration_ms});const n={...e};delete n.restore_from,process.env.JOYSTICK_DB_SETTINGS=JSON.stringify(n),l(),r.info("Removed restore_from from settings after successful restore")}catch(t){r.error("Startup restore failed",{backup_filename:e.restore_from,error:t.message}),r.info("Continuing with fresh database after restore failure")}},de=()=>{try{return l(),m()}catch{return null}},ue=async e=>{const r=e?.data_path||"./data";z(r),B(),await $(),v()},le=e=>{try{M(),e.info("Replication manager initialized")}catch(r){e.warn("Failed to initialize replication manager",{error:r.message})}},me=e=>{try{H(),e.info("Write forwarder initialized")}catch(r){e.warn("Failed to initialize write forwarder",{error:r.message})}},fe=(e,r)=>{if(e?.s3)try{P(),r.info("Backup scheduling started")}catch(t){r.warn("Failed to start backup scheduling",{error:t.message})}},ge=async(e,r)=>{try{const t=await L(e);return t&&r.info("HTTP server started",{http_port:e}),t}catch(t){return r.warn("Failed to start HTTP server",{error:t.message}),null}},he=()=>{if(y()){const{tcp_port:e,http_port:r}=p();F(e,r)}else J()},we=()=>R({max_connections:1e3,idle_timeout:600*1e3,request_timeout:5*1e3}),ye=async(e,r,t,n)=>{s.update_activity(e.id);try{const o=t.parse_messages(r);for(const a of o)await ve(e,a,r.length,n)}catch(o){n.error("Message parsing failed",{client_id:e.id,error:o.message}),c(e,{message:"Invalid message format"}),e.end()}},ve=async(e,r,t,n)=>{const o=r,a=o?.op||null;if(!a){c(e,{message:"Missing operation type"});return}if(!ie(a)){c(e,{message:"Invalid operation type"});return}const b=s.create_request_timeout(e.id,a);try{await be(e,a,o,t)}finally{clearTimeout(b)}},be=async(e,r,t,n)=>{const o=t?.data||{};switch(r){case"authentication":await ne(e,o);break;case"setup":await se(e,o);break;case"insert_one":case"update_one":case"delete_one":case"delete_many":case"bulk_write":case"find_one":case"find":case"create_index":case"drop_index":case"get_indexes":await V(e,r,o,f,n,s,i);break;case"ping":j(e);break;case"admin":await Y(e,o,f,s,i);break;case"reload":await Se(e);break;default:c(e,{message:`Operation ${r} not implemented`})}},Se=async e=>{if(!f(e)){c(e,{message:"Authentication required"});return}try{const r=ke(),t=await xe(),n=Te(r,t);_(e,n)}catch(r){const t={ok:0,error:`Reload operation failed: ${r.message}`};_(e,t)}},ke=()=>{try{return m()}catch{return null}},xe=async()=>{try{return await l(),m()}catch{return{port:1983,authentication:{}}}},Te=(e,r)=>({ok:1,status:"success",message:"Configuration reloaded successfully",changes:{port_changed:e?e.port!==r.port:!1,authentication_changed:e?e.authentication?.password_hash!==r.authentication?.password_hash:!1},timestamp:new Date().toISOString()}),ze=(e,r)=>{r.info("Client disconnected",{socket_id:e.id}),i.delete(e.id),s.remove_connection(e.id)},Oe=(e,r,t)=>{t.error("Socket error",{socket_id:e.id,error:r.message}),i.delete(e.id),s.remove_connection(e.id)},Ie=(e,r,t)=>{e.on("data",async n=>{await ye(e,n,r,t)}),e.on("end",()=>{ze(e,t)}),e.on("error",n=>{Oe(e,n,t)})},Re=(e,r)=>{if(!s.add_connection(e))return;const t=I();Ie(e,t,r)},Ce=()=>async()=>{try{await Q(),G(),await W(),await U(),s&&s.shutdown(),i.clear(),await C(),await new Promise(e=>setTimeout(e,100)),await O(),D(),Z()}catch{}},sr=async()=>{const{create_context_logger:e}=h("server"),r=e(),t=de();await pe(t,r),await ue(t),le(r),me(r),fe(t,r),s=we();const{http_port:n}=p();await ge(n,r),he();const o=S.createServer((a={})=>{Re(a,r)});return o.cleanup=Ce(),o},Ee=e=>{try{v();const r=X();console.log("Emergency Recovery Token Generated"),console.log(`Visit: ${r.url}`),console.log("Token expires in 10 minutes"),e.info("Recovery token generated via CLI",{expires_at:new Date(r.expires_at).toISOString()}),process.exit(0)}catch(r){console.error("Failed to generate recovery token:",r.message),e.error("Recovery token generation failed",{error:r.message}),process.exit(1)}},qe=()=>{const{tcp_port:e}=p();return{worker_count:process.env.WORKER_COUNT?parseInt(process.env.WORKER_COUNT):void 0,port:e,environment:process.env.NODE_ENV||"development"}},Ne=(e,r)=>{const{tcp_port:t,http_port:n}=p(),o=ee();r.info("Starting JoystickDB server...",{workers:e.worker_count||"auto",tcp_port:t,http_port:n,environment:e.environment,has_settings:o,port_source:o?"JOYSTICK_DB_SETTINGS":"default"})};if(import.meta.url===`file://${process.argv[1]}`){const{create_context_logger:e}=h("main"),r=e();process.argv.includes("--generate-recovery-token")&&Ee(r);const t=qe();Ne(t,r),T(t)}export{ne as authentication,ie as check_op_type,sr as create_server,ar as parse_data,se as setup};
@@ -1 +1 @@
1
- import{get_database as A}from"./query_engine.js";import{create_index as k,get_indexes as z,drop_index as w}from"./index_manager.js";import{get_settings as Q}from"./load_settings.js";import R from"./logger.js";const{create_context_logger:_}=R("auto_index_manager");let l=null,u=new Map,i=new Map,x=null;const E=()=>(l||(l=A().openDB("auto_indexes",{create:!0}),H(),G(),D()),l),h=()=>{if(!l)throw new Error("Auto index database not initialized. Call initialize_auto_index_database first.");return l},g=()=>{try{return Q().auto_indexing||{enabled:!0,frequency_threshold:100,performance_threshold_ms:50,max_auto_indexes_per_collection:10,monitoring_window_hours:24,cleanup_unused_after_hours:168,excluded_fields:["_id","created_at"],included_collections:["*"],excluded_collections:[]}}catch{return{enabled:!0,frequency_threshold:100,performance_threshold_ms:50,max_auto_indexes_per_collection:10,monitoring_window_hours:24,cleanup_unused_after_hours:168,excluded_fields:["_id","created_at"],included_collections:["*"],excluded_collections:[]}}},B=e=>{const o=g();return o.excluded_collections.includes(e)?!1:o.included_collections.includes("*")?!0:o.included_collections.includes(e)},C=e=>!g().excluded_fields.includes(e),N=e=>{const o=[];if(!e||typeof e!="object")return o;for(const[t,a]of Object.entries(e))C(t)&&o.push(t);return o},P=(e,o,t,a=!1,s=null)=>{const r=_(),n=g();if(!n.enabled||!B(e)||!l)return;const c=N(o),f=new Date;u.has(e)||u.set(e,new Map);const d=u.get(e);for(const y of c){d.has(y)||d.set(y,{query_count:0,total_time_ms:0,avg_time_ms:0,last_queried:f,slow_query_count:0,used_index_count:0});const m=d.get(y);m.query_count++,m.total_time_ms+=t,m.avg_time_ms=m.total_time_ms/m.query_count,m.last_queried=f,t>n.performance_threshold_ms&&m.slow_query_count++,a&&(s===y||s===null)&&m.used_index_count++}r.debug("Query recorded for auto-indexing analysis",{collection:e,fields:c,execution_time_ms:t,used_index:a,indexed_field:s})},T=()=>{const e=_();try{const o=h(),t={};for(const[a,s]of u.entries()){t[a]={};for(const[r,n]of s.entries())t[a][r]={...n,last_queried:n.last_queried.toISOString()}}o.put("query_stats",t),e.debug("Query statistics saved to database")}catch(o){e.error("Failed to save query statistics",{error:o.message})}},G=()=>{const e=_();try{const t=h().get("query_stats");if(t){u.clear();for(const[a,s]of Object.entries(t)){const r=new Map;for(const[n,c]of Object.entries(s))r.set(n,{...c,last_queried:new Date(c.last_queried)});u.set(a,r)}e.debug("Query statistics loaded from database")}}catch(o){e.error("Failed to load query statistics",{error:o.message})}},p=()=>{const e=_();try{const o=h(),t={};for(const[a,s]of i.entries()){t[a]={};for(const[r,n]of s.entries())t[a][r]={...n,created_at:n.created_at.toISOString(),last_used:n.last_used?n.last_used.toISOString():null}}o.put("auto_index_metadata",t),e.debug("Auto index metadata saved to database")}catch(o){e.error("Failed to save auto index metadata",{error:o.message})}},H=()=>{const e=_();try{const t=h().get("auto_index_metadata");if(t){i.clear();for(const[a,s]of Object.entries(t)){const r=new Map;for(const[n,c]of Object.entries(s))r.set(n,{...c,created_at:new Date(c.created_at),last_used:c.last_used?new Date(c.last_used):null});i.set(a,r)}e.debug("Auto index metadata loaded from database")}}catch(o){e.error("Failed to load auto index metadata",{error:o.message})}},q=(e,o)=>{try{const t=i.get(e);return!!(t&&t.has(o))}catch{return!1}},v=()=>{const e=g(),o=[],t=new Date,a=e.monitoring_window_hours*60*60*1e3;for(const[s,r]of u.entries()){const n=z("default",s);if(!(n.filter(f=>q(s,f.field)).length>=e.max_auto_indexes_per_collection))for(const[f,d]of r.entries()){if(t-d.last_queried>a||n.some(S=>S.field===f))continue;const j=d.query_count>=e.frequency_threshold,I=d.avg_time_ms>=e.performance_threshold_ms,M=d.slow_query_count>0;(j||I&&M)&&o.push({collection:s,field:f,stats:{...d},priority:d.slow_query_count*2+d.query_count/e.frequency_threshold})}}return o.sort((s,r)=>r.priority-s.priority)},b=async(e,o,t)=>{const a=_();try{return await k("default",e,o,{sparse:!0}),i.has(e)||i.set(e,new Map),i.get(e).set(o,{created_at:new Date,query_count_at_creation:t.query_count,avg_performance_improvement_ms:0,last_used:null,usage_count:0,auto_created:!0}),p(),a.info("Automatic index created",{collection:e,field:o,query_count:t.query_count,avg_time_ms:t.avg_time_ms,slow_query_count:t.slow_query_count}),!0}catch(s){return a.error("Failed to create automatic index",{collection:e,field:o,error:s.message}),!1}},F=async()=>{const e=_();if(g().enabled)try{const t=v();if(t.length===0){e.debug("No automatic index candidates found");return}e.info("Evaluating automatic index candidates",{candidate_count:t.length});for(const a of t.slice(0,5))await b(a.collection,a.field,a.stats)&&await new Promise(r=>setTimeout(r,100))}catch(t){e.error("Failed to evaluate automatic indexes",{error:t.message})}},J=async()=>{const e=_(),o=g(),t=new Date,a=o.cleanup_unused_after_hours*60*60*1e3;try{for(const[s,r]of i.entries())for(const[n,c]of r.entries())c.last_used?t-c.last_used>a&&(await w("default",s,n),r.delete(n),e.info("Removed unused automatic index",{collection:s,field:n,last_used:c.last_used,usage_count:c.usage_count})):t-c.created_at>a&&(await w("default",s,n),r.delete(n),e.info("Removed unused automatic index",{collection:s,field:n,created_at:c.created_at,usage_count:c.usage_count}));p()}catch(s){e.error("Failed to cleanup unused indexes",{error:s.message})}},K=(e,o)=>{const t=i.get(e);if(t&&t.has(o)){const a=t.get(o);a.last_used=new Date,a.usage_count++}},D=()=>{const e=g();x&&clearInterval(x),e.enabled&&(x=setInterval(async()=>{T(),await F(),await J()},6e4))},O=()=>{x&&(clearInterval(x),x=null)},L=(e=null)=>{if(e){const t=u.get(e);if(!t)return{};const a={};for(const[s,r]of t.entries())a[s]={...r};return a}const o={};for(const[t,a]of u.entries()){o[t]={};for(const[s,r]of a.entries())o[t][s]={...r}}return o},U=()=>{const e={total_auto_indexes:0,collections:{}};for(const[o,t]of i.entries()){e.collections[o]={};for(const[a,s]of t.entries())e.total_auto_indexes++,e.collections[o][a]={...s}}return e},V=async(e=null)=>{const o=_();try{if(e){const t=v().filter(a=>a.collection===e);for(const a of t)await b(a.collection,a.field,a.stats);o.info("Forced index evaluation completed",{collection:e,candidates_processed:t.length})}else await F(),o.info("Forced index evaluation completed for all collections");return{acknowledged:!0}}catch(t){throw o.error("Failed to force index evaluation",{error:t.message}),t}},W=async(e,o=null)=>{const t=_();try{const a=i.get(e);if(!a)return{acknowledged:!0,removed_count:0};const s=o||Array.from(a.keys());let r=0;for(const n of s)a.has(n)&&(await w("default",e,n),a.delete(n),r++,t.info("Removed automatic index",{collection:e,field:n}));return p(),{acknowledged:!0,removed_count:r}}catch(a){throw t.error("Failed to remove automatic indexes",{collection:e,field_names:o,error:a.message}),a}},X=()=>{if(O(),l)try{l.remove("query_stats"),l.remove("auto_index_metadata")}catch{}u.clear(),i.clear(),l=null};export{X as cleanup_auto_index_database,V as force_index_evaluation,h as get_auto_index_database,U as get_auto_index_statistics,L as get_query_statistics,E as initialize_auto_index_database,q as is_auto_created_index,K as record_index_usage,P as record_query,W as remove_automatic_indexes,D as start_evaluation_timer,O as stop_evaluation_timer};
1
+ import{get_database as I}from"./query_engine.js";import{create_index as M,get_indexes as q,drop_index as w}from"./index_manager.js";import{get_settings as S}from"./load_settings.js";import A from"./logger.js";const{create_context_logger:d}=A("auto_index_manager");let _=null,u=new Map,c=new Map,m=null;const k=()=>(_||(_=I().openDB("auto_indexes",{create:!0}),N(),C(),O()),_),g=()=>{if(!_)throw new Error("Auto index database not initialized. Call initialize_auto_index_database first.");return _},f=()=>{try{return S().auto_indexing||{enabled:!0,frequency_threshold:100,performance_threshold_ms:50,max_auto_indexes_per_collection:10,monitoring_window_hours:24,cleanup_unused_after_hours:168,excluded_fields:["_id","created_at"],included_collections:["*"],excluded_collections:[]}}catch{return{enabled:!0,frequency_threshold:100,performance_threshold_ms:50,max_auto_indexes_per_collection:10,monitoring_window_hours:24,cleanup_unused_after_hours:168,excluded_fields:["_id","created_at"],included_collections:["*"],excluded_collections:[]}}},z=e=>{const o=f();return o.excluded_collections.includes(e)?!1:o.included_collections.includes("*")?!0:o.included_collections.includes(e)},Q=e=>!f().excluded_fields.includes(e),R=e=>{const o=[];if(!e||typeof e!="object")return o;for(const[t,a]of Object.entries(e))Q(t)&&o.push(t);return o},E=(e,o,t,a=!1,n=null)=>{const r=d(),s=f();if(!s.enabled||!z(e)||!_)return;const i=R(o),x=new Date;u.has(e)||u.set(e,new Map);const y=u.get(e);for(const h of i){y.has(h)||y.set(h,{query_count:0,total_time_ms:0,avg_time_ms:0,last_queried:x,slow_query_count:0,used_index_count:0});const l=y.get(h);l.query_count++,l.total_time_ms+=t,l.avg_time_ms=l.total_time_ms/l.query_count,l.last_queried=x,t>s.performance_threshold_ms&&l.slow_query_count++,a&&(n===h||n===null)&&l.used_index_count++}r.debug("Query recorded for auto-indexing analysis",{collection:e,fields:i,execution_time_ms:t,used_index:a,indexed_field:n})},B=()=>{const e=d();try{const o=g(),t={};for(const[a,n]of u.entries()){t[a]={};for(const[r,s]of n.entries())t[a][r]={...s,last_queried:s.last_queried.toISOString()}}o.put("query_stats",t),e.debug("Query statistics saved to database")}catch(o){e.error("Failed to save query statistics",{error:o.message})}},C=()=>{const e=d();try{const t=g().get("query_stats");if(t){u.clear();for(const[a,n]of Object.entries(t)){const r=new Map;for(const[s,i]of Object.entries(n))r.set(s,{...i,last_queried:new Date(i.last_queried)});u.set(a,r)}e.debug("Query statistics loaded from database")}}catch(o){e.error("Failed to load query statistics",{error:o.message})}},p=()=>{const e=d();try{const o=g(),t={};for(const[a,n]of c.entries()){t[a]={};for(const[r,s]of n.entries())t[a][r]={...s,created_at:s.created_at.toISOString(),last_used:s.last_used?s.last_used.toISOString():null}}o.put("auto_index_metadata",t),e.debug("Auto index metadata saved to database")}catch(o){e.error("Failed to save auto index metadata",{error:o.message})}},N=()=>{const e=d();try{const t=g().get("auto_index_metadata");if(t){c.clear();for(const[a,n]of Object.entries(t)){const r=new Map;for(const[s,i]of Object.entries(n))r.set(s,{...i,created_at:new Date(i.created_at),last_used:i.last_used?new Date(i.last_used):null});c.set(a,r)}e.debug("Auto index metadata loaded from database")}}catch(o){e.error("Failed to load auto index metadata",{error:o.message})}},v=(e,o)=>{try{const t=c.get(e);return!!(t&&t.has(o))}catch{return!1}},P=(e,o)=>q("default",e).filter(n=>v(e,n.field)).length>=o.max_auto_indexes_per_collection,T=(e,o)=>new Date-e.last_queried<=o,G=(e,o)=>o.some(t=>t.field===e),H=(e,o)=>{const t=e.query_count>=o.frequency_threshold,a=e.avg_time_ms>=o.performance_threshold_ms,n=e.slow_query_count>0;return t||a&&n},J=(e,o)=>e.slow_query_count*2+e.query_count/o.frequency_threshold,K=(e,o,t,a)=>({collection:e,field:o,stats:{...t},priority:J(t,a)}),b=()=>{const e=f(),o=[],t=e.monitoring_window_hours*60*60*1e3;for(const[a,n]of u.entries()){if(P(a,e))continue;const r=q("default",a);for(const[s,i]of n.entries())T(i,t)&&(G(s,r)||H(i,e)&&o.push(K(a,s,i,e)))}return o.sort((a,n)=>n.priority-a.priority)},F=async(e,o,t)=>{const a=d();try{return await M("default",e,o,{sparse:!0}),c.has(e)||c.set(e,new Map),c.get(e).set(o,{created_at:new Date,query_count_at_creation:t.query_count,avg_performance_improvement_ms:0,last_used:null,usage_count:0,auto_created:!0}),p(),a.info("Automatic index created",{collection:e,field:o,query_count:t.query_count,avg_time_ms:t.avg_time_ms,slow_query_count:t.slow_query_count}),!0}catch(n){return a.error("Failed to create automatic index",{collection:e,field:o,error:n.message}),!1}},D=async()=>{const e=d();if(f().enabled)try{const t=b();if(t.length===0){e.debug("No automatic index candidates found");return}e.info("Evaluating automatic index candidates",{candidate_count:t.length});for(const a of t.slice(0,5))await F(a.collection,a.field,a.stats)&&await new Promise(r=>setTimeout(r,100))}catch(t){e.error("Failed to evaluate automatic indexes",{error:t.message})}},L=async()=>{const e=d(),o=f(),t=new Date,a=o.cleanup_unused_after_hours*60*60*1e3;try{for(const[n,r]of c.entries())for(const[s,i]of r.entries())i.last_used?t-i.last_used>a&&(await w("default",n,s),r.delete(s),e.info("Removed unused automatic index",{collection:n,field:s,last_used:i.last_used,usage_count:i.usage_count})):t-i.created_at>a&&(await w("default",n,s),r.delete(s),e.info("Removed unused automatic index",{collection:n,field:s,created_at:i.created_at,usage_count:i.usage_count}));p()}catch(n){e.error("Failed to cleanup unused indexes",{error:n.message})}},U=(e,o)=>{const t=c.get(e);if(t&&t.has(o)){const a=t.get(o);a.last_used=new Date,a.usage_count++}},O=()=>{const e=f();m&&clearInterval(m),e.enabled&&(m=setInterval(async()=>{B(),await D(),await L()},6e4))},j=()=>{m&&(clearInterval(m),m=null)},V=(e=null)=>{if(e){const t=u.get(e);if(!t)return{};const a={};for(const[n,r]of t.entries())a[n]={...r};return a}const o={};for(const[t,a]of u.entries()){o[t]={};for(const[n,r]of a.entries())o[t][n]={...r}}return o},W=()=>{const e={total_auto_indexes:0,collections:{}};for(const[o,t]of c.entries()){e.collections[o]={};for(const[a,n]of t.entries())e.total_auto_indexes++,e.collections[o][a]={...n}}return e},X=async(e=null)=>{const o=d();try{if(e){const t=b().filter(a=>a.collection===e);for(const a of t)await F(a.collection,a.field,a.stats);o.info("Forced index evaluation completed",{collection:e,candidates_processed:t.length})}else await D(),o.info("Forced index evaluation completed for all collections");return{acknowledged:!0}}catch(t){throw o.error("Failed to force index evaluation",{error:t.message}),t}},Y=async(e,o=null)=>{const t=d();try{const a=c.get(e);if(!a)return{acknowledged:!0,removed_count:0};const n=o||Array.from(a.keys());let r=0;for(const s of n)a.has(s)&&(await w("default",e,s),a.delete(s),r++,t.info("Removed automatic index",{collection:e,field:s}));return p(),{acknowledged:!0,removed_count:r}}catch(a){throw t.error("Failed to remove automatic indexes",{collection:e,field_names:o,error:a.message}),a}},Z=()=>{if(j(),_)try{_.remove("query_stats"),_.remove("auto_index_metadata")}catch{}u.clear(),c.clear(),_=null};export{Z as cleanup_auto_index_database,X as force_index_evaluation,g as get_auto_index_database,W as get_auto_index_statistics,V as get_query_statistics,k as initialize_auto_index_database,v as is_auto_created_index,U as record_index_usage,E as record_query,Y as remove_automatic_indexes,O as start_evaluation_timer,j as stop_evaluation_timer};
@@ -1 +1 @@
1
- import{S3Client as F,PutObjectCommand as L,GetObjectCommand as W,ListObjectsV2Command as x,DeleteObjectCommand as j}from"@aws-sdk/client-s3";import{createReadStream as M,createWriteStream as G,existsSync as w,unlinkSync as $,statSync as T}from"fs";import{mkdir as v,rm as b}from"fs/promises";import{resolve as z,join as C}from"path";import{spawn as B}from"child_process";import{createHash as q}from"crypto";import{get_settings as I}from"./load_settings.js";import{get_database as A}from"./query_engine.js";import H from"./logger.js";const{create_context_logger:f}=H("backup_manager");let D=null,y=null;const S=()=>{const e=f();try{const t=I();if(!t.s3)throw new Error("S3 configuration not found in settings");const{bucket:c,region:r,access_key:o,secret_key:d,endpoint:n}=t.s3;if(!c||!r||!o||!d)throw new Error("Missing required S3 configuration: bucket, region, access_key, secret_key");if(!D){const s={region:r,credentials:{accessKeyId:o,secretAccessKey:d}};n&&n!==`https://s3.${r}.amazonaws.com`&&(s.endpoint=n,s.forcePathStyle=!0),D=new F(s),e.info("S3 client initialized",{bucket:c,region:r,endpoint:n||"default"})}return{client:D,bucket:c}}catch(t){throw e.error("Failed to initialize S3 client",{error:t.message}),t}},R=async()=>{const e=f();try{const{client:t,bucket:c}=S(),r=new x({Bucket:c,MaxKeys:1});return await t.send(r),e.info("S3 connection test successful",{bucket:c}),!0}catch(t){throw e.error("S3 connection test failed",{error:t.message}),new Error(`S3 connection failed: ${t.message}`)}},K=e=>new Promise((t,c)=>{const r=q("sha256"),o=M(e);o.on("data",d=>r.update(d)),o.on("end",()=>t(r.digest("hex"))),o.on("error",c)}),E=async()=>{const e=f(),t=Date.now();try{const{client:c,bucket:r}=S(),o=z("./temp_backup");await v(o,{recursive:!0});const n=`joystickdb-backup-${new Date().toISOString().replace(/[:.]/g,"-")}.tar.gz`,s=C(o,n);e.info("Starting backup creation",{backup_filename:n,temp_backup_path:s});const u=B("tar",["--sparse","-czf",s,"-C","./data","."],{stdio:["pipe","pipe","pipe"]});let _="",l="";u.stdout.on("data",k=>{_+=k.toString()}),u.stderr.on("data",k=>{l+=k.toString()});const p=await new Promise(k=>{u.on("close",k)});if(p!==0)throw new Error(`Tar process failed with exit code ${p}: ${l}`);const a=await K(s),i=T(s);e.info("Backup file created",{backup_filename:n,size_bytes:i.size,size_mb:Math.round(i.size/1024/1024),checksum:a});const m=M(s),g=new L({Bucket:r,Key:n,Body:m,Metadata:{checksum:a,created_at:new Date().toISOString(),size_bytes:i.size.toString()}});await c.send(g),$(s),await b(o,{recursive:!0,force:!0});const h=Date.now()-t;return e.info("Backup completed successfully",{backup_filename:n,duration_ms:h,size_mb:Math.round(i.size/1024/1024),checksum:a,s3_bucket:r}),{filename:n,size_bytes:i.size,checksum:a,created_at:new Date().toISOString(),duration_ms:h,s3_location:`s3://${r}/${n}`}}catch(c){e.error("Backup creation failed",{error:c.message});try{const r=z("./temp_backup");w(r)&&await b(r,{recursive:!0,force:!0})}catch(r){e.warn("Failed to clean up temporary backup files",{error:r.message})}throw c}},O=async()=>{const e=f();try{const{client:t,bucket:c}=S(),r=new x({Bucket:c,Prefix:"joystickdb-backup-"}),o=await t.send(r);if(!o.Contents||o.Contents.length===0)return{backups:[],total_count:0,total_size_bytes:0};const d=o.Contents.filter(s=>s.Key.startsWith("joystickdb-backup-")&&s.Key.endsWith(".tar.gz")).map(s=>({filename:s.Key,size_bytes:s.Size,size_mb:Math.round(s.Size/1024/1024),last_modified:s.LastModified.toISOString(),s3_location:`s3://${c}/${s.Key}`})).sort((s,u)=>new Date(u.last_modified)-new Date(s.last_modified)),n=d.reduce((s,u)=>s+u.size_bytes,0);return e.info("Listed backups",{total_count:d.length,total_size_mb:Math.round(n/1024/1024)}),{backups:d,total_count:d.length,total_size_bytes:n,total_size_mb:Math.round(n/1024/1024)}}catch(t){throw e.error("Failed to list backups",{error:t.message}),t}},V=async e=>{const t=f(),c=Date.now();try{const{client:r,bucket:o}=S(),d=z("./temp_restore");await v(d,{recursive:!0});const n=C(d,e);t.info("Starting backup restore",{backup_filename:e,temp_backup_path:n});const s=new W({Bucket:o,Key:e}),u=await r.send(s);if(!u.Body)throw new Error("Empty backup file received from S3");const _=G(n);if(await new Promise((a,i)=>{u.Body.pipe(_),_.on("finish",a),_.on("error",i),u.Body.on("error",i)}),u.Metadata&&u.Metadata.checksum){const a=await K(n);if(a!==u.Metadata.checksum)throw new Error(`Backup checksum mismatch. Expected: ${u.Metadata.checksum}, Got: ${a}`);t.info("Backup checksum verified",{checksum:a})}const l=A(),p=z("./data_backup_before_restore");if(w("./data")){const a=B("cp",["-r","./data",p]);await new Promise(i=>{a.on("close",i)})}try{w("./data")&&await b("./data",{recursive:!0,force:!0}),await v("./data",{recursive:!0});const a=B("tar",["-xzf",n,"-C","./data"],{stdio:["pipe","pipe","pipe"]});let i="";a.stderr.on("data",h=>{i+=h.toString()});const m=await new Promise(h=>{a.on("close",h)});if(m!==0)throw new Error(`Tar extraction failed with exit code ${m}: ${i}`);$(n),await b(d,{recursive:!0,force:!0}),w(p)&&await b(p,{recursive:!0,force:!0});const g=Date.now()-c;return t.info("Backup restore completed successfully",{backup_filename:e,duration_ms:g}),{backup_filename:e,restored_at:new Date().toISOString(),duration_ms:g,status:"success"}}catch(a){if(w(p)){w("./data")&&await b("./data",{recursive:!0,force:!0});const i=B("mv",[p,"./data"]);await new Promise(m=>{i.on("close",m)})}throw a}}catch(r){t.error("Backup restore failed",{backup_filename:e,error:r.message});try{const o=z("./temp_restore");w(o)&&await b(o,{recursive:!0,force:!0})}catch(o){t.warn("Failed to clean up temporary restore files",{error:o.message})}throw r}},P=async()=>{const e=f();try{const{client:t,bucket:c}=S(),o=(await O()).backups;if(o.length===0)return{deleted_count:0,retained_count:0};o.sort((a,i)=>new Date(i.last_modified)-new Date(a.last_modified));const d=new Date,n=3600*1e3,s=24*n,u=[],_=[],l=[];for(const a of o){const i=new Date(a.last_modified),m=d-i;if(m<=24*n)u.push(a);else if(m<=30*s){const g=i.toDateString();_.find(k=>new Date(k.last_modified).toDateString()===g)?l.push(a):_.push(a)}else l.push(a)}u.length>24&&l.push(...u.slice(24));for(const a of l)try{const i=new j({Bucket:c,Key:a.filename});await t.send(i),e.info("Deleted old backup",{filename:a.filename})}catch(i){e.warn("Failed to delete backup",{filename:a.filename,error:i.message})}const p=o.length-l.length;return e.info("Backup cleanup completed",{deleted_count:l.length,retained_count:p,hourly_backups:Math.min(u.length,24),daily_backups:_.length}),{deleted_count:l.length,retained_count:p,hourly_backups:Math.min(u.length,24),daily_backups:_.length}}catch(t){throw e.error("Backup cleanup failed",{error:t.message}),t}},J=()=>{const e=f();try{const t=I();if(!t.s3){e.info("S3 not configured, backup scheduling disabled");return}const c=t.backup_schedule||"hourly";let r;switch(c){case"hourly":r=3600*1e3;break;case"daily":r=1440*60*1e3;break;case"weekly":r=10080*60*1e3;break;default:e.warn("Invalid backup schedule, using hourly",{schedule:c}),r=3600*1e3}y&&clearInterval(y),y=setInterval(async()=>{try{e.info("Starting scheduled backup",{schedule:c}),await E(),await P()}catch(o){e.error("Scheduled backup failed",{error:o.message})}},r),e.info("Backup schedule started",{schedule:c,interval_ms:r})}catch(t){e.error("Failed to start backup schedule",{error:t.message})}},N=()=>{const e=f();y&&(clearInterval(y),y=null,e.info("Backup schedule stopped"))};export{P as cleanup_old_backups,E as create_backup,S as get_s3_client,O as list_backups,V as restore_backup,J as start_backup_schedule,N as stop_backup_schedule,R as test_s3_connection};
1
+ import{S3Client as F,PutObjectCommand as L,GetObjectCommand as W,ListObjectsV2Command as D,DeleteObjectCommand as j}from"@aws-sdk/client-s3";import{createReadStream as x,createWriteStream as G,existsSync as k,unlinkSync as M,statSync as T}from"fs";import{mkdir as v,rm as h}from"fs/promises";import{resolve as g,join as $}from"path";import{spawn as z}from"child_process";import{createHash as q}from"crypto";import{get_settings as C}from"./load_settings.js";import{get_database as A}from"./query_engine.js";import H from"./logger.js";const{create_context_logger:m}=H("backup_manager");let B=null,w=null;const b=()=>{const e=m();try{const t=C();if(!t.s3)throw new Error("S3 configuration not found in settings");const{bucket:s,region:r,access_key:a,secret_key:o,endpoint:n}=t.s3;if(!s||!r||!a||!o)throw new Error("Missing required S3 configuration: bucket, region, access_key, secret_key");if(!B){const c={region:r,credentials:{accessKeyId:a,secretAccessKey:o}};n&&n!==`https://s3.${r}.amazonaws.com`&&(c.endpoint=n,c.forcePathStyle=!0),B=new F(c),e.info("S3 client initialized",{bucket:s,region:r,endpoint:n||"default"})}return{client:B,bucket:s}}catch(t){throw e.error("Failed to initialize S3 client",{error:t.message}),t}},R=async()=>{const e=m();try{const{client:t,bucket:s}=b(),r=new D({Bucket:s,MaxKeys:1});return await t.send(r),e.info("S3 connection test successful",{bucket:s}),!0}catch(t){throw e.error("S3 connection test failed",{error:t.message}),new Error(`S3 connection failed: ${t.message}`)}},I=e=>new Promise((t,s)=>{const r=q("sha256"),a=x(e);a.on("data",o=>r.update(o)),a.on("end",()=>t(r.digest("hex"))),a.on("error",s)}),V=()=>`joystickdb-backup-${new Date().toISOString().replace(/[:.]/g,"-")}.tar.gz`,J=async()=>{const e=g("./temp_backup");return await v(e,{recursive:!0}),e},N=async e=>{const t=z("tar",["--sparse","-czf",e,"-C","./data","."],{stdio:["pipe","pipe","pipe"]});let s="",r="";t.stdout.on("data",o=>{s+=o.toString()}),t.stderr.on("data",o=>{r+=o.toString()});const a=await new Promise(o=>{t.on("close",o)});if(a!==0)throw new Error(`Tar process failed with exit code ${a}: ${r}`)},Q=async(e,t,s,r,a,o)=>{const n=x(e),c=new L({Bucket:o,Key:t,Body:n,Metadata:{checksum:s,created_at:new Date().toISOString(),size_bytes:r.toString()}});await a.send(c)},U=async(e,t)=>{M(e),await h(t,{recursive:!0,force:!0})},X=async e=>{try{const t=g("./temp_backup");k(t)&&await h(t,{recursive:!0,force:!0})}catch(t){e.warn("Failed to clean up temporary backup files",{error:t.message})}},K=async()=>{const e=m(),t=Date.now();try{const{client:s,bucket:r}=b(),a=await J(),o=V(),n=$(a,o);e.info("Starting backup creation",{backup_filename:o,temp_backup_path:n}),await N(n);const c=await I(n),u=T(n);e.info("Backup file created",{backup_filename:o,size_bytes:u.size,size_mb:Math.round(u.size/1024/1024),checksum:c}),await Q(n,o,c,u.size,s,r),await U(n,a);const l=Date.now()-t;return e.info("Backup completed successfully",{backup_filename:o,duration_ms:l,size_mb:Math.round(u.size/1024/1024),checksum:c,s3_bucket:r}),{filename:o,size_bytes:u.size,checksum:c,created_at:new Date().toISOString(),duration_ms:l,s3_location:`s3://${r}/${o}`}}catch(s){throw e.error("Backup creation failed",{error:s.message}),await X(e),s}},E=async()=>{const e=m();try{const{client:t,bucket:s}=b(),r=new D({Bucket:s,Prefix:"joystickdb-backup-"}),a=await t.send(r);if(!a.Contents||a.Contents.length===0)return{backups:[],total_count:0,total_size_bytes:0};const o=a.Contents.filter(c=>c.Key.startsWith("joystickdb-backup-")&&c.Key.endsWith(".tar.gz")).map(c=>({filename:c.Key,size_bytes:c.Size,size_mb:Math.round(c.Size/1024/1024),last_modified:c.LastModified.toISOString(),s3_location:`s3://${s}/${c.Key}`})).sort((c,u)=>new Date(u.last_modified)-new Date(c.last_modified)),n=o.reduce((c,u)=>c+u.size_bytes,0);return e.info("Listed backups",{total_count:o.length,total_size_mb:Math.round(n/1024/1024)}),{backups:o,total_count:o.length,total_size_bytes:n,total_size_mb:Math.round(n/1024/1024)}}catch(t){throw e.error("Failed to list backups",{error:t.message}),t}},Y=async e=>{const t=m(),s=Date.now();try{const{client:r,bucket:a}=b(),o=g("./temp_restore");await v(o,{recursive:!0});const n=$(o,e);t.info("Starting backup restore",{backup_filename:e,temp_backup_path:n});const c=new W({Bucket:a,Key:e}),u=await r.send(c);if(!u.Body)throw new Error("Empty backup file received from S3");const l=G(n);if(await new Promise((i,d)=>{u.Body.pipe(l),l.on("finish",i),l.on("error",d),u.Body.on("error",d)}),u.Metadata&&u.Metadata.checksum){const i=await I(n);if(i!==u.Metadata.checksum)throw new Error(`Backup checksum mismatch. Expected: ${u.Metadata.checksum}, Got: ${i}`);t.info("Backup checksum verified",{checksum:i})}const p=A(),_=g("./data_backup_before_restore");if(k("./data")){const i=z("cp",["-r","./data",_]);await new Promise(d=>{i.on("close",d)})}try{k("./data")&&await h("./data",{recursive:!0,force:!0}),await v("./data",{recursive:!0});const i=z("tar",["-xzf",n,"-C","./data"],{stdio:["pipe","pipe","pipe"]});let d="";i.stderr.on("data",S=>{d+=S.toString()});const f=await new Promise(S=>{i.on("close",S)});if(f!==0)throw new Error(`Tar extraction failed with exit code ${f}: ${d}`);M(n),await h(o,{recursive:!0,force:!0}),k(_)&&await h(_,{recursive:!0,force:!0});const y=Date.now()-s;return t.info("Backup restore completed successfully",{backup_filename:e,duration_ms:y}),{backup_filename:e,restored_at:new Date().toISOString(),duration_ms:y,status:"success"}}catch(i){if(k(_)){k("./data")&&await h("./data",{recursive:!0,force:!0});const d=z("mv",[_,"./data"]);await new Promise(f=>{d.on("close",f)})}throw i}}catch(r){t.error("Backup restore failed",{backup_filename:e,error:r.message});try{const a=g("./temp_restore");k(a)&&await h(a,{recursive:!0,force:!0})}catch(a){t.warn("Failed to clean up temporary restore files",{error:a.message})}throw r}},O=async()=>{const e=m();try{const{client:t,bucket:s}=b(),a=(await E()).backups;if(a.length===0)return{deleted_count:0,retained_count:0};a.sort((i,d)=>new Date(d.last_modified)-new Date(i.last_modified));const o=new Date,n=3600*1e3,c=24*n,u=[],l=[],p=[];for(const i of a){const d=new Date(i.last_modified),f=o-d;if(f<=24*n)u.push(i);else if(f<=30*c){const y=d.toDateString();l.find(P=>new Date(P.last_modified).toDateString()===y)?p.push(i):l.push(i)}else p.push(i)}u.length>24&&p.push(...u.slice(24));for(const i of p)try{const d=new j({Bucket:s,Key:i.filename});await t.send(d),e.info("Deleted old backup",{filename:i.filename})}catch(d){e.warn("Failed to delete backup",{filename:i.filename,error:d.message})}const _=a.length-p.length;return e.info("Backup cleanup completed",{deleted_count:p.length,retained_count:_,hourly_backups:Math.min(u.length,24),daily_backups:l.length}),{deleted_count:p.length,retained_count:_,hourly_backups:Math.min(u.length,24),daily_backups:l.length}}catch(t){throw e.error("Backup cleanup failed",{error:t.message}),t}},Z=()=>{const e=m();try{const t=C();if(!t.s3){e.info("S3 not configured, backup scheduling disabled");return}const s=t.backup_schedule||"hourly";let r;switch(s){case"hourly":r=3600*1e3;break;case"daily":r=1440*60*1e3;break;case"weekly":r=10080*60*1e3;break;default:e.warn("Invalid backup schedule, using hourly",{schedule:s}),r=3600*1e3}w&&clearInterval(w),w=setInterval(async()=>{try{e.info("Starting scheduled backup",{schedule:s}),await K(),await O()}catch(a){e.error("Scheduled backup failed",{error:a.message})}},r),e.info("Backup schedule started",{schedule:s,interval_ms:r})}catch(t){e.error("Failed to start backup schedule",{error:t.message})}},ee=()=>{const e=m();w&&(clearInterval(w),w=null,e.info("Backup schedule stopped"))};export{O as cleanup_old_backups,K as create_backup,b as get_s3_client,E as list_backups,Y as restore_backup,Z as start_backup_schedule,ee as stop_backup_schedule,R as test_s3_connection};
@@ -1 +1 @@
1
- import{get_database as O}from"./query_engine.js";import j from"./logger.js";const{create_context_logger:k}=j("index_manager");let $=null;const z=()=>{try{$=O().openDB("indexes",{create:!0})}catch{throw new Error("Main database not initialized. Call initialize_database first.")}return $},_=()=>{if(!$)throw new Error("Index database not initialized. Call initialize_index_database first.");return $},g=(t,e,r,s)=>{const n=typeof s=="object"?JSON.stringify(s):String(s);return`index:${t}:${e}:${r}:${n}`},R=t=>{const e=t.split(":");return e.length<5||e[0]!=="index"?null:{database:e[1],collection:e[2],field:e[3],value:e.slice(4).join(":")}},I=(t,e,r)=>`meta:${t}:${e}:${r}`,v=(t,e)=>{const r=e.split(".");let s=t;for(const n of r){if(s==null)return;s=s[n]}return s},A=(t,e)=>t.unique===(e.unique||!1)&&t.sparse===(e.sparse||!1),C=(t,e,r,s,n,i)=>{const u=`index:${t}:${e}:${r}:`,o=n.getRange({start:u,end:u+"\xFF"});for(const{key:l}of o)n.remove(l);const c=`${t}:${e}:`,d=i.getRange({start:c,end:c+"\xFF"});if(s.unique){const l=new Map;for(const{key:x,value:p}of d){const y=JSON.parse(p),a=v(y,r);if(a!=null){const w=typeof a=="object"?JSON.stringify(a):String(a),h=l.get(w)||0;if(l.set(w,h+1),h>=1)throw new Error(`Duplicate value for unique index on ${t}.${e}.${r}: ${a}`)}}}const f=i.getRange({start:c,end:c+"\xFF"});for(const{key:l,value:x}of f){const p=JSON.parse(x),y=v(p,r);if(y==null){if(!s.sparse){const h=g(t,e,r,null),q=n.get(h)||[];q.push(p._id),n.put(h,q)}continue}const a=g(t,e,r,y),w=n.get(a)||[];w.push(p._id),n.put(a,w)}},J=async(t,e,r,s={})=>{const n=k();if(!t)throw new Error("Database name is required");if(!e)throw new Error("Collection name is required");if(!r)throw new Error("Field name is required");const i=_(),u=O(),o=I(t,e,r),c=s.upsert||!1;try{let d="created";return await i.transaction(()=>{const f=i.get(o);if(f){if(!c)throw new Error(`Index on ${t}.${e}.${r} already exists`);if(A(f,s)){d="unchanged";return}d="updated";try{C(t,e,r,s,i,u)}catch(p){throw p}}else{const x=`${t}:${e}:`,p=u.getRange({start:x,end:x+"\xFF"});for(const{key:y,value:a}of p){const w=JSON.parse(a),h=v(w,r);if(h==null){if(!s.sparse){const E=g(t,e,r,null),S=i.get(E)||[];S.push(w._id),i.put(E,S)}continue}if(s.unique){const E=g(t,e,r,h);if((i.get(E)||[]).length>0)throw new Error(`Duplicate value for unique index on ${t}.${e}.${r}: ${h}`)}const q=g(t,e,r,h),D=i.get(q)||[];D.push(w._id),i.put(q,D)}}const l={database:t,collection:e,field:r,unique:s.unique||!1,sparse:s.sparse||!1,created_at:f?f.created_at:new Date().toISOString(),updated_at:d==="updated"?new Date().toISOString():void 0};i.put(o,l)}),n.info(`Index ${d} successfully`,{database:t,collection:e,field:r,options:s,operation_type:d}),{acknowledged:!0,operation_type:d}}catch(d){throw n.error("Failed to create/upsert index",{database:t,collection:e,field:r,error:d.message}),d}},N=async(t,e,r)=>{const s=k();if(!t)throw new Error("Database name is required");if(!e)throw new Error("Collection name is required");if(!r)throw new Error("Field name is required");const n=_(),i=I(t,e,r);try{return await n.transaction(()=>{if(!n.get(i))throw new Error(`Index on ${t}.${e}.${r} does not exist`);n.remove(i);const o=`index:${t}:${e}:${r}:`,c=n.getRange({start:o,end:o+"\xFF"});for(const{key:d}of c)n.remove(d)}),s.info("Index dropped successfully",{database:t,collection:e,field:r}),{acknowledged:!0}}catch(u){throw s.error("Failed to drop index",{database:t,collection:e,field:r,error:u.message}),u}},F=(t,e)=>{const r=k();if(!t)throw new Error("Database name is required");if(!e)throw new Error("Collection name is required");const s=_(),n=[];try{const i=`meta:${t}:${e}:`,u=s.getRange({start:i,end:i+"\xFF"});for(const{key:o,value:c}of u)n.push(c);return r.info("Retrieved indexes",{database:t,collection:e,count:n.length}),n}catch(i){throw r.error("Failed to get indexes",{database:t,collection:e,error:i.message}),i}},M=async(t,e,r)=>{const s=k(),n=_();try{const i=F(t,e);await n.transaction(()=>{for(const u of i){const o=v(r,u.field);if(o==null){if(!u.sparse){const f=g(t,e,u.field,null),l=n.get(f)||[];l.push(r._id),n.put(f,l)}continue}if(u.unique){const f=g(t,e,u.field,o);if((n.get(f)||[]).length>0)throw new Error(`Duplicate value for unique index on ${t}.${e}.${u.field}: ${o}`)}const c=g(t,e,u.field,o),d=n.get(c)||[];d.push(r._id),n.put(c,d)}})}catch(i){throw s.error("Failed to update indexes on insert",{database:t,collection:e,document_id:r._id,error:i.message}),i}},b=async(t,e,r,s)=>{const n=k(),i=_();try{const u=F(t,e);await i.transaction(()=>{for(const o of u){const c=v(r,o.field),d=v(s,o.field);if(c!==d){if(c!=null){const f=g(t,e,o.field,c),x=(i.get(f)||[]).filter(p=>p!==r._id);x.length===0?i.remove(f):i.put(f,x)}else if(!o.sparse){const f=g(t,e,o.field,null),x=(i.get(f)||[]).filter(p=>p!==r._id);x.length===0?i.remove(f):i.put(f,x)}if(d!=null){if(o.unique){const x=g(t,e,o.field,d);if((i.get(x)||[]).length>0)throw new Error(`Duplicate value for unique index on ${t}.${e}.${o.field}: ${d}`)}const f=g(t,e,o.field,d),l=i.get(f)||[];l.push(s._id),i.put(f,l)}else if(!o.sparse){const f=g(t,e,o.field,null),l=i.get(f)||[];l.push(s._id),i.put(f,l)}}}})}catch(u){throw n.error("Failed to update indexes on update",{database:t,collection:e,document_id:r._id,error:u.message}),u}},B=async(t,e,r)=>{const s=k(),n=_();try{const i=F(t,e);await n.transaction(()=>{for(const u of i){const o=v(r,u.field);let c;if(o==null)if(!u.sparse)c=g(t,e,u.field,null);else continue;else c=g(t,e,u.field,o);const f=(n.get(c)||[]).filter(l=>l!==r._id);f.length===0?n.remove(c):n.put(c,f)}})}catch(i){throw s.error("Failed to update indexes on delete",{database:t,collection:e,document_id:r._id,error:i.message}),i}},G=(t,e,r,s,n)=>{const i=k(),u=_(),o=new Set;try{switch(s){case"$eq":case"eq":{const d=g(t,e,r,n);(u.get(d)||[]).forEach(l=>o.add(l));break}case"$in":{if(!Array.isArray(n))throw new Error("$in operator requires an array value");for(const d of n){const f=g(t,e,r,d);(u.get(f)||[]).forEach(x=>o.add(x))}break}case"$exists":{const d=`index:${t}:${e}:${r}:`,f=u.getRange({start:d,end:d+"\xFF"});for(const{key:l,value:x}of f){const p=R(l),y=p&&p.value!=="null";(n&&y||!n&&!y)&&x.forEach(a=>o.add(a))}break}default:return null}const c=Array.from(o);return i.info("Index query completed",{database:t,collection:e,field:r,operator:s,document_ids_found:c.length}),c.length>0?c:null}catch(c){return i.error("Failed to query index",{database:t,collection:e,field:r,operator:s,error:c.message}),null}},H=(t,e,r)=>{const s=F(t,e);for(const[n,i]of Object.entries(r))if(s.some(o=>o.field===n))if(typeof i=="object"&&i!==null&&!Array.isArray(i)){const o=Object.keys(i),c=["$eq","$in","$exists"];if(o.some(d=>c.includes(d)))return{field:n,operators:o.filter(d=>c.includes(d))}}else return{field:n,operators:["eq"]};return null},K=()=>{if($)try{const t=$.getRange();for(const{key:e}of t)$.remove(e)}catch{}$=null};export{g as build_index_key,H as can_use_index,K as cleanup_index_database,J as create_index,N as drop_index,G as find_documents_by_index,_ as get_index_database,F as get_indexes,z as initialize_index_database,R as parse_index_key,B as update_indexes_on_delete,M as update_indexes_on_insert,b as update_indexes_on_update};
1
+ import{get_database as O}from"./query_engine.js";import j from"./logger.js";const{create_context_logger:y}=j("index_manager");let a=null;const z=()=>{try{a=O().openDB("indexes",{create:!0})}catch{throw new Error("Main database not initialized. Call initialize_database first.")}return a},h=()=>{if(!a)throw new Error("Index database not initialized. Call initialize_index_database first.");return a},g=(t,e,r,s)=>{const n=typeof s=="object"?JSON.stringify(s):String(s);return`index:${t}:${e}:${r}:${n}`},R=t=>{const e=t.split(":");return e.length<5||e[0]!=="index"?null:{database:e[1],collection:e[2],field:e[3],value:e.slice(4).join(":")}},I=(t,e,r)=>`meta:${t}:${e}:${r}`,w=(t,e)=>{const r=e.split(".");let s=t;for(const n of r){if(s==null)return;s=s[n]}return s},A=(t,e)=>t.unique===(e.unique||!1)&&t.sparse===(e.sparse||!1),C=(t,e,r,s)=>{const n=`index:${t}:${e}:${r}:`,i=s.getRange({start:n,end:n+"\xFF"});for(const{key:u}of i)s.remove(u)},J=(t,e,r,s)=>{const n=`${t}:${e}:`,i=s.getRange({start:n,end:n+"\xFF"}),u=new Map;for(const{key:d,value:f}of i){const c=JSON.parse(f),o=w(c,r);if(o!=null){const x=typeof o=="object"?JSON.stringify(o):String(o),l=u.get(x)||0;if(u.set(x,l+1),l>=1)throw new Error(`Duplicate value for unique index on ${t}.${e}.${r}: ${o}`)}}},N=(t,e,r,s,n,i)=>{const u=`${t}:${e}:`,d=i.getRange({start:u,end:u+"\xFF"});for(const{key:f,value:c}of d){const o=JSON.parse(c),x=w(o,r);if(x==null){if(!s.sparse){const $=g(t,e,r,null),_=n.get($)||[];_.push(o._id),n.put($,_)}continue}const l=g(t,e,r,x),p=n.get(l)||[];p.push(o._id),n.put(l,p)}},M=(t,e,r,s,n,i)=>{C(t,e,r,n),s.unique&&J(t,e,r,i),N(t,e,r,s,n,i)},B=async(t,e,r,s={})=>{const n=y();if(!t)throw new Error("Database name is required");if(!e)throw new Error("Collection name is required");if(!r)throw new Error("Field name is required");const i=h(),u=O(),d=I(t,e,r),f=s.upsert||!1;try{let c="created";return await i.transaction(()=>{const o=i.get(d);if(o){if(!f)throw new Error(`Index on ${t}.${e}.${r} already exists`);if(A(o,s)){c="unchanged";return}c="updated";try{M(t,e,r,s,i,u)}catch(p){throw p}}else{const l=`${t}:${e}:`,p=u.getRange({start:l,end:l+"\xFF"});for(const{key:$,value:_}of p){const F=JSON.parse(_),k=w(F,r);if(k==null){if(!s.sparse){const q=g(t,e,r,null),E=i.get(q)||[];E.push(F._id),i.put(q,E)}continue}if(s.unique){const q=g(t,e,r,k);if((i.get(q)||[]).length>0)throw new Error(`Duplicate value for unique index on ${t}.${e}.${r}: ${k}`)}const S=g(t,e,r,k),D=i.get(S)||[];D.push(F._id),i.put(S,D)}}const x={database:t,collection:e,field:r,unique:s.unique||!1,sparse:s.sparse||!1,created_at:o?o.created_at:new Date().toISOString(),updated_at:c==="updated"?new Date().toISOString():void 0};i.put(d,x)}),n.info(`Index ${c} successfully`,{database:t,collection:e,field:r,options:s,operation_type:c}),{acknowledged:!0,operation_type:c}}catch(c){throw n.error("Failed to create/upsert index",{database:t,collection:e,field:r,error:c.message}),c}},b=async(t,e,r)=>{const s=y();if(!t)throw new Error("Database name is required");if(!e)throw new Error("Collection name is required");if(!r)throw new Error("Field name is required");const n=h(),i=I(t,e,r);try{return await n.transaction(()=>{if(!n.get(i))throw new Error(`Index on ${t}.${e}.${r} does not exist`);n.remove(i);const d=`index:${t}:${e}:${r}:`,f=n.getRange({start:d,end:d+"\xFF"});for(const{key:c}of f)n.remove(c)}),s.info("Index dropped successfully",{database:t,collection:e,field:r}),{acknowledged:!0}}catch(u){throw s.error("Failed to drop index",{database:t,collection:e,field:r,error:u.message}),u}},v=(t,e)=>{const r=y();if(!t)throw new Error("Database name is required");if(!e)throw new Error("Collection name is required");const s=h(),n=[];try{const i=`meta:${t}:${e}:`,u=s.getRange({start:i,end:i+"\xFF"});for(const{key:d,value:f}of u)n.push(f);return r.info("Retrieved indexes",{database:t,collection:e,count:n.length}),n}catch(i){throw r.error("Failed to get indexes",{database:t,collection:e,error:i.message}),i}},G=async(t,e,r)=>{const s=y(),n=h();if(!r)throw new Error("Document is required for index update");try{const i=v(t,e);await n.transaction(()=>{for(const u of i){const d=w(r,u.field);if(d==null){if(!u.sparse){const o=g(t,e,u.field,null),x=n.get(o)||[];x.push(r._id),n.put(o,x)}continue}if(u.unique){const o=g(t,e,u.field,d);if((n.get(o)||[]).length>0)throw new Error(`Duplicate value for unique index on ${t}.${e}.${u.field}: ${d}`)}const f=g(t,e,u.field,d),c=n.get(f)||[];c.push(r._id),n.put(f,c)}})}catch(i){throw s.error("Failed to update indexes on insert",{database:t,collection:e,document_id:r._id,error:i.message}),i}},H=async(t,e,r,s)=>{const n=y(),i=h();try{const u=v(t,e);await i.transaction(()=>{for(const d of u){const f=w(r,d.field),c=w(s,d.field);if(f!==c){if(f!=null){const o=g(t,e,d.field,f),l=(i.get(o)||[]).filter(p=>p!==r._id);l.length===0?i.remove(o):i.put(o,l)}else if(!d.sparse){const o=g(t,e,d.field,null),l=(i.get(o)||[]).filter(p=>p!==r._id);l.length===0?i.remove(o):i.put(o,l)}if(c!=null){if(d.unique){const l=g(t,e,d.field,c);if((i.get(l)||[]).length>0)throw new Error(`Duplicate value for unique index on ${t}.${e}.${d.field}: ${c}`)}const o=g(t,e,d.field,c),x=i.get(o)||[];x.push(s._id),i.put(o,x)}else if(!d.sparse){const o=g(t,e,d.field,null),x=i.get(o)||[];x.push(s._id),i.put(o,x)}}}})}catch(u){throw n.error("Failed to update indexes on update",{database:t,collection:e,document_id:r._id,error:u.message}),u}},K=async(t,e,r)=>{const s=y(),n=h();try{const i=v(t,e);await n.transaction(()=>{for(const u of i){const d=w(r,u.field);let f;if(d==null)if(!u.sparse)f=g(t,e,u.field,null);else continue;else f=g(t,e,u.field,d);const o=(n.get(f)||[]).filter(x=>x!==r._id);o.length===0?n.remove(f):n.put(f,o)}})}catch(i){throw s.error("Failed to update indexes on delete",{database:t,collection:e,document_id:r._id,error:i.message}),i}},L=(t,e,r,s,n)=>{const i=y(),u=h(),d=new Set;try{switch(s){case"$eq":case"eq":{const c=g(t,e,r,n);(u.get(c)||[]).forEach(x=>d.add(x));break}case"$in":{if(!Array.isArray(n))throw new Error("$in operator requires an array value");for(const c of n){const o=g(t,e,r,c);(u.get(o)||[]).forEach(l=>d.add(l))}break}case"$exists":{const c=`index:${t}:${e}:${r}:`,o=u.getRange({start:c,end:c+"\xFF"});for(const{key:x,value:l}of o){const p=R(x),$=p&&p.value!=="null";(n&&$||!n&&!$)&&l.forEach(_=>d.add(_))}break}default:return null}const f=Array.from(d);return i.info("Index query completed",{database:t,collection:e,field:r,operator:s,document_ids_found:f.length}),f.length>0?f:null}catch(f){return i.error("Failed to query index",{database:t,collection:e,field:r,operator:s,error:f.message}),null}},P=(t,e,r)=>{const s=v(t,e);for(const[n,i]of Object.entries(r))if(s.some(d=>d.field===n))if(typeof i=="object"&&i!==null&&!Array.isArray(i)){const d=Object.keys(i),f=["$eq","$in","$exists"];if(d.some(c=>f.includes(c)))return{field:n,operators:d.filter(c=>f.includes(c))}}else return{field:n,operators:["eq"]};return null},Q=()=>{if(a)try{const t=a.getRange();for(const{key:e}of t)a.remove(e)}catch{}a=null};export{g as build_index_key,P as can_use_index,Q as cleanup_index_database,B as create_index,b as drop_index,L as find_documents_by_index,h as get_index_database,v as get_indexes,z as initialize_index_database,R as parse_index_key,K as update_indexes_on_delete,G as update_indexes_on_insert,H as update_indexes_on_update};
@@ -1 +1 @@
1
- import{encode_message as s}from"./tcp_protocol.js";import{get_write_forwarder as g}from"./write_forwarder.js";import{get_replication_manager as h}from"./replication_manager.js";import{check_and_grow_map_size as b}from"./query_engine.js";import{performance_monitor as f}from"./performance_monitor.js";import x from"./logger.js";import k from"./operations/insert_one.js";import v from"./operations/update_one.js";import q from"./operations/delete_one.js";import D from"./operations/delete_many.js";import A from"./operations/bulk_write.js";import I from"./operations/find_one.js";import $ from"./operations/find.js";import C from"./operations/create_index.js";import E from"./operations/drop_index.js";import L from"./operations/get_indexes.js";import U from"./operations/admin.js";const{create_context_logger:Z}=x("operation_dispatcher"),j=o=>!o||typeof o!="string"||o.length>64||["admin","config","local"].includes(o.toLowerCase())?!1:/^[a-zA-Z0-9_-]+$/.test(o),ee=async(o,r,e,u,c=0,a=null,d=null)=>{const l=Z(),m=Date.now();if(!u(o)){const t=s({ok:0,error:"Authentication required"});o.write(t),f.log_structured_operation(o.id,r,null,0,"error","Authentication required",c,0);return}const i=e.database||"default";if(!j(i)){const t=s({ok:0,error:"Invalid database name. Database names must be alphanumeric with underscores/hyphens, max 64 characters, and cannot be reserved names (admin, config, local)."});o.write(t),f.log_structured_operation(o.id,r,e.collection,0,"error","Invalid database name",c,0);return}if(!await g().forward_operation(o,r,e))try{let n;switch(r){case"insert_one":n=await k(i,e.collection,e.document,e.options);break;case"update_one":n=await v(i,e.collection,e.filter,e.update,e.options);break;case"delete_one":n=await q(i,e.collection,e.filter,e.options);break;case"delete_many":n=await D(i,e.collection,e.filter,e.options);break;case"bulk_write":n=await A(i,e.collection,e.operations,e.options);break;case"find_one":n=await I(i,e.collection,e.filter,e.options);break;case"find":n=await $(i,e.collection,e.filter,e.options);break;case"create_index":n=await C(i,e.collection,e.field,e.options);break;case"drop_index":n=await E(i,e.collection,e.field);break;case"get_indexes":n=await L(i,e.collection);break;default:throw new Error(`Unsupported operation: ${r}`)}const t=Date.now()-m;let _;r==="find_one"?_={ok:1,document:n}:r==="find"?_={ok:1,documents:n}:_={ok:1,...n};const p=s(_),w=p.length;o.write(p),f.log_structured_operation(o.id,r,e.collection,t,"success",null,c,w),l.info("Database operation completed",{client_id:o.id,op:r,collection:e.collection,duration_ms:t,status:"success",request_size:c,response_size:w}),r!=="find"&&r!=="find_one"&&r!=="get_indexes"&&(h().queue_replication(r,e.collection,e),setImmediate(()=>b()))}catch(n){const t=Date.now()-m;f.log_structured_operation(o.id,r,e.collection,t,"error",n.message,c,0),l.error("Database operation failed",{client_id:o.id,op:r,collection:e.collection,duration_ms:t,status:"error",error:n.message,request_size:c});const _={ok:0,error:n.message},p=s(_);o.write(p)}},oe=async(o,r,e,u=null,c=null)=>{if(!e(o)){const d=s({ok:!1,error:"Authentication required"});o.write(d);return}try{const a=r?.admin_action,l=await U(a,r||{},u,c);if(a){const m={ok:1,...l},i=s(m);o.write(i)}else{const m={ok:!0,...l},i=s(m);o.write(i)}}catch(a){const d={ok:0,error:`Admin operation failed: ${a.message}`},l=s(d);o.write(l)}},re=o=>{const e=s({ok:1});o.write(e)};export{oe as handle_admin_operation,ee as handle_database_operation,re as handle_ping_operation};
1
+ import{encode_message as f}from"./tcp_protocol.js";import{get_write_forwarder as w}from"./write_forwarder.js";import{get_replication_manager as h}from"./replication_manager.js";import{check_and_grow_map_size as x}from"./query_engine.js";import{performance_monitor as v}from"./performance_monitor.js";import D from"./logger.js";import q from"./operations/insert_one.js";import A from"./operations/update_one.js";import b from"./operations/delete_one.js";import I from"./operations/delete_many.js";import $ from"./operations/bulk_write.js";import C from"./operations/find_one.js";import E from"./operations/find.js";import L from"./operations/create_index.js";import U from"./operations/drop_index.js";import Z from"./operations/get_indexes.js";import j from"./operations/admin.js";const{create_context_logger:d}=D("operation_dispatcher"),k=e=>e.length>64,B=e=>["admin","config","local"].includes(e.toLowerCase()),F=e=>/^[a-zA-Z0-9_-]+$/.test(e),G=e=>!e||typeof e!="string"||k(e)||B(e)?!1:F(e),H=()=>({ok:0,error:"Authentication required"}),J=()=>({ok:0,error:"Invalid database name. Database names must be alphanumeric with underscores/hyphens, max 64 characters, and cannot be reserved names (admin, config, local)."}),_=(e,r)=>{const o=f(r);e.write(o)},m=(e,r,o,s,t,n,c,i)=>{v.log_structured_operation(e,r,o,s,t,n,c,i)},g=(e,r,o,s,t,n,c,i,l=null)=>{const p={client_id:r,op:o,collection:s,duration_ms:t,status:n,request_size:c};n==="success"?(p.response_size=i,e.info("Database operation completed",p)):(p.error=l,e.error("Database operation failed",p))},K=async(e,r,o)=>{switch(e){case"insert_one":return await q(r,o.collection,o.document,o.options);case"update_one":return await A(r,o.collection,o.filter,o.update,o.options);case"delete_one":return await b(r,o.collection,o.filter,o.options);case"delete_many":return await I(r,o.collection,o.filter,o.options);case"bulk_write":return await $(r,o.collection,o.operations,o.options);case"find_one":return await C(r,o.collection,o.filter,o.options);case"find":return await E(r,o.collection,o.filter,o.options);case"create_index":return await L(r,o.collection,o.field,o.options);case"drop_index":return await U(r,o.collection,o.field);case"get_indexes":return await Z(r,o.collection);default:throw new Error(`Unsupported operation: ${e}`)}},M=(e,r)=>e==="find_one"?{ok:1,document:r}:e==="find"?{ok:1,documents:r}:{ok:1,...r},N=e=>!["find","find_one","get_indexes"].includes(e),O=(e,r)=>{if(!N(e))return;h().queue_replication(e,r.collection,r),setImmediate(()=>x())},P=(e,r,o,s,t,n)=>{const c=d(),i=Date.now()-t,l=M(r,s),u=f(l).length;_(e,l),m(e.id,r,o.collection,i,"success",null,n,u),g(c,e.id,r,o.collection,i,"success",n,u),O(r,o)},Q=(e,r,o,s,t,n)=>{const c=d(),i=Date.now()-t;m(e.id,r,o.collection,i,"error",s.message,n,0),g(c,e.id,r,o.collection,i,"error",n,0,s.message);const l={ok:0,error:s.message};_(e,l)},pe=async(e,r,o,s,t=0,n=null,c=null)=>{const i=Date.now();if(!s(e)){const a=H();_(e,a),m(e.id,r,null,0,"error","Authentication required",t,0);return}const l=o.database||"default";if(!G(l)){const a=J();_(e,a),m(e.id,r,o.collection,0,"error","Invalid database name",t,0);return}if(!await w().forward_operation(e,r,o))try{const a=await K(r,l,o);P(e,r,o,a,i,t)}catch(a){Q(e,r,o,a,i,t)}},R=()=>({ok:!1,error:"Authentication required"}),S=(e,r)=>e?{ok:1,...r}:{ok:!0,...r},T=e=>({ok:0,error:`Admin operation failed: ${e}`}),me=async(e,r,o,s=null,t=null)=>{if(!o(e)){const n=R();_(e,n);return}try{const n=r?.admin_action,i=await j(n,r||{},s,t),l=S(n,i);_(e,l)}catch(n){const c=T(n.message);_(e,c)}},ue=e=>{_(e,{ok:1})};export{me as handle_admin_operation,pe as handle_database_operation,ue as handle_ping_operation};
@@ -1 +1 @@
1
- import{get_database as x}from"../query_engine.js";import{get_settings as q}from"../load_settings.js";import{get_write_queue as F}from"../write_queue.js";import{get_auth_stats as C}from"../auth_manager.js";import{get_query_statistics as R,get_auto_index_statistics as j,force_index_evaluation as W,remove_automatic_indexes as A}from"../auto_index_manager.js";import{create_index as U,drop_index as J,get_indexes as B}from"../index_manager.js";import{test_s3_connection as L,create_backup as T,list_backups as I,restore_backup as G,cleanup_old_backups as H}from"../backup_manager.js";import{get_replication_manager as v}from"../replication_manager.js";import{get_write_forwarder as K}from"../write_forwarder.js";import Q from"../logger.js";import{performance_monitor as w}from"../performance_monitor.js";const{create_context_logger:k}=Q("admin"),V=()=>{const r=k();try{const e=x();let a;try{a=q()}catch{a={port:1983}}let o={};try{const s=e.getStats?e.getStats():{};o={pageSize:s.pageSize||0,treeDepth:s.treeDepth||0,treeBranchPages:s.treeBranchPages||0,treeLeafPages:s.treeLeafPages||0,entryCount:s.entryCount||0,mapSize:s.mapSize||0,lastPageNumber:s.lastPageNumber||0}}catch{o={error:"Could not retrieve database stats"}}const n={};let _=0;try{for(const{key:s}of e.getRange())if(typeof s=="string"&&s.includes(":")&&!s.startsWith("_")){const l=s.split(":")[0];n[l]=(n[l]||0)+1,_++}}catch(s){r.warn("Could not iterate database range for stats",{error:s.message})}const t=process.memoryUsage(),i={rss:Math.round(t.rss/1024/1024),heapTotal:Math.round(t.heapTotal/1024/1024),heapUsed:Math.round(t.heapUsed/1024/1024),external:Math.round(t.external/1024/1024)},c=o.mapSize>0?Math.round(o.lastPageNumber*o.pageSize/o.mapSize*100):0;return{server:{uptime:Math.floor(process.uptime()),uptime_formatted:E(process.uptime()),memory_usage:i,memory_usage_raw:t,node_version:process.version,platform:process.platform,arch:process.arch,pid:process.pid,cpu_usage:process.cpuUsage()},database:{total_documents:_,total_collections:Object.keys(n).length,collections:n,stats:o,map_size_usage_percent:c,disk_usage:{map_size_mb:Math.round((o.mapSize||0)/1024/1024),used_space_mb:Math.round((o.lastPageNumber||0)*(o.pageSize||0)/1024/1024)}},performance:{ops_per_second:P(),avg_response_time_ms:N()}}}catch(e){throw r.error("Failed to get enhanced stats",{error:e.message}),e}},E=r=>{const e=Math.floor(r/86400),a=Math.floor(r%86400/3600),o=Math.floor(r%3600/60),n=Math.floor(r%60);return e>0?`${e}d ${a}h ${o}m ${n}s`:a>0?`${a}h ${o}m ${n}s`:o>0?`${o}m ${n}s`:`${n}s`};let $=0,D=0,X=Date.now();const P=()=>{const r=(Date.now()-X)/1e3;return r>0?Math.round($/r):0},N=()=>$>0?Math.round(D/$):0,Y=r=>{$++,D+=r},Z=(r="default")=>{const e=k();try{const a=x(),o={};let n=0;try{for(const{key:t}of a.getRange())if(typeof t=="string"&&t.includes(":")&&!t.startsWith("_")){const i=t.split(":");if(i.length>=3){const c=i[0],s=i[1];c===r&&(o[s]||(o[s]={name:s,document_count:0,indexes:[],estimated_size_bytes:0}),o[s].document_count++,n++)}}}catch(t){e.warn("Could not iterate database range for collections",{error:t.message});const i=["admin_test","test_collection","queue_test","users","products","orders","sessions","logs","analytics","settings","another_collection","list_test","pagination_test","get_test","query_test","admin_insert_test","admin_update_test","admin_delete_test"];for(const c of i)try{const s=`${r}:${c}:`,l=a.getRange({start:s,end:s+"\xFF"});let u=0;for(const h of l)u++,n++;u>0&&(o[c]={name:c,document_count:u,indexes:[],estimated_size_bytes:u*100})}catch{continue}}try{const t=`index:${r}:`,i=a.getRange({start:t,end:t+"\xFF"});for(const{key:c,value:s}of i)if(typeof c=="string"&&c.startsWith(t)){const l=c.substring(t.length),u=l.split(":")[0];o[u]&&(o[u].indexes.includes(l.split(":")[1])||o[u].indexes.push(l.split(":")[1]))}}catch(t){e.warn("Could not iterate index range",{error:t.message})}const _=Object.values(o);return{collections:_,total_collections:_.length,total_documents:n}}catch(a){throw e.error("Failed to list collections",{error:a.message}),a}},ee=(r,e={})=>{const a=k();if(!r)throw new Error("Collection name is required");try{const o=x(),{limit:n=50,skip:_=0,sort_field:t,sort_order:i="asc",database:c="default"}=e,s=[],l=`${c}:${r}:`;let u=0,h=0;for(const{key:p,value:b}of o.getRange({start:l,end:l+"\xFF"}))if(typeof p=="string"&&p.startsWith(l)){if(h<_){h++;continue}if(u>=n)break;try{const d=JSON.parse(b),m=p.substring(l.length);s.push({_id:m,...d}),u++}catch(d){a.warn("Could not parse document",{collection:r,key:p,error:d.message})}}return t&&s.length>0&&s.sort((p,b)=>{const d=p[t],m=b[t];return i==="desc"?m>d?1:m<d?-1:0:d>m?1:d<m?-1:0}),{collection:r,documents:s,count:s.length,skip:_,limit:n,has_more:u===n}}catch(o){throw a.error("Failed to list documents",{collection:r,error:o.message}),o}},te=(r,e,a="default")=>{const o=k();if(!r||!e)throw new Error("Collection name and document ID are required");try{const n=x(),_=`${a}:${r}:${e}`,t=n.get(_);if(!t)return{found:!1,collection:r,document_id:e};const i=JSON.parse(t);return{found:!0,collection:r,document_id:e,document:{_id:e,...i}}}catch(n){throw o.error("Failed to get document",{collection:r,document_id:e,error:n.message}),n}},re=(r,e={},a={})=>{const o=k();if(!r)throw new Error("Collection name is required");try{const n=x(),{limit:_=100,skip:t=0,database:i="default"}=a,c=[],s=`${i}:${r}:`;let l=0,u=0,h=0;for(const{key:p,value:b}of n.getRange({start:s,end:s+"\xFF"}))if(typeof p=="string"&&p.startsWith(s)){h++;try{const d=JSON.parse(b),z={_id:p.substring(s.length),...d};if(Object.keys(e).every(M=>{const y=e[M],g=z[M];return typeof y=="object"&&y!==null?Object.keys(y).every(S=>{const f=y[S];switch(S){case"$gt":return g>f;case"$gte":return g>=f;case"$lt":return g<f;case"$lte":return g<=f;case"$ne":return g!==f;case"$in":return Array.isArray(f)&&f.includes(g);case"$regex":const O=y.$options||"";return new RegExp(f,O).test(String(g));default:return g===y}}):g===y})){if(u<t){u++;continue}if(l>=_)break;c.push(z),l++}}catch(d){o.warn("Could not parse document during query",{collection:r,key:p,error:d.message})}}return{collection:r,filter:e,documents:c,count:c.length,total_examined:h,skip:t,limit:_,has_more:l===_}}catch(n){throw o.error("Failed to query documents",{collection:r,filter:e,error:n.message}),n}},oe=async(r,e,a,o={})=>await(await import("./insert_one.js")).default(r,e,a,o),se=async(r,e,a,o,n={})=>await(await import("./update_one.js")).default(r,e,a,o,n),ne=async(r,e,a,o={})=>await(await import("./delete_one.js")).default(r,e,a,o);var ye=async(r,e={},a,o)=>{const n=k(),_=Date.now();try{let t;switch(r){case"stats":t={server:{uptime:Math.floor(process.uptime()),uptime_formatted:E(process.uptime()),node_version:process.version,platform:process.platform,arch:process.arch,pid:process.pid},memory:w.get_memory_stats(),database:{...w.get_database_stats(),map_size_mb:Math.round((w.get_database_stats()?.map_size||0)/1024/1024),used_space_mb:Math.round((w.get_database_stats()?.used_space||0)/1024/1024),usage_percent:w.get_database_stats()?.usage_percent||0},performance:{ops_per_second:P(),avg_response_time_ms:N()},system:w.get_system_stats(),connections:a?.get_stats()||{},write_queue:F()?.get_stats()||{},authentication:{authenticated_clients:o?.size||0,...C()},settings:(()=>{try{return{port:q().port||1983}}catch{return{port:1983}}})()};break;case"list_collections":t=Z();break;case"list_documents":t=ee(e.collection,{limit:e.limit,skip:e.skip,sort_field:e.sort_field,sort_order:e.sort_order});break;case"get_document":t=te(e.collection,e.document_id);break;case"query_documents":t=re(e.collection,e.filter,{limit:e.limit,skip:e.skip});break;case"insert_document":t=await oe(e.database||"default",e.collection,e.document,e.options);break;case"update_document":const c=e.document_id?{_id:e.document_id}:e.filter;t=await se(e.database||"default",e.collection,c,e.update,e.options);break;case"delete_document":const s=e.document_id?{_id:e.document_id}:e.filter;t=await ne(e.database||"default",e.collection,s,e.options);break;case"test_s3_connection":t=await L();break;case"backup_now":t=await T();break;case"list_backups":t=await I();break;case"restore_backup":if(!e.backup_filename)throw new Error("backup_filename is required for restore operation");t=await G(e.backup_filename);break;case"cleanup_backups":t=await H();break;case"get_auto_index_stats":t=j();break;case"get_query_stats":t=R(e.collection);break;case"evaluate_auto_indexes":t=await W(e.collection);break;case"remove_auto_indexes":if(!e.collection)throw new Error("collection is required for remove_auto_indexes operation");t=await A(e.collection,e.field_names);break;case"create_index":if(!e.collection||!e.field)throw new Error("collection and field are required for create_index operation");t=await U(e.database||"default",e.collection,e.field,e.options);break;case"drop_index":if(!e.collection||!e.field)throw new Error("collection and field are required for drop_index operation");t=await J(e.database||"default",e.collection,e.field);break;case"get_indexes":if(!e.collection)throw new Error("collection is required for get_indexes operation");t={indexes:B(e.database||"default",e.collection)};break;case"get_replication_status":t=v().get_replication_status();break;case"add_secondary":if(!e.id||!e.ip||!e.port||!e.private_key)throw new Error("id, ip, port, and private_key are required for add_secondary operation");t=await v().add_secondary({id:e.id,ip:e.ip,port:e.port,private_key:e.private_key,enabled:!0});break;case"remove_secondary":if(!e.secondary_id)throw new Error("secondary_id is required for remove_secondary operation");t=v().remove_secondary(e.secondary_id);break;case"sync_secondaries":t=await v().sync_secondaries();break;case"get_secondary_health":t=v().get_secondary_health();break;case"get_forwarder_status":t=K().get_forwarder_status();break;default:t={...V(),connections:a?.get_stats()||{},write_queue:F()?.get_stats()||{},authentication:{authenticated_clients:o?.size||0,...C()},settings:(()=>{try{return{port:q().port||1983}}catch{return{port:1983}}})()}}const i=Date.now()-_;return Y(i),n.info("Admin operation completed",{admin_action:r||"default",duration_ms:i,status:"success"}),t}catch(t){const i=Date.now()-_;throw n.error("Admin operation failed",{admin_action:r||"default",duration_ms:i,status:"error",error:t.message}),t}};export{ye as default,Y as track_operation};
1
+ import{get_database as b}from"../query_engine.js";import{get_settings as x}from"../load_settings.js";import{get_write_queue as $}from"../write_queue.js";import{get_auth_stats as q}from"../auth_manager.js";import{get_query_statistics as F,get_auto_index_statistics as C,force_index_evaluation as E,remove_automatic_indexes as D}from"../auto_index_manager.js";import{create_index as O,drop_index as P,get_indexes as N}from"../index_manager.js";import{test_s3_connection as R,create_backup as j,list_backups as U,restore_backup as W,cleanup_old_backups as A}from"../backup_manager.js";import{get_replication_manager as w}from"../replication_manager.js";import{get_write_forwarder as J}from"../write_forwarder.js";import B from"../logger.js";import{performance_monitor as h}from"../performance_monitor.js";const{create_context_logger:y}=B("admin"),L=()=>{try{return x()}catch{return{port:1983}}},T=t=>{try{const e=t.getStats?t.getStats():{};return{pageSize:e.pageSize||0,treeDepth:e.treeDepth||0,treeBranchPages:e.treeBranchPages||0,treeLeafPages:e.treeLeafPages||0,entryCount:e.entryCount||0,mapSize:e.mapSize||0,lastPageNumber:e.lastPageNumber||0}}catch{return{error:"Could not retrieve database stats"}}},I=(t,e)=>{const r={};let s=0;try{for(const{key:n}of t.getRange())if(typeof n=="string"&&n.includes(":")&&!n.startsWith("_")){const a=n.split(":")[0];r[a]=(r[a]||0)+1,s++}}catch(n){e.warn("Could not iterate database range for stats",{error:n.message})}return{collections:r,total_documents:s}},G=()=>{const t=process.memoryUsage();return{rss:Math.round(t.rss/1024/1024),heapTotal:Math.round(t.heapTotal/1024/1024),heapUsed:Math.round(t.heapUsed/1024/1024),external:Math.round(t.external/1024/1024)}},H=t=>t.mapSize>0?Math.round(t.lastPageNumber*t.pageSize/t.mapSize*100):0,K=t=>({uptime:Math.floor(process.uptime()),uptime_formatted:v(process.uptime()),memory_usage:t,memory_usage_raw:process.memoryUsage(),node_version:process.version,platform:process.platform,arch:process.arch,pid:process.pid,cpu_usage:process.cpuUsage()}),Q=(t,e,r,s)=>({total_documents:t,total_collections:Object.keys(e).length,collections:e,stats:r,map_size_usage_percent:s,disk_usage:{map_size_mb:Math.round((r.mapSize||0)/1024/1024),used_space_mb:Math.round((r.lastPageNumber||0)*(r.pageSize||0)/1024/1024)}}),V=()=>{const t=y();try{const e=b(),r=L(),s=T(e),{collections:n,total_documents:a}=I(e,t),o=G(),c=H(s);return{server:K(o),database:Q(a,n,s,c),performance:{ops_per_second:M(),avg_response_time_ms:S()}}}catch(e){throw t.error("Failed to get enhanced stats",{error:e.message}),e}},v=t=>{const e=Math.floor(t/86400),r=Math.floor(t%86400/3600),s=Math.floor(t%3600/60),n=Math.floor(t%60);return e>0?`${e}d ${r}h ${s}m ${n}s`:r>0?`${r}h ${s}m ${n}s`:s>0?`${s}m ${n}s`:`${n}s`};let k=0,z=0,X=Date.now();const M=()=>{const t=(Date.now()-X)/1e3;return t>0?Math.round(k/t):0},S=()=>k>0?Math.round(z/k):0,Y=t=>{k++,z+=t},Z=t=>({name:t,document_count:0,indexes:[],estimated_size_bytes:0}),ee=(t,e,r)=>{const s={};let n=0;try{for(const{key:a}of t.getRange())if(typeof a=="string"&&a.includes(":")&&!a.startsWith("_")){const o=a.split(":");if(o.length>=3){const c=o[0],i=o[1];c===e&&(s[i]||(s[i]=Z(i)),s[i].document_count++,n++)}}}catch(a){r.warn("Could not iterate database range for collections",{error:a.message})}return{collections_map:s,total_documents:n}},te=(t,e,r)=>{const s=["admin_test","test_collection","queue_test","users","products","orders","sessions","logs","analytics","settings","another_collection","list_test","pagination_test","get_test","query_test","admin_insert_test","admin_update_test","admin_delete_test"];let n=0;for(const a of s)try{const o=`${e}:${a}:`,c=t.getRange({start:o,end:o+"\xFF"});let i=0;for(const _ of c)i++,n++;i>0&&(r[a]={name:a,document_count:i,indexes:[],estimated_size_bytes:i*100})}catch{continue}return n},re=(t,e,r,s)=>{try{const n=`index:${e}:`,a=t.getRange({start:n,end:n+"\xFF"});for(const{key:o,value:c}of a)if(typeof o=="string"&&o.startsWith(n)){const i=o.substring(n.length),_=i.split(":")[0],u=i.split(":")[1];r[_]&&u&&(r[_].indexes.includes(u)||r[_].indexes.push(u))}}catch(n){s.warn("Could not iterate index range",{error:n.message})}},oe=(t="default")=>{const e=y();try{const r=b();let{collections_map:s,total_documents:n}=ee(r,t,e);Object.keys(s).length===0&&(n+=te(r,t,s)),re(r,t,s,e);const a=Object.values(s);return{collections:a,total_collections:a.length,total_documents:n}}catch(r){throw e.error("Failed to list collections",{error:r.message}),r}},se=(t,e={})=>{const r=y();if(!t)throw new Error("Collection name is required");try{const s=b(),{limit:n=50,skip:a=0,sort_field:o,sort_order:c="asc",database:i="default"}=e,_=[],u=`${i}:${t}:`;let p=0,g=0;for(const{key:d,value:f}of s.getRange({start:u,end:u+"\xFF"}))if(typeof d=="string"&&d.startsWith(u)){if(g<a){g++;continue}if(p>=n)break;try{const l=JSON.parse(f),m=d.substring(u.length);_.push({_id:m,...l}),p++}catch(l){r.warn("Could not parse document",{collection:t,key:d,error:l.message})}}return o&&_.length>0&&_.sort((d,f)=>{const l=d[o],m=f[o];return c==="desc"?m>l?1:m<l?-1:0:l>m?1:l<m?-1:0}),{collection:t,documents:_,count:_.length,skip:a,limit:n,has_more:p===n}}catch(s){throw r.error("Failed to list documents",{collection:t,error:s.message}),s}},ne=(t,e,r="default")=>{const s=y();if(!t||!e)throw new Error("Collection name and document ID are required");try{const n=b(),a=`${r}:${t}:${e}`,o=n.get(a);if(!o)return{found:!1,collection:t,document_id:e};const c=JSON.parse(o);return{found:!0,collection:t,document_id:e,document:{_id:e,...c}}}catch(n){throw s.error("Failed to get document",{collection:t,document_id:e,error:n.message}),n}},ae=(t,e,r,s)=>{switch(t){case"$gt":return r>e;case"$gte":return r>=e;case"$lt":return r<e;case"$lte":return r<=e;case"$ne":return r!==e;case"$in":return Array.isArray(e)&&e.includes(r);case"$regex":const n=s.$options||"";return new RegExp(e,n).test(String(r));default:return r===s}},ce=(t,e)=>Object.keys(e).every(r=>{const s=e[r],n=t[r];return typeof s=="object"&&s!==null?Object.keys(s).every(a=>{const o=s[a];return ae(a,o,n,s)}):n===s}),ie=(t,e,r,s,n)=>{try{const a=JSON.parse(e),c={_id:t.substring(r.length),...a};return ce(c,s)?c:null}catch(a){return n.warn("Could not parse document during query",{key:t,error:a.message}),null}},_e=(t,e={},r={})=>{const s=y();if(!t)throw new Error("Collection name is required");try{const n=b(),{limit:a=100,skip:o=0,database:c="default"}=r,i=[],_=`${c}:${t}:`;let u=0,p=0,g=0;for(const{key:d,value:f}of n.getRange({start:_,end:_+"\xFF"}))if(typeof d=="string"&&d.startsWith(_)){g++;const l=ie(d,f,_,e,s);if(l){if(p<o){p++;continue}if(u>=a)break;i.push(l),u++}}return{collection:t,filter:e,documents:i,count:i.length,total_examined:g,skip:o,limit:a,has_more:u===a}}catch(n){throw s.error("Failed to query documents",{collection:t,filter:e,error:n.message}),n}},ue=async(t,e,r,s={})=>await(await import("./insert_one.js")).default(t,e,r,s),le=async(t,e,r,s,n={})=>await(await import("./update_one.js")).default(t,e,r,s,n),de=async(t,e,r,s={})=>await(await import("./delete_one.js")).default(t,e,r,s);var qe=async(t,e={},r,s)=>{const n=y(),a=Date.now();try{let o;switch(t){case"stats":o={server:{uptime:Math.floor(process.uptime()),uptime_formatted:v(process.uptime()),node_version:process.version,platform:process.platform,arch:process.arch,pid:process.pid},memory:h.get_memory_stats(),database:{...h.get_database_stats(),map_size_mb:Math.round((h.get_database_stats()?.map_size||0)/1024/1024),used_space_mb:Math.round((h.get_database_stats()?.used_space||0)/1024/1024),usage_percent:h.get_database_stats()?.usage_percent||0},performance:{ops_per_second:M(),avg_response_time_ms:S()},system:h.get_system_stats(),connections:r?.get_stats()||{},write_queue:$()?.get_stats()||{},authentication:{authenticated_clients:s?.size||0,...q()},settings:(()=>{try{return{port:x().port||1983}}catch{return{port:1983}}})()};break;case"list_collections":o=oe();break;case"list_documents":o=se(e.collection,{limit:e.limit,skip:e.skip,sort_field:e.sort_field,sort_order:e.sort_order});break;case"get_document":o=ne(e.collection,e.document_id);break;case"query_documents":o=_e(e.collection,e.filter,{limit:e.limit,skip:e.skip});break;case"insert_document":o=await ue(e.database||"default",e.collection,e.document,e.options);break;case"update_document":const i=e.document_id?{_id:e.document_id}:e.filter;o=await le(e.database||"default",e.collection,i,e.update,e.options);break;case"delete_document":const _=e.document_id?{_id:e.document_id}:e.filter;o=await de(e.database||"default",e.collection,_,e.options);break;case"test_s3_connection":o=await R();break;case"backup_now":o=await j();break;case"list_backups":o=await U();break;case"restore_backup":if(!e.backup_filename)throw new Error("backup_filename is required for restore operation");o=await W(e.backup_filename);break;case"cleanup_backups":o=await A();break;case"get_auto_index_stats":o=C();break;case"get_query_stats":o=F(e.collection);break;case"evaluate_auto_indexes":o=await E(e.collection);break;case"remove_auto_indexes":if(!e.collection)throw new Error("collection is required for remove_auto_indexes operation");o=await D(e.collection,e.field_names);break;case"create_index":if(!e.collection||!e.field)throw new Error("collection and field are required for create_index operation");o=await O(e.database||"default",e.collection,e.field,e.options);break;case"drop_index":if(!e.collection||!e.field)throw new Error("collection and field are required for drop_index operation");o=await P(e.database||"default",e.collection,e.field);break;case"get_indexes":if(!e.collection)throw new Error("collection is required for get_indexes operation");o={indexes:N(e.database||"default",e.collection)};break;case"get_replication_status":o=w().get_replication_status();break;case"add_secondary":if(!e.id||!e.ip||!e.port||!e.private_key)throw new Error("id, ip, port, and private_key are required for add_secondary operation");o=await w().add_secondary({id:e.id,ip:e.ip,port:e.port,private_key:e.private_key,enabled:!0});break;case"remove_secondary":if(!e.secondary_id)throw new Error("secondary_id is required for remove_secondary operation");o=w().remove_secondary(e.secondary_id);break;case"sync_secondaries":o=await w().sync_secondaries();break;case"get_secondary_health":o=w().get_secondary_health();break;case"get_forwarder_status":o=J().get_forwarder_status();break;default:o={...V(),connections:r?.get_stats()||{},write_queue:$()?.get_stats()||{},authentication:{authenticated_clients:s?.size||0,...q()},settings:(()=>{try{return{port:x().port||1983}}catch{return{port:1983}}})()}}const c=Date.now()-a;return Y(c),n.info("Admin operation completed",{admin_action:t||"default",duration_ms:c,status:"success"}),o}catch(o){const c=Date.now()-a;throw n.error("Admin operation failed",{admin_action:t||"default",duration_ms:c,status:"error",error:o.message}),o}};export{qe as default,Y as track_operation};
@@ -1 +1 @@
1
- import{get_database as m,generate_document_id as g,build_collection_key as y}from"../query_engine.js";import{get_write_queue as b}from"../write_queue.js";import k from"../logger.js";const{create_context_logger:S}=k("bulk_write"),O=(r,o)=>{const e={...r};for(const[s,n]of Object.entries(o))switch(s){case"$set":Object.assign(e,n);break;case"$unset":for(const t of Object.keys(n))delete e[t];break;case"$inc":for(const[t,i]of Object.entries(n))e[t]=(e[t]||0)+i;break;case"$push":for(const[t,i]of Object.entries(n))Array.isArray(e[t])||(e[t]=[]),e[t].push(i);break;case"$pull":for(const[t,i]of Object.entries(n))Array.isArray(e[t])&&(e[t]=e[t].filter(c=>c!==i));break;default:throw new Error(`Unsupported update operator: ${s}`)}return e},h=(r,o)=>{if(!o||Object.keys(o).length===0)return!0;for(const[e,s]of Object.entries(o))if(r[e]!==s)return!1;return!0},E=(r,o,e,s)=>{const n=s.document;if(!n||typeof n!="object")throw new Error("insertOne operation requires a valid document");const t=n._id||g(),i=y(o,e,t);if(r.get(i))throw new Error(`Document with _id ${t} already exists`);const u={...n,_id:t,_created_at:new Date().toISOString(),_updated_at:new Date().toISOString()};return r.put(i,JSON.stringify(u)),{inserted_id:t}},j=(r,o,e,s)=>{const{filter:n,update:t,upsert:i=!1}=s;if(!n||typeof n!="object")throw new Error("updateOne operation requires a valid filter");if(!t||typeof t!="object")throw new Error("updateOne operation requires a valid update");const c=`${o}:${e}:`;let u=0,a=0,_=null;const d=r.getRange({start:c,end:c+"\xFF"});for(const{key:f,value:w}of d){const l=JSON.parse(w);if(h(l,n)){u=1;const p=O(l,t);return p._updated_at=new Date().toISOString(),JSON.stringify(l)!==JSON.stringify(p)&&(r.put(f,JSON.stringify(p)),a=1),{matched_count:u,modified_count:a}}}if(i){const f=g(),w=y(o,e,f),l={...n,_id:f,_created_at:new Date().toISOString(),_updated_at:new Date().toISOString()},p=O(l,t);r.put(w,JSON.stringify(p)),_=f}return{matched_count:u,modified_count:a,upserted_id:_}},q=(r,o,e,s)=>{const{filter:n}=s;if(!n||typeof n!="object")throw new Error("deleteOne operation requires a valid filter");const t=`${o}:${e}:`,i=r.getRange({start:t,end:t+"\xFF"});for(const{key:c,value:u}of i){const a=JSON.parse(u);if(h(a,n))return r.remove(c),{deleted_count:1}}return{deleted_count:0}},$=async(r,o,e,s={})=>{const n=S();if(!r)throw new Error("Database name is required");if(!o)throw new Error("Collection name is required");if(!Array.isArray(e)||e.length===0)throw new Error("Operations must be a non-empty array");const t=m(),i={acknowledged:!0,inserted_count:0,matched_count:0,modified_count:0,deleted_count:0,upserted_count:0,inserted_ids:{},upserted_ids:{}};return await t.transaction(()=>{e.forEach((c,u)=>{const a=Object.keys(c)[0],_=c[a];switch(a){case"insert_one":case"insertOne":{const d=E(t,r,o,_);i.inserted_count++,i.inserted_ids[u]=d.inserted_id;break}case"update_one":case"updateOne":{const d=j(t,r,o,_);i.matched_count+=d.matched_count,i.modified_count+=d.modified_count,d.upserted_id&&(i.upserted_count++,i.upserted_ids[u]=d.upserted_id);break}case"delete_one":case"deleteOne":{const d=q(t,r,o,_);i.deleted_count+=d.deleted_count;break}default:throw new Error(`Unsupported bulk operation: ${a}`)}})}),n.info("Bulk write operation completed",{database:r,collection:o,operations_count:e.length,results:i}),i},v=async(r,o,e,s={})=>{if(!r)throw new Error("Database name is required");if(!o)throw new Error("Collection name is required");if(!Array.isArray(e)||e.length===0)throw new Error("Operations must be a non-empty array");return await b().enqueue_write_operation(()=>$(r,o,e,s),{operation:"bulk_write",database:r,collection:o,operations_count:e.length})};var J=v;export{J as default};
1
+ import{get_database as h,generate_document_id as l,build_collection_key as f}from"../query_engine.js";import{get_write_queue as k}from"../write_queue.js";import b from"../logger.js";const{create_context_logger:O}=b("bulk_write"),v=e=>{if(!e)throw new Error("Database name is required")},j=e=>{if(!e)throw new Error("Collection name is required")},$=e=>{if(!Array.isArray(e)||e.length===0)throw new Error("Operations must be a non-empty array")},m=(e,r,t)=>{v(e),j(r),$(t)},E=(e,r)=>({...e,...r}),q=(e,r)=>{const t={...e};for(const o of Object.keys(r))delete t[o];return t},x=(e,r)=>{const t={...e};for(const[o,n]of Object.entries(r))t[o]=(t[o]||0)+n;return t},S=(e,r)=>{const t={...e};for(const[o,n]of Object.entries(r))Array.isArray(t[o])||(t[o]=[]),t[o]=[...t[o],n];return t},J=(e,r)=>{const t={...e};for(const[o,n]of Object.entries(r))Array.isArray(t[o])&&(t[o]=t[o].filter(c=>c!==n));return t},y=(e,r)=>{let t={...e};for(const[o,n]of Object.entries(r))switch(o){case"$set":t=E(t,n);break;case"$unset":t=q(t,n);break;case"$inc":t=x(t,n);break;case"$push":t=S(t,n);break;case"$pull":t=J(t,n);break;default:throw new Error(`Unsupported update operator: ${o}`)}return t},N=(e,r,t)=>e[r]===t,w=(e,r)=>{if(!r||Object.keys(r).length===0)return!0;for(const[t,o]of Object.entries(r))if(!N(e,t,o))return!1;return!0},A=e=>{if(!e||typeof e!="object")throw new Error("insertOne operation requires a valid document")},_=()=>new Date().toISOString(),F=e=>{const r=e._id||l(),t=_();return{...e,_id:r,_created_at:t,_updated_at:t}},z=(e,r,t)=>{if(e.get(r))throw new Error(`Document with _id ${t} already exists`)},D=(e,r,t,o)=>{const n=o.document;A(n);const c=F(n),s=f(r,t,c._id);return z(e,s,c._id),e.put(s,JSON.stringify(c)),{inserted_id:c._id}},R=(e,r)=>{if(!e||typeof e!="object")throw new Error("updateOne operation requires a valid filter");if(!r||typeof r!="object")throw new Error("updateOne operation requires a valid update")},U=e=>({...e,_updated_at:_()}),B=(e,r)=>JSON.stringify(e)!==JSON.stringify(r),C=(e,r)=>{const t=l(),o=_(),n={...e,_id:t,_created_at:o,_updated_at:o};return y(n,r)},I=(e,r,t,o,n)=>{const c=`${r}:${t}:`,s=e.getRange({start:c,end:c+"\xFF"});for(const{key:i,value:a}of s){const d=JSON.parse(a);if(w(d,o)){const g=y(d,n),u=U(g);let p=0;return B(d,u)&&(e.put(i,JSON.stringify(u)),p=1),{matched_count:1,modified_count:p}}}return null},G=(e,r,t,o)=>{const{filter:n,update:c,upsert:s=!1}=o;R(n,c);const i=I(e,r,t,n,c);if(i)return i;if(s){const a=C(n,c),d=f(r,t,a._id);return e.put(d,JSON.stringify(a)),{matched_count:0,modified_count:0,upserted_id:a._id}}return{matched_count:0,modified_count:0}},H=e=>{if(!e||typeof e!="object")throw new Error("deleteOne operation requires a valid filter")},K=(e,r,t,o)=>{const n=`${r}:${t}:`,c=e.getRange({start:n,end:n+"\xFF"});for(const{key:s,value:i}of c){const a=JSON.parse(i);if(w(a,o))return e.remove(s),1}return 0},L=(e,r,t,o)=>{const{filter:n}=o;return H(n),{deleted_count:K(e,r,t,n)}},M=()=>({acknowledged:!0,inserted_count:0,matched_count:0,modified_count:0,deleted_count:0,upserted_count:0,inserted_ids:{},upserted_ids:{}}),P=e=>({insertOne:"insert_one",updateOne:"update_one",deleteOne:"delete_one"})[e]||e,Q=(e,r,t)=>{e.inserted_count++,e.inserted_ids[t]=r.inserted_id},T=(e,r,t)=>{e.matched_count+=r.matched_count,e.modified_count+=r.modified_count,r.upserted_id&&(e.upserted_count++,e.upserted_ids[t]=r.upserted_id)},V=(e,r)=>{e.deleted_count+=r.deleted_count},W=(e,r,t,o,n,c)=>{const s=Object.keys(o)[0],i=o[s];switch(P(s)){case"insert_one":{const d=D(e,r,t,i);Q(c,d,n);break}case"update_one":{const d=G(e,r,t,i);T(c,d,n);break}case"delete_one":{const d=L(e,r,t,i);V(c,d);break}default:throw new Error(`Unsupported bulk operation: ${s}`)}},X=(e,r,t,o,n)=>{e.info("Bulk write operation completed",{database:r,collection:t,operations_count:o,results:n})},Y=(e,r,t)=>({operation:"bulk_write",database:e,collection:r,operations_count:t}),Z=async(e,r,t,o={})=>{const n=O();m(e,r,t);const c=h(),s=M();return await c.transaction(()=>{t.forEach((i,a)=>{W(c,e,r,i,a,s)})}),X(n,e,r,t.length,s),s},ee=async(e,r,t,o={})=>{m(e,r,t);const n=k(),c=Y(e,r,t.length);return await n.enqueue_write_operation(()=>Z(e,r,t,o),c)};var ne=ee;export{ne as default};