@lenne.tech/nest-server 11.2.0 → 11.4.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.
Files changed (73) hide show
  1. package/bin/migrate.js +40 -0
  2. package/dist/core/common/decorators/unified-field.decorator.d.ts +2 -0
  3. package/dist/core/common/decorators/unified-field.decorator.js +26 -9
  4. package/dist/core/common/decorators/unified-field.decorator.js.map +1 -1
  5. package/dist/core/common/models/core-persistence.model.js +2 -2
  6. package/dist/core/common/models/core-persistence.model.js.map +1 -1
  7. package/dist/core/modules/file/core-file-info.model.js +41 -23
  8. package/dist/core/modules/file/core-file-info.model.js.map +1 -1
  9. package/dist/core/modules/migrate/cli/migrate-cli.d.ts +3 -0
  10. package/dist/core/modules/migrate/cli/migrate-cli.js +221 -0
  11. package/dist/core/modules/migrate/cli/migrate-cli.js.map +1 -0
  12. package/dist/core/modules/migrate/helpers/migration.helper.d.ts +12 -0
  13. package/dist/core/modules/migrate/helpers/migration.helper.js +57 -0
  14. package/dist/core/modules/migrate/helpers/migration.helper.js.map +1 -0
  15. package/dist/core/modules/migrate/helpers/ts-compiler.d.ts +2 -0
  16. package/dist/core/modules/migrate/helpers/ts-compiler.js +3 -0
  17. package/dist/core/modules/migrate/helpers/ts-compiler.js.map +1 -0
  18. package/dist/core/modules/migrate/index.d.ts +4 -0
  19. package/dist/core/modules/migrate/index.js +21 -0
  20. package/dist/core/modules/migrate/index.js.map +1 -0
  21. package/dist/core/modules/migrate/migration-runner.d.ts +26 -0
  22. package/dist/core/modules/migrate/migration-runner.js +124 -0
  23. package/dist/core/modules/migrate/migration-runner.js.map +1 -0
  24. package/dist/core/modules/migrate/mongo-state-store.d.ts +30 -0
  25. package/dist/core/modules/migrate/mongo-state-store.js +105 -0
  26. package/dist/core/modules/migrate/mongo-state-store.js.map +1 -0
  27. package/dist/core/modules/migrate/templates/migration-with-helper.template.d.ts +2 -0
  28. package/dist/core/modules/migrate/templates/migration-with-helper.template.js +10 -0
  29. package/dist/core/modules/migrate/templates/migration-with-helper.template.js.map +1 -0
  30. package/dist/core/modules/migrate/templates/migration.template.d.ts +2 -0
  31. package/dist/core/modules/migrate/templates/migration.template.js +15 -0
  32. package/dist/core/modules/migrate/templates/migration.template.js.map +1 -0
  33. package/dist/core/modules/user/core-user.model.js +95 -54
  34. package/dist/core/modules/user/core-user.model.js.map +1 -1
  35. package/dist/index.d.ts +1 -0
  36. package/dist/index.js +1 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/main.js +22 -0
  39. package/dist/main.js.map +1 -1
  40. package/dist/server/common/models/persistence.model.js +13 -11
  41. package/dist/server/common/models/persistence.model.js.map +1 -1
  42. package/dist/server/modules/auth/auth.model.js +6 -2
  43. package/dist/server/modules/auth/auth.model.js.map +1 -1
  44. package/dist/server/modules/user/user.controller.d.ts +19 -0
  45. package/dist/server/modules/user/user.controller.js +256 -0
  46. package/dist/server/modules/user/user.controller.js.map +1 -0
  47. package/dist/server/modules/user/user.model.js +37 -24
  48. package/dist/server/modules/user/user.model.js.map +1 -1
  49. package/dist/server/modules/user/user.module.js +2 -1
  50. package/dist/server/modules/user/user.module.js.map +1 -1
  51. package/dist/tsconfig.build.tsbuildinfo +1 -1
  52. package/package.json +33 -29
  53. package/src/core/common/decorators/unified-field.decorator.ts +49 -10
  54. package/src/core/common/models/core-persistence.model.ts +3 -3
  55. package/src/core/modules/file/core-file-info.model.ts +40 -22
  56. package/src/core/modules/migrate/MIGRATION_FROM_NODEPIT.md +219 -0
  57. package/src/core/modules/migrate/README.md +452 -0
  58. package/src/core/modules/migrate/cli/migrate-cli.ts +319 -0
  59. package/src/core/modules/migrate/helpers/migration.helper.ts +117 -0
  60. package/src/core/modules/migrate/helpers/ts-compiler.js +14 -0
  61. package/src/core/modules/migrate/index.ts +41 -0
  62. package/src/core/modules/migrate/migration-runner.ts +230 -0
  63. package/src/core/modules/migrate/mongo-state-store.ts +283 -0
  64. package/src/core/modules/migrate/templates/migration-with-helper.template.ts +72 -0
  65. package/src/core/modules/migrate/templates/migration.template.ts +59 -0
  66. package/src/core/modules/user/core-user.model.ts +120 -78
  67. package/src/index.ts +9 -3
  68. package/src/main.ts +25 -0
  69. package/src/server/common/models/persistence.model.ts +15 -13
  70. package/src/server/modules/auth/auth.model.ts +7 -3
  71. package/src/server/modules/user/user.controller.ts +242 -0
  72. package/src/server/modules/user/user.model.ts +39 -26
  73. package/src/server/modules/user/user.module.ts +2 -1
@@ -0,0 +1,230 @@
1
+ /* eslint-disable no-console */
2
+ // Console output is required for migration runner feedback
3
+
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+
7
+ import { MongoStateStore } from './mongo-state-store';
8
+
9
+ /**
10
+ * Migration file interface
11
+ */
12
+ export interface MigrationFile {
13
+ /** Down function */
14
+ down?: () => Promise<void>;
15
+ /** File path */
16
+ filePath: string;
17
+ /** Timestamp when migration was created */
18
+ timestamp: number;
19
+ /** Migration name/title */
20
+ title: string;
21
+ /** Up function */
22
+ up: () => Promise<void>;
23
+ }
24
+
25
+ /**
26
+ * Migration runner configuration
27
+ */
28
+ export interface MigrationRunnerOptions {
29
+ /** Directory containing migration files */
30
+ migrationsDirectory: string;
31
+ /** Pattern to match migration files (default: *.ts, *.js) */
32
+ pattern?: RegExp;
33
+ /** State store for tracking migrations */
34
+ stateStore: MongoStateStore;
35
+ }
36
+
37
+ /**
38
+ * Simple migration runner for NestJS applications
39
+ *
40
+ * This provides a programmatic way to run migrations without requiring the `migrate` CLI.
41
+ * It's a lightweight alternative for projects that want to run migrations from code.
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * import { MigrationRunner, MongoStateStore } from '@lenne.tech/nest-server';
46
+ *
47
+ * const runner = new MigrationRunner({
48
+ * stateStore: new MongoStateStore('mongodb://localhost/mydb'),
49
+ * migrationsDirectory: './migrations'
50
+ * });
51
+ *
52
+ * // Run all pending migrations
53
+ * await runner.up();
54
+ *
55
+ * // Rollback last migration
56
+ * await runner.down();
57
+ * ```
58
+ */
59
+ export class MigrationRunner {
60
+ private options: MigrationRunnerOptions;
61
+ private pattern: RegExp;
62
+
63
+ constructor(options: MigrationRunnerOptions) {
64
+ this.options = options;
65
+ this.pattern = options.pattern || /\.(ts|js)$/;
66
+ }
67
+
68
+ /**
69
+ * Load all migration files from the migrations directory
70
+ */
71
+ private async loadMigrationFiles(): Promise<MigrationFile[]> {
72
+ const files = fs
73
+ .readdirSync(this.options.migrationsDirectory)
74
+ .filter((file) => this.pattern.test(file))
75
+ .sort(); // Sort alphabetically (timestamp-based filenames will be in order)
76
+
77
+ const migrations: MigrationFile[] = [];
78
+
79
+ for (const file of files) {
80
+ const filePath = path.join(this.options.migrationsDirectory, file);
81
+
82
+ const module = require(filePath);
83
+
84
+ if (!module.up) {
85
+ console.warn(`Migration ${file} has no 'up' function, skipping...`);
86
+ continue;
87
+ }
88
+
89
+ // Extract timestamp from filename (format: TIMESTAMP-name.js)
90
+ const timestampMatch = file.match(/^(\d+)-/);
91
+ const timestamp = timestampMatch ? parseInt(timestampMatch[1], 10) : Date.now();
92
+
93
+ migrations.push({
94
+ down: module.down,
95
+ filePath,
96
+ timestamp,
97
+ title: file,
98
+ up: module.up,
99
+ });
100
+ }
101
+
102
+ return migrations;
103
+ }
104
+
105
+ /**
106
+ * Run all pending migrations (up)
107
+ */
108
+ async up(): Promise<void> {
109
+ const allMigrations = await this.loadMigrationFiles();
110
+ const state = await this.options.stateStore.loadAsync();
111
+ const completedMigrations = (state.migrations || []).map((m) => m.title);
112
+
113
+ const pendingMigrations = allMigrations.filter((m) => !completedMigrations.includes(m.title));
114
+
115
+ if (pendingMigrations.length === 0) {
116
+ console.log('No pending migrations');
117
+ return;
118
+ }
119
+
120
+ console.log(`Running ${pendingMigrations.length} pending migration(s)...`);
121
+
122
+ for (const migration of pendingMigrations) {
123
+ console.log(`Running migration: ${migration.title}`);
124
+ await migration.up();
125
+
126
+ // Update state
127
+ const newState = await this.options.stateStore.loadAsync();
128
+ const migrations = newState.migrations || [];
129
+ migrations.push({
130
+ timestamp: migration.timestamp,
131
+ title: migration.title,
132
+ });
133
+
134
+ await this.options.stateStore.saveAsync({
135
+ lastRun: migration.title,
136
+ migrations,
137
+ up: () => {},
138
+ } as any);
139
+
140
+ console.log(`✓ Migration completed: ${migration.title}`);
141
+ }
142
+
143
+ console.log('All migrations completed successfully');
144
+ }
145
+
146
+ /**
147
+ * Rollback the last migration (down)
148
+ */
149
+ async down(): Promise<void> {
150
+ const state = await this.options.stateStore.loadAsync();
151
+ const completedMigrations = state.migrations || [];
152
+
153
+ if (completedMigrations.length === 0) {
154
+ console.log('No migrations to rollback');
155
+ return;
156
+ }
157
+
158
+ const lastMigration = completedMigrations[completedMigrations.length - 1];
159
+ const allMigrations = await this.loadMigrationFiles();
160
+ const migrationToRollback = allMigrations.find((m) => m.title === lastMigration.title);
161
+
162
+ if (!migrationToRollback) {
163
+ throw new Error(`Migration file not found: ${lastMigration.title}`);
164
+ }
165
+
166
+ if (!migrationToRollback.down) {
167
+ throw new Error(`Migration ${lastMigration.title} has no 'down' function`);
168
+ }
169
+
170
+ console.log(`Rolling back migration: ${migrationToRollback.title}`);
171
+ await migrationToRollback.down();
172
+
173
+ // Update state
174
+ const newMigrations = completedMigrations.slice(0, -1);
175
+ await this.options.stateStore.saveAsync({
176
+ lastRun: newMigrations.length > 0 ? newMigrations[newMigrations.length - 1].title : undefined,
177
+ migrations: newMigrations,
178
+ up: () => {},
179
+ } as any);
180
+
181
+ console.log(`✓ Migration rolled back: ${migrationToRollback.title}`);
182
+ }
183
+
184
+ /**
185
+ * Get migration status
186
+ */
187
+ async status(): Promise<{
188
+ completed: string[];
189
+ pending: string[];
190
+ }> {
191
+ const allMigrations = await this.loadMigrationFiles();
192
+ const state = await this.options.stateStore.loadAsync();
193
+ const completedMigrations = (state.migrations || []).map((m) => m.title);
194
+
195
+ return {
196
+ completed: completedMigrations,
197
+ pending: allMigrations.filter((m) => !completedMigrations.includes(m.title)).map((m) => m.title),
198
+ };
199
+ }
200
+
201
+ /**
202
+ * Create a new migration file
203
+ */
204
+ static async create(migrationsDirectory: string, name: string): Promise<string> {
205
+ const timestamp = Date.now();
206
+ const fileName = `${timestamp}-${name}.ts`;
207
+ const filePath = path.join(migrationsDirectory, fileName);
208
+
209
+ const template = `/**
210
+ * Migration: ${name}
211
+ * Created: ${new Date().toISOString()}
212
+ */
213
+
214
+ export const up = async () => {
215
+ // TODO: Implement migration
216
+ console.log('Running migration: ${name}');
217
+ };
218
+
219
+ export const down = async () => {
220
+ // TODO: Implement rollback
221
+ console.log('Rolling back migration: ${name}');
222
+ };
223
+ `;
224
+
225
+ fs.writeFileSync(filePath, template, 'utf-8');
226
+ console.log(`✓ Created migration: ${fileName}`);
227
+
228
+ return filePath;
229
+ }
230
+ }
@@ -0,0 +1,283 @@
1
+ import { Db, MongoClient } from 'mongodb';
2
+ import { promisify } from 'util';
3
+
4
+ /**
5
+ * Migration options interface (compatible with migrate package)
6
+ */
7
+ export interface MigrationOptions {
8
+ [key: string]: unknown;
9
+ stateStore: MongoStateStore;
10
+ }
11
+
12
+ /**
13
+ * Migration set interface (compatible with migrate package)
14
+ */
15
+ export interface MigrationSet {
16
+ down?: (done?: (err?: Error) => void) => void;
17
+ lastRun?: string;
18
+ migrations: Array<{ timestamp?: number; title: string }>;
19
+ up: (done?: (err?: Error) => void) => void;
20
+ }
21
+
22
+ /**
23
+ * Options for MongoStateStore configuration
24
+ */
25
+ export interface MongoStateStoreOptions {
26
+ /** Name of the collection to store migration state (default: 'migrations') */
27
+ collectionName?: string;
28
+ /**
29
+ * Optionally specify a collection to use for locking. This is intended for
30
+ * clusters with multiple nodes to ensure that not more than one migration
31
+ * can run at any given time. You must use the `synchronizedMigration` or
32
+ * `synchronizedUp` function, instead of triggering the migration via
33
+ * `migrate` directly.
34
+ */
35
+ lockCollectionName?: string;
36
+ /** MongoDB connection URI */
37
+ uri: string;
38
+ }
39
+
40
+ /**
41
+ * MongoDB State Store for migration state management
42
+ *
43
+ * This class provides a MongoDB-based state store for migration frameworks,
44
+ * allowing migration states to be persisted directly in MongoDB instead of
45
+ * in separate files. It supports MongoDB 6+ and provides a locking mechanism
46
+ * for clustered environments.
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * const stateStore = new MongoStateStore('mongodb://localhost/mydb');
51
+ * // or with options
52
+ * const stateStore = new MongoStateStore({
53
+ * uri: 'mongodb://localhost/mydb',
54
+ * collectionName: 'custom_migrations',
55
+ * lockCollectionName: 'migration_lock'
56
+ * });
57
+ * ```
58
+ */
59
+ export class MongoStateStore {
60
+ /** Collection name for storing migration state */
61
+ private readonly collectionName: string;
62
+
63
+ /** MongoDB connection URI */
64
+ readonly mongodbHost: string;
65
+
66
+ /** Optional collection name for locking mechanism */
67
+ readonly lockCollectionName?: string;
68
+
69
+ /**
70
+ * Creates a new MongoStateStore instance
71
+ *
72
+ * @param objectOrHost - MongoDB URI string or configuration object
73
+ */
74
+ constructor(objectOrHost: MongoStateStoreOptions | string) {
75
+ this.mongodbHost = typeof objectOrHost === 'string' ? objectOrHost : objectOrHost.uri;
76
+ this.collectionName =
77
+ typeof objectOrHost === 'string' ? 'migrations' : (objectOrHost.collectionName ?? 'migrations');
78
+ this.lockCollectionName = typeof objectOrHost !== 'string' ? objectOrHost.lockCollectionName : undefined;
79
+ }
80
+
81
+ /**
82
+ * Loads the migration state from MongoDB
83
+ *
84
+ * @param fn - Callback function receiving error or migration set
85
+ */
86
+ load(fn: (err?: Error, set?: MigrationSet) => void): void {
87
+ this.loadAsync()
88
+ .then((result) => fn(undefined, result))
89
+ .catch((err) => fn(err));
90
+ }
91
+
92
+ /**
93
+ * Loads the migration state from MongoDB (async version)
94
+ *
95
+ * @returns Promise with migration set
96
+ */
97
+ async loadAsync(): Promise<MigrationSet> {
98
+ return dbRequest(this.mongodbHost, async (db) => {
99
+ const result = await db.collection(this.collectionName).find({}).toArray();
100
+
101
+ if (result.length > 1) {
102
+ throw new Error(`Expected exactly one result, but got ${result.length}`);
103
+ }
104
+
105
+ if (result.length === 0) {
106
+ console.debug('No migrations found, probably running the very first time');
107
+ // Return empty object for compatibility with @nodepit/migrate-state-store-mongodb
108
+ return {} as MigrationSet;
109
+ }
110
+
111
+ return result[0] as unknown as MigrationSet;
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Saves the migration state to MongoDB
117
+ *
118
+ * @param set - Migration set to save
119
+ * @param fn - Callback function receiving optional error
120
+ */
121
+ save(set: MigrationSet, fn: (err?: Error) => void): void {
122
+ this.saveAsync(set)
123
+ .then(() => fn())
124
+ .catch((err) => fn(err));
125
+ }
126
+
127
+ /**
128
+ * Saves the migration state to MongoDB (async version)
129
+ *
130
+ * @param set - Migration set to save
131
+ */
132
+ async saveAsync(set: MigrationSet): Promise<void> {
133
+ const { lastRun, migrations } = set;
134
+ await dbRequest(this.mongodbHost, async (db) => {
135
+ await db.collection(this.collectionName).replaceOne({}, { lastRun, migrations }, { upsert: true });
136
+ });
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Wraps migrations with a lock to prevent simultaneous execution in clustered environments
142
+ *
143
+ * This function ensures that only one instance can run migrations at a time by using
144
+ * a MongoDB-based locking mechanism. To use this functionality, you must set the
145
+ * `lockCollectionName` in the `MongoStateStore` options.
146
+ *
147
+ * @param opts - Migration options including state store
148
+ * @param callback - Callback function that receives the migration set
149
+ * @throws Error if state store is not configured correctly
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * await synchronizedMigration({
154
+ * stateStore: new MongoStateStore({
155
+ * uri: 'mongodb://localhost/db',
156
+ * lockCollectionName: 'migrationlock'
157
+ * })
158
+ * }, async (migrationSet) => {
159
+ * // Only one instance at a time will execute this
160
+ * await promisify(migrationSet.up).call(migrationSet);
161
+ * });
162
+ * ```
163
+ */
164
+ export async function synchronizedMigration(
165
+ opts: MigrationOptions,
166
+ callback: (set: MigrationSet) => Promise<void>,
167
+ ): Promise<void> {
168
+ if (!opts.stateStore) {
169
+ throw new Error('No `stateStore` in migration options');
170
+ }
171
+
172
+ const stateStore = opts.stateStore;
173
+
174
+ if (!(stateStore instanceof MongoStateStore)) {
175
+ throw new Error('Given `stateStore` is not `MongoStateStore`');
176
+ }
177
+
178
+ const lockCollectionName = stateStore.lockCollectionName;
179
+
180
+ if (typeof lockCollectionName !== 'string') {
181
+ throw new Error('`lockCollectionName` in MongoStateStore is not set');
182
+ }
183
+
184
+ try {
185
+ await acquireLock(stateStore.mongodbHost, lockCollectionName);
186
+
187
+ // Load migration set using async method
188
+ const set = await stateStore.loadAsync();
189
+ await callback(set);
190
+ } finally {
191
+ await releaseLock(stateStore.mongodbHost, lockCollectionName);
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Executes all pending migrations in a synchronized manner (for clustered environments)
197
+ *
198
+ * This is a convenience function that wraps `synchronizedMigration` and automatically
199
+ * calls the `up` method on the migration set.
200
+ *
201
+ * @param opts - Migration options including state store
202
+ * @throws Error if state store is not configured correctly
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * await synchronizedUp({
207
+ * stateStore: new MongoStateStore({
208
+ * uri: 'mongodb://localhost/db',
209
+ * lockCollectionName: 'migrationlock'
210
+ * })
211
+ * });
212
+ * ```
213
+ */
214
+ export async function synchronizedUp(opts: MigrationOptions): Promise<void> {
215
+ await synchronizedMigration(opts, async (loadedSet) => {
216
+ await promisify(loadedSet.up).call(loadedSet);
217
+ });
218
+ }
219
+
220
+ /**
221
+ * Acquires a lock in MongoDB to ensure only one migration runs at a time
222
+ *
223
+ * @param url - MongoDB connection URI
224
+ * @param lockCollectionName - Name of the collection to use for locking
225
+ */
226
+ async function acquireLock(url: string, lockCollectionName: string): Promise<void> {
227
+ await dbRequest(url, async (db) => {
228
+ const collection = db.collection(lockCollectionName);
229
+
230
+ // Create unique index for atomicity
231
+ // https://docs.mongodb.com/manual/reference/method/db.collection.update/#use-unique-indexes
232
+ // https://groups.google.com/forum/#!topic/mongodb-user/-fucdS-7kIU
233
+ // https://stackoverflow.com/questions/33346175/mongodb-upsert-operation-seems-not-atomic-which-throws-duplicatekeyexception/34784533
234
+ await collection.createIndex({ lock: 1 }, { unique: true });
235
+
236
+ let showMessage = true;
237
+
238
+ for (;;) {
239
+ // Use updateOne with upsert for atomic lock acquisition (same as original package)
240
+ const result = await collection.updateOne({ lock: 'lock' }, { $set: { lock: 'lock' } }, { upsert: true });
241
+ const lockAcquired = result.upsertedCount > 0;
242
+
243
+ if (lockAcquired) {
244
+ break;
245
+ }
246
+
247
+ if (showMessage) {
248
+ console.debug('Waiting for migration lock release …');
249
+ showMessage = false;
250
+ }
251
+
252
+ await promisify(setTimeout)(100);
253
+ }
254
+ });
255
+ }
256
+
257
+ /**
258
+ * Executes database operations with automatic connection management
259
+ *
260
+ * @param url - MongoDB connection URI
261
+ * @param callback - Callback function to execute with database instance
262
+ * @returns Promise with callback result
263
+ */
264
+ async function dbRequest<T>(url: string, callback: (db: Db) => Promise<T> | T): Promise<T> {
265
+ let client: MongoClient | undefined;
266
+ try {
267
+ client = await MongoClient.connect(url);
268
+ const db = client.db();
269
+ return await callback(db);
270
+ } finally {
271
+ await client?.close();
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Releases a migration lock in MongoDB
277
+ *
278
+ * @param url - MongoDB connection URI
279
+ * @param lockCollectionName - Name of the collection used for locking
280
+ */
281
+ async function releaseLock(url: string, lockCollectionName: string): Promise<void> {
282
+ await dbRequest(url, (db) => db.collection(lockCollectionName).deleteOne({ lock: 'lock' }));
283
+ }
@@ -0,0 +1,72 @@
1
+ // import { Db } from 'mongodb';
2
+
3
+ /**
4
+ * Migration template with helper function
5
+ *
6
+ * This template uses the migration helper from @lenne.tech/nest-server.
7
+ * To use this template, you need to create a helper function in your project
8
+ * that returns the database connection.
9
+ *
10
+ * Example setup in your project's migrations-utils/db.ts:
11
+ * ```typescript
12
+ * import config from '../src/config.env';
13
+ * import { MongoClient } from 'mongodb';
14
+ *
15
+ * export const getDb = async () => {
16
+ * const client = await MongoClient.connect(config.mongoose.uri);
17
+ * return client.db();
18
+ * };
19
+ * ```
20
+ */
21
+
22
+ // Import your project's database helper
23
+ // import { getDb } from '../migrations-utils/db';
24
+
25
+ // Or use the nest-server helper with your config:
26
+ // import config from '../src/config.env';
27
+ // import { getDb } from '@lenne.tech/nest-server';
28
+ // const db = await getDb(config.mongoose.uri);
29
+
30
+ /**
31
+ * Run migration
32
+ */
33
+ export const up = async () => {
34
+ // const db: Db = await getDb();
35
+ /*
36
+ Code your update script here!
37
+
38
+ Example: Add a new field to all documents
39
+ await db.collection('users').updateMany(
40
+ { email: { $exists: false } },
41
+ { $set: { email: '' } }
42
+ );
43
+
44
+ Example: Create a new collection
45
+ await db.createCollection('new_collection');
46
+
47
+ Example: Create an index
48
+ await db.collection('users').createIndex({ email: 1 }, { unique: true });
49
+ */
50
+ };
51
+
52
+ /**
53
+ * Rollback migration
54
+ */
55
+ export const down = async () => {
56
+ // const db: Db = await getDb();
57
+ /*
58
+ Code your downgrade script here!
59
+
60
+ Example: Remove the field added in up()
61
+ await db.collection('users').updateMany(
62
+ {},
63
+ { $unset: { email: '' } }
64
+ );
65
+
66
+ Example: Drop the collection
67
+ await db.dropCollection('new_collection');
68
+
69
+ Example: Drop the index
70
+ await db.collection('users').dropIndex('email_1');
71
+ */
72
+ };
@@ -0,0 +1,59 @@
1
+ import { Db } from 'mongodb';
2
+
3
+ /**
4
+ * Migration template for nest-server
5
+ *
6
+ * This template can be used with the migrate CLI:
7
+ * migrate create --template-file ./node_modules/@lenne.tech/nest-server/dist/core/modules/migrate/templates/migration.template.js
8
+ *
9
+ * Or copy this file to your project's migrations-utils folder and customize it.
10
+ */
11
+
12
+ /**
13
+ * Get database connection
14
+ *
15
+ * IMPORTANT: Replace this function with your actual database connection logic.
16
+ * This is a placeholder that should import your project's config.
17
+ */
18
+ const getDb = async (): Promise<Db> => {
19
+ // TODO: Import your config and return the database connection
20
+ // Example:
21
+ // import config from '../src/config.env';
22
+ // const { MongoClient } = require('mongodb');
23
+ // const client = await MongoClient.connect(config.mongoose.uri);
24
+ // return client.db();
25
+
26
+ throw new Error(
27
+ 'Please configure the getDb() function in this migration file or use the migration helper from @lenne.tech/nest-server',
28
+ );
29
+ };
30
+
31
+ /**
32
+ * Run migration
33
+ *
34
+ * Code your update script here!
35
+ */
36
+ export const up = async () => {
37
+ const db: Db = await getDb();
38
+
39
+ // Example: Add a new field to all documents in a collection
40
+ // await db.collection('users').updateMany(
41
+ // { email: { $exists: false } },
42
+ // { $set: { email: '' } }
43
+ // );
44
+ };
45
+
46
+ /**
47
+ * Rollback migration
48
+ *
49
+ * Code your downgrade script here!
50
+ */
51
+ export const down = async () => {
52
+ const db: Db = await getDb();
53
+
54
+ // Example: Remove the field added in the up() function
55
+ // await db.collection('users').updateMany(
56
+ // {},
57
+ // { $unset: { email: '' } }
58
+ // );
59
+ };