@push.rocks/smartdb 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartdb",
3
- "version": "2.8.0",
3
+ "version": "2.9.0",
4
4
  "private": false,
5
5
  "description": "A MongoDB-compatible embedded database server with wire protocol support, backed by a high-performance Rust engine.",
6
6
  "exports": {
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartdb',
6
- version: '2.8.0',
6
+ version: '2.9.0',
7
7
  description: 'A MongoDB-compatible embedded database server with wire protocol support, backed by a high-performance Rust engine.'
8
8
  }
package/ts/index.ts CHANGED
@@ -22,4 +22,14 @@ export type {
22
22
  ICollectionInfo,
23
23
  IDocumentsResult,
24
24
  ISmartDbMetrics,
25
+ ISmartDbHealth,
26
+ ISmartDbDatabaseTenantInput,
27
+ ISmartDbDeleteDatabaseTenantInput,
28
+ ISmartDbRotateDatabaseTenantPasswordInput,
29
+ ISmartDbDatabaseTenantDescriptor,
30
+ ISmartDbDeleteDatabaseTenantResult,
31
+ ISmartDbDatabaseExportCollection,
32
+ ISmartDbDatabaseExport,
33
+ ISmartDbImportDatabaseInput,
34
+ ISmartDbImportDatabaseResult,
25
35
  } from './ts_smartdb/index.js';
@@ -21,4 +21,14 @@ export type {
21
21
  ICollectionInfo,
22
22
  IDocumentsResult,
23
23
  ISmartDbMetrics,
24
+ ISmartDbHealth,
25
+ ISmartDbDatabaseTenantInput,
26
+ ISmartDbDeleteDatabaseTenantInput,
27
+ ISmartDbRotateDatabaseTenantPasswordInput,
28
+ ISmartDbDatabaseTenantDescriptor,
29
+ ISmartDbDeleteDatabaseTenantResult,
30
+ ISmartDbDatabaseExportCollection,
31
+ ISmartDbDatabaseExport,
32
+ ISmartDbImportDatabaseInput,
33
+ ISmartDbImportDatabaseResult,
24
34
  } from './rust-db-bridge.js';
@@ -83,6 +83,73 @@ export interface ISmartDbMetrics {
83
83
  uptimeSeconds: number;
84
84
  }
85
85
 
86
+ export interface ISmartDbHealth {
87
+ running: boolean;
88
+ storage?: 'memory' | 'file';
89
+ storagePath?: string;
90
+ authEnabled?: boolean;
91
+ authUsers?: number;
92
+ usersPathConfigured?: boolean;
93
+ databaseCount: number;
94
+ collectionCount: number;
95
+ uptimeSeconds?: number;
96
+ }
97
+
98
+ export interface ISmartDbDatabaseTenantInput {
99
+ databaseName: string;
100
+ username: string;
101
+ password: string;
102
+ roles?: string[];
103
+ }
104
+
105
+ export interface ISmartDbDeleteDatabaseTenantInput {
106
+ databaseName: string;
107
+ username?: string;
108
+ }
109
+
110
+ export interface ISmartDbRotateDatabaseTenantPasswordInput {
111
+ username: string;
112
+ password: string;
113
+ }
114
+
115
+ export interface ISmartDbDatabaseTenantDescriptor {
116
+ databaseName: string;
117
+ username: string;
118
+ roles: string[];
119
+ authSource: string;
120
+ mongodbUri?: string;
121
+ }
122
+
123
+ export interface ISmartDbDeleteDatabaseTenantResult {
124
+ databaseName: string;
125
+ deletedUsers: number;
126
+ databaseDropped: boolean;
127
+ }
128
+
129
+ export interface ISmartDbDatabaseExportCollection {
130
+ name: string;
131
+ documents: Record<string, any>[];
132
+ indexes: Record<string, any>[];
133
+ }
134
+
135
+ export interface ISmartDbDatabaseExport {
136
+ format: 'smartdb.database.export.v1';
137
+ databaseName: string;
138
+ exportedAtMs: number;
139
+ collections: ISmartDbDatabaseExportCollection[];
140
+ }
141
+
142
+ export interface ISmartDbImportDatabaseInput {
143
+ databaseName: string;
144
+ source: ISmartDbDatabaseExport;
145
+ }
146
+
147
+ export interface ISmartDbImportDatabaseResult {
148
+ databaseName: string;
149
+ collections: number;
150
+ documents: number;
151
+ }
152
+
86
153
  /**
87
154
  * Type-safe command definitions for the RustDb IPC protocol.
88
155
  */
@@ -90,7 +157,36 @@ type TSmartDbCommands = {
90
157
  start: { params: { config: ISmartDbRustConfig }; result: { connectionUri: string } };
91
158
  stop: { params: Record<string, never>; result: void };
92
159
  getStatus: { params: Record<string, never>; result: { running: boolean } };
160
+ getHealth: { params: Record<string, never>; result: ISmartDbHealth };
93
161
  getMetrics: { params: Record<string, never>; result: ISmartDbMetrics };
162
+ createDatabaseTenant: {
163
+ params: ISmartDbDatabaseTenantInput;
164
+ result: ISmartDbDatabaseTenantDescriptor;
165
+ };
166
+ deleteDatabaseTenant: {
167
+ params: ISmartDbDeleteDatabaseTenantInput;
168
+ result: ISmartDbDeleteDatabaseTenantResult;
169
+ };
170
+ rotateDatabaseTenantPassword: {
171
+ params: ISmartDbRotateDatabaseTenantPasswordInput;
172
+ result: ISmartDbDatabaseTenantDescriptor;
173
+ };
174
+ listDatabaseTenants: {
175
+ params: Record<string, never>;
176
+ result: { tenants: ISmartDbDatabaseTenantDescriptor[] };
177
+ };
178
+ getDatabaseTenantDescriptor: {
179
+ params: { databaseName: string; username: string };
180
+ result: ISmartDbDatabaseTenantDescriptor;
181
+ };
182
+ exportDatabase: {
183
+ params: { databaseName: string };
184
+ result: ISmartDbDatabaseExport;
185
+ };
186
+ importDatabase: {
187
+ params: ISmartDbImportDatabaseInput;
188
+ result: ISmartDbImportDatabaseResult;
189
+ };
94
190
  getOpLog: {
95
191
  params: { sinceSeq?: number; limit?: number; db?: string; collection?: string };
96
192
  result: IOpLogResult;
@@ -202,7 +298,7 @@ export class RustDbBridge extends EventEmitter {
202
298
  envVarName: 'SMARTDB_RUST_BINARY',
203
299
  platformPackagePrefix: '@push.rocks/smartdb',
204
300
  localPaths: buildLocalPaths(),
205
- maxPayloadSize: 10 * 1024 * 1024, // 10 MB
301
+ maxPayloadSize: 100 * 1024 * 1024, // database exports/imports can be larger than command replies
206
302
  });
207
303
 
208
304
  // Forward events from the inner bridge
@@ -251,6 +347,48 @@ export class RustDbBridge extends EventEmitter {
251
347
  return this.bridge.sendCommand('getMetrics', {} as Record<string, never>) as Promise<ISmartDbMetrics>;
252
348
  }
253
349
 
350
+ public async getHealth(): Promise<ISmartDbHealth> {
351
+ return this.bridge.sendCommand('getHealth', {} as Record<string, never>) as Promise<ISmartDbHealth>;
352
+ }
353
+
354
+ public async createDatabaseTenant(
355
+ params: ISmartDbDatabaseTenantInput,
356
+ ): Promise<ISmartDbDatabaseTenantDescriptor> {
357
+ return this.bridge.sendCommand('createDatabaseTenant', params) as Promise<ISmartDbDatabaseTenantDescriptor>;
358
+ }
359
+
360
+ public async deleteDatabaseTenant(
361
+ params: ISmartDbDeleteDatabaseTenantInput,
362
+ ): Promise<ISmartDbDeleteDatabaseTenantResult> {
363
+ return this.bridge.sendCommand('deleteDatabaseTenant', params) as Promise<ISmartDbDeleteDatabaseTenantResult>;
364
+ }
365
+
366
+ public async rotateDatabaseTenantPassword(
367
+ params: ISmartDbRotateDatabaseTenantPasswordInput,
368
+ ): Promise<ISmartDbDatabaseTenantDescriptor> {
369
+ return this.bridge.sendCommand('rotateDatabaseTenantPassword', params) as Promise<ISmartDbDatabaseTenantDescriptor>;
370
+ }
371
+
372
+ public async listDatabaseTenants(): Promise<ISmartDbDatabaseTenantDescriptor[]> {
373
+ const result = await this.bridge.sendCommand('listDatabaseTenants', {} as Record<string, never>) as { tenants: ISmartDbDatabaseTenantDescriptor[] };
374
+ return result.tenants;
375
+ }
376
+
377
+ public async getDatabaseTenantDescriptor(params: {
378
+ databaseName: string;
379
+ username: string;
380
+ }): Promise<ISmartDbDatabaseTenantDescriptor> {
381
+ return this.bridge.sendCommand('getDatabaseTenantDescriptor', params) as Promise<ISmartDbDatabaseTenantDescriptor>;
382
+ }
383
+
384
+ public async exportDatabase(params: { databaseName: string }): Promise<ISmartDbDatabaseExport> {
385
+ return this.bridge.sendCommand('exportDatabase', params) as Promise<ISmartDbDatabaseExport>;
386
+ }
387
+
388
+ public async importDatabase(params: ISmartDbImportDatabaseInput): Promise<ISmartDbImportDatabaseResult> {
389
+ return this.bridge.sendCommand('importDatabase', params) as Promise<ISmartDbImportDatabaseResult>;
390
+ }
391
+
254
392
  public async getOpLog(params: {
255
393
  sinceSeq?: number;
256
394
  limit?: number;
@@ -8,6 +8,15 @@ import type {
8
8
  ICollectionInfo,
9
9
  IDocumentsResult,
10
10
  ISmartDbMetrics,
11
+ ISmartDbHealth,
12
+ ISmartDbDatabaseTenantInput,
13
+ ISmartDbDeleteDatabaseTenantInput,
14
+ ISmartDbRotateDatabaseTenantPasswordInput,
15
+ ISmartDbDatabaseTenantDescriptor,
16
+ ISmartDbDeleteDatabaseTenantResult,
17
+ ISmartDbDatabaseExport,
18
+ ISmartDbImportDatabaseInput,
19
+ ISmartDbImportDatabaseResult,
11
20
  } from '../rust-db-bridge.js';
12
21
 
13
22
  /**
@@ -204,6 +213,85 @@ export class SmartdbServer {
204
213
  return this.options.host ?? '127.0.0.1';
205
214
  }
206
215
 
216
+ /**
217
+ * Create an isolated database/user pair for an application tenant.
218
+ */
219
+ async createDatabaseTenant(
220
+ params: ISmartDbDatabaseTenantInput,
221
+ ): Promise<ISmartDbDatabaseTenantDescriptor> {
222
+ const descriptor = await this.bridge.createDatabaseTenant(params);
223
+ return this.withTenantMongoUri(descriptor, params.password);
224
+ }
225
+
226
+ /**
227
+ * Delete a tenant database and its tenant user(s).
228
+ */
229
+ async deleteDatabaseTenant(
230
+ params: ISmartDbDeleteDatabaseTenantInput,
231
+ ): Promise<ISmartDbDeleteDatabaseTenantResult> {
232
+ return this.bridge.deleteDatabaseTenant(params);
233
+ }
234
+
235
+ /**
236
+ * Rotate a tenant user's password without restarting the server.
237
+ */
238
+ async rotateDatabaseTenantPassword(
239
+ params: ISmartDbRotateDatabaseTenantPasswordInput,
240
+ ): Promise<ISmartDbDatabaseTenantDescriptor> {
241
+ const descriptor = await this.bridge.rotateDatabaseTenantPassword(params);
242
+ return this.withTenantMongoUri(descriptor, params.password);
243
+ }
244
+
245
+ /**
246
+ * List known database tenants.
247
+ */
248
+ async listDatabaseTenants(): Promise<ISmartDbDatabaseTenantDescriptor[]> {
249
+ return this.bridge.listDatabaseTenants();
250
+ }
251
+
252
+ /**
253
+ * Get a tenant descriptor without exposing a password.
254
+ */
255
+ async getDatabaseTenantDescriptor(params: {
256
+ databaseName: string;
257
+ username: string;
258
+ }): Promise<ISmartDbDatabaseTenantDescriptor> {
259
+ return this.bridge.getDatabaseTenantDescriptor(params);
260
+ }
261
+
262
+ /**
263
+ * Export one database as an Extended JSON snapshot.
264
+ */
265
+ async exportDatabase(params: { databaseName: string }): Promise<ISmartDbDatabaseExport> {
266
+ return this.bridge.exportDatabase(params);
267
+ }
268
+
269
+ /**
270
+ * Replace one database with a previously exported snapshot.
271
+ */
272
+ async importDatabase(params: ISmartDbImportDatabaseInput): Promise<ISmartDbImportDatabaseResult> {
273
+ return this.bridge.importDatabase(params);
274
+ }
275
+
276
+ /**
277
+ * Get readiness/health details for long-running service use.
278
+ */
279
+ async getHealth(): Promise<ISmartDbHealth> {
280
+ if (!this.isRunning) {
281
+ return {
282
+ running: false,
283
+ storage: this.options.storage,
284
+ storagePath: this.options.storage === 'file' ? this.options.storagePath : this.options.persistPath,
285
+ authEnabled: Boolean(this.options.auth?.enabled),
286
+ authUsers: this.options.auth?.users?.length ?? 0,
287
+ usersPathConfigured: Boolean(this.options.auth?.usersPath),
288
+ databaseCount: 0,
289
+ collectionCount: 0,
290
+ };
291
+ }
292
+ return this.bridge.getHealth();
293
+ }
294
+
207
295
  // --- OpLog / Debug API ---
208
296
 
209
297
  /**
@@ -258,4 +346,26 @@ export class SmartdbServer {
258
346
  async getMetrics(): Promise<ISmartDbMetrics> {
259
347
  return this.bridge.getMetrics();
260
348
  }
349
+
350
+ private withTenantMongoUri(
351
+ descriptor: ISmartDbDatabaseTenantDescriptor,
352
+ password: string,
353
+ ): ISmartDbDatabaseTenantDescriptor {
354
+ return {
355
+ ...descriptor,
356
+ mongodbUri: this.buildTenantMongoUri(descriptor.databaseName, descriptor.username, password),
357
+ };
358
+ }
359
+
360
+ private buildTenantMongoUri(databaseName: string, username: string, password: string): string {
361
+ const host = this.options.socketPath
362
+ ? encodeURIComponent(this.options.socketPath)
363
+ : `${this.options.host ?? '127.0.0.1'}:${this.options.port ?? 27017}`;
364
+ const auth = `${encodeURIComponent(username)}:${encodeURIComponent(password)}@`;
365
+ const query = new URLSearchParams({ authSource: databaseName });
366
+ if (this.options.tls?.enabled) {
367
+ query.set('tls', 'true');
368
+ }
369
+ return `mongodb://${auth}${host}/${encodeURIComponent(databaseName)}?${query.toString()}`;
370
+ }
261
371
  }