@rebasepro/server-mongo 0.0.1-canary.4829d6e

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 (52) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +86 -0
  3. package/dist/MongoBootstrapper.d.ts +18 -0
  4. package/dist/MongoBootstrapper.d.ts.map +1 -0
  5. package/dist/auth/ensure-collections.d.ts +3 -0
  6. package/dist/auth/ensure-collections.d.ts.map +1 -0
  7. package/dist/auth/services.d.ts +156 -0
  8. package/dist/auth/services.d.ts.map +1 -0
  9. package/dist/connection.d.ts +35 -0
  10. package/dist/connection.d.ts.map +1 -0
  11. package/dist/db/MongoConditionBuilder.d.ts +64 -0
  12. package/dist/db/MongoConditionBuilder.d.ts.map +1 -0
  13. package/dist/db/MongoDataService.d.ts +101 -0
  14. package/dist/db/MongoDataService.d.ts.map +1 -0
  15. package/dist/ensure-collections-Bkx_O5CQ.js +94 -0
  16. package/dist/ensure-collections-Bkx_O5CQ.js.map +1 -0
  17. package/dist/ensure-history-collection-yajOt2dv.js +21 -0
  18. package/dist/ensure-history-collection-yajOt2dv.js.map +1 -0
  19. package/dist/factory.d.ts +151 -0
  20. package/dist/factory.d.ts.map +1 -0
  21. package/dist/history/ensure-history-collection.d.ts +3 -0
  22. package/dist/history/ensure-history-collection.d.ts.map +1 -0
  23. package/dist/index.d.ts +18 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.es.js +2508 -0
  26. package/dist/index.es.js.map +1 -0
  27. package/dist/index.umd.js +2925 -0
  28. package/dist/index.umd.js.map +1 -0
  29. package/dist/services/MongoDriver.d.ts +125 -0
  30. package/dist/services/MongoDriver.d.ts.map +1 -0
  31. package/dist/services/MongoHistoryService.d.ts +37 -0
  32. package/dist/services/MongoHistoryService.d.ts.map +1 -0
  33. package/dist/services/MongoRealtimeService.d.ts +103 -0
  34. package/dist/services/MongoRealtimeService.d.ts.map +1 -0
  35. package/dist/websocket-DQlwCHFq.js +281 -0
  36. package/dist/websocket-DQlwCHFq.js.map +1 -0
  37. package/dist/websocket.d.ts +7 -0
  38. package/dist/websocket.d.ts.map +1 -0
  39. package/package.json +81 -0
  40. package/src/MongoBootstrapper.ts +196 -0
  41. package/src/auth/ensure-collections.ts +105 -0
  42. package/src/auth/services.ts +732 -0
  43. package/src/connection.ts +60 -0
  44. package/src/db/MongoConditionBuilder.ts +224 -0
  45. package/src/db/MongoDataService.ts +368 -0
  46. package/src/factory.ts +305 -0
  47. package/src/history/ensure-history-collection.ts +22 -0
  48. package/src/index.ts +25 -0
  49. package/src/services/MongoDriver.ts +1120 -0
  50. package/src/services/MongoHistoryService.ts +181 -0
  51. package/src/services/MongoRealtimeService.ts +446 -0
  52. package/src/websocket.ts +297 -0
@@ -0,0 +1,196 @@
1
+ import { Db, MongoClient } from "mongodb";
2
+ import type {
3
+ BackendBootstrapper,
4
+ InitializedDriver,
5
+ BootstrappedAuth,
6
+ DatabaseAdmin,
7
+ HistoryConfig,
8
+ RealtimeProvider,
9
+ DataDriver,
10
+ CollectionConfig
11
+ } from "@rebasepro/types";
12
+ import { MongoDriver } from "./services/MongoDriver";
13
+ import { MongoRealtimeService } from "./services/MongoRealtimeService";
14
+ import { MongoCollectionRegistry } from "./factory";
15
+ import { MongoAuthRepository, MongoUserService, MongoRoleService } from "./auth/services";
16
+ import { logger } from "@rebasepro/server";
17
+
18
+ export interface MongoDriverConfig {
19
+ connection: Db;
20
+ client: MongoClient;
21
+ }
22
+
23
+ export interface MongoDriverInternals {
24
+ db: Db;
25
+ client: MongoClient;
26
+ registry: MongoCollectionRegistry;
27
+ realtimeService: MongoRealtimeService;
28
+ driver: MongoDriver;
29
+ }
30
+
31
+ /** Shape of the config object passed to `initializeDriver` by the coordinator. */
32
+ interface DriverInitConfig {
33
+ collections?: CollectionConfig[];
34
+ }
35
+
36
+ /** Shape of the auth config passed to `initializeAuth`. */
37
+ interface AuthInitConfig {
38
+ email?: import("@rebasepro/server").EmailConfig;
39
+ }
40
+
41
+
42
+
43
+ export function createMongoBootstrapper(mongoConfig: MongoDriverConfig): BackendBootstrapper {
44
+ // Cached admin object, set during getAdmin() and used by initializeWebsockets
45
+ let cachedAdmin: DatabaseAdmin | undefined;
46
+
47
+ return {
48
+ type: "mongodb",
49
+
50
+ async initializeDriver(config: unknown): Promise<InitializedDriver> {
51
+ const { collections } = config as DriverInitConfig;
52
+
53
+ const registry = new MongoCollectionRegistry();
54
+ if (collections) {
55
+ collections.forEach(collection => registry.register(collection));
56
+ }
57
+
58
+ const db = mongoConfig.connection;
59
+ const client = mongoConfig.client;
60
+
61
+ // Verify connection
62
+ try {
63
+ await db.command({ ping: 1 });
64
+ } catch (err) {
65
+ logger.error("❌ Failed to connect to MongoDB", { error: err });
66
+ }
67
+
68
+ const realtimeService = new MongoRealtimeService(db);
69
+ const driver = new MongoDriver(db, realtimeService, undefined, registry);
70
+
71
+ const internals: MongoDriverInternals = {
72
+ db,
73
+ client,
74
+ registry,
75
+ realtimeService,
76
+ driver
77
+ };
78
+
79
+ return {
80
+ driver,
81
+ realtimeProvider: realtimeService,
82
+ collectionRegistry: registry,
83
+ internals
84
+ };
85
+ },
86
+
87
+ async initializeAuth(config: unknown, driverResult: InitializedDriver): Promise<BootstrappedAuth | undefined> {
88
+ const internals = driverResult.internals as MongoDriverInternals;
89
+ const db = internals.db;
90
+
91
+ const { ensureAuthCollectionsExist } = await import("./auth/ensure-collections");
92
+ await ensureAuthCollectionsExist(db);
93
+
94
+ const { createEmailService } = await import("@rebasepro/server");
95
+ const authConfig = config as AuthInitConfig | undefined;
96
+ let emailService: unknown;
97
+ if (authConfig?.email) {
98
+ emailService = createEmailService(authConfig.email);
99
+ }
100
+
101
+ const userService = new MongoUserService(db);
102
+ const roleService = new MongoRoleService(db);
103
+ const authRepository = new MongoAuthRepository(db);
104
+
105
+ return {
106
+ userService,
107
+ roleService,
108
+ authRepository,
109
+ emailService
110
+ };
111
+ },
112
+
113
+ async initializeHistory(config: HistoryConfig, driverResult: InitializedDriver): Promise<{ historyService: unknown } | undefined> {
114
+ if (!config) return undefined;
115
+
116
+ const internals = driverResult.internals as MongoDriverInternals;
117
+ const db = internals.db;
118
+
119
+ const { ensureHistoryCollectionExists } = await import("./history/ensure-history-collection");
120
+ await ensureHistoryCollectionExists(db);
121
+
122
+ const { MongoHistoryService } = await import("./services/MongoHistoryService");
123
+
124
+ const retention = typeof config === "object" ? config.retention : undefined;
125
+ const historyService = new MongoHistoryService(db, retention ? { ttlDays: retention } : undefined);
126
+
127
+ return { historyService };
128
+ },
129
+
130
+ async initializeRealtime(_config: unknown, driverResult: InitializedDriver): Promise<RealtimeProvider | undefined> {
131
+ const internals = driverResult.internals as MongoDriverInternals;
132
+ return internals.realtimeService;
133
+ },
134
+
135
+ getAdmin(driverResult: InitializedDriver): DatabaseAdmin | undefined {
136
+ const internals = driverResult.internals as MongoDriverInternals;
137
+ const db = internals.db;
138
+
139
+ const admin: DatabaseAdmin = {
140
+ async executeAggregate(pipeline: Record<string, unknown>[]) {
141
+ const firstStage = pipeline[0];
142
+ const collName = (firstStage as { $from?: string })?.$from ?? "__admin__";
143
+ const cursor = db.collection(collName).aggregate(pipeline);
144
+ return await cursor.toArray() as Record<string, unknown>[];
145
+ },
146
+ async fetchCollectionStats(collectionName: string) {
147
+ const stats = await db.command({ collStats: collectionName }) as { count: number; size: number };
148
+ return { count: stats.count,
149
+ sizeBytes: stats.size };
150
+ },
151
+ async fetchUnmappedTables(mappedPaths?: string[]) {
152
+ const allCollections = await db.listCollections().toArray();
153
+ const names = allCollections.map(c => c.name).filter(n => !n.startsWith("system."));
154
+ if (!mappedPaths || mappedPaths.length === 0) return names;
155
+ const mappedSet = new Set(mappedPaths.map(p => p.toLowerCase()));
156
+ return names.filter(n => !mappedSet.has(n.toLowerCase()));
157
+ },
158
+ async fetchTableMetadata(collectionName: string) {
159
+ const sample = await db.collection(collectionName).findOne();
160
+ if (!sample) return { columns: [],
161
+ foreignKeys: [],
162
+ junctions: [],
163
+ policies: [] };
164
+ const columns = Object.entries(sample).map(([key, value]) => ({
165
+ column_name: key,
166
+ data_type: typeof value,
167
+ udt_name: typeof value,
168
+ is_nullable: "YES",
169
+ column_default: null,
170
+ character_maximum_length: null
171
+ }));
172
+ return { columns,
173
+ foreignKeys: [],
174
+ junctions: [],
175
+ policies: [] };
176
+ }
177
+ };
178
+
179
+ cachedAdmin = admin;
180
+ return admin;
181
+ },
182
+
183
+ mountRoutes() {},
184
+
185
+ async initializeWebsockets(server: unknown, realtimeService: RealtimeProvider, driver: DataDriver, config?: unknown): Promise<void> {
186
+ const { createMongoWebSocket } = await import("./websocket");
187
+ createMongoWebSocket(
188
+ server as import("http").Server,
189
+ realtimeService as MongoRealtimeService,
190
+ driver as MongoDriver,
191
+ config as Record<string, unknown> | undefined,
192
+ cachedAdmin
193
+ );
194
+ }
195
+ };
196
+ }
@@ -0,0 +1,105 @@
1
+ import { Db } from "mongodb";
2
+ import { logger } from "@rebasepro/server";
3
+
4
+ /**
5
+ * Default roles to seed on first run
6
+ */
7
+ const DEFAULT_ROLES = [
8
+ {
9
+ _id: "admin",
10
+ id: "admin",
11
+ name: "Admin",
12
+ isAdmin: true,
13
+ defaultPermissions: { read: true,
14
+ create: true,
15
+ edit: true,
16
+ delete: true },
17
+ createdAt: new Date()
18
+ },
19
+ {
20
+ _id: "editor",
21
+ id: "editor",
22
+ name: "Editor",
23
+ isAdmin: false,
24
+ defaultPermissions: { read: true,
25
+ create: true,
26
+ edit: true,
27
+ delete: true },
28
+ createdAt: new Date()
29
+ },
30
+ {
31
+ _id: "viewer",
32
+ id: "viewer",
33
+ name: "Viewer",
34
+ isAdmin: false,
35
+ defaultPermissions: { read: true,
36
+ create: false,
37
+ edit: false,
38
+ delete: false },
39
+ createdAt: new Date()
40
+ }
41
+ ];
42
+
43
+ export async function ensureAuthCollectionsExist(db: Db): Promise<void> {
44
+ logger.info("🔍 Checking MongoDB auth collections and indexes...");
45
+
46
+ try {
47
+ // Users
48
+ const users = db.collection("rebase_users");
49
+ await users.createIndex({ email: 1 }, { unique: true });
50
+
51
+ // User Identities
52
+ const identities = db.collection("rebase_user_identities");
53
+ await identities.createIndex({ provider: 1,
54
+ provider_id: 1 }, { unique: true });
55
+ await identities.createIndex({ user_id: 1 });
56
+
57
+ // User Roles (junction collection)
58
+ const userRoles = db.collection("rebase_user_roles");
59
+ await userRoles.createIndex({ user_id: 1,
60
+ role_id: 1 }, { unique: true });
61
+ await userRoles.createIndex({ user_id: 1 });
62
+
63
+ // Refresh Tokens
64
+ const refreshTokens = db.collection("rebase_refresh_tokens");
65
+ await refreshTokens.createIndex({ token_hash: 1 }, { unique: true });
66
+ await refreshTokens.createIndex({ user_id: 1,
67
+ user_agent: 1,
68
+ ip_address: 1 }, { unique: true });
69
+ await refreshTokens.createIndex({ user_id: 1 });
70
+
71
+ // Password Reset Tokens
72
+ const resetTokens = db.collection("rebase_password_reset_tokens");
73
+ await resetTokens.createIndex({ token_hash: 1 }, { unique: true });
74
+ await resetTokens.createIndex({ user_id: 1 });
75
+
76
+ // Seed roles
77
+ await seedDefaultRoles(db);
78
+
79
+ logger.info("✅ MongoDB Auth collections ready");
80
+ } catch (error) {
81
+ logger.error("❌ Failed to set up MongoDB auth collections", { error: error });
82
+ }
83
+ }
84
+
85
+ async function seedDefaultRoles(db: Db): Promise<void> {
86
+ const roles = db.collection("rebase_roles");
87
+ const count = await roles.countDocuments();
88
+
89
+ if (count > 0) {
90
+ logger.info(`📋 Found ${count} existing roles`);
91
+ return;
92
+ }
93
+
94
+ logger.info("🌱 Seeding default roles...");
95
+
96
+ for (const role of DEFAULT_ROLES) {
97
+ await roles.updateOne(
98
+ { id: role.id },
99
+ { $setOnInsert: role },
100
+ { upsert: true }
101
+ );
102
+ }
103
+
104
+ logger.info("✅ Default roles created: admin, editor, viewer");
105
+ }