@aneuhold/be-ts-db-lib 4.2.18 → 4.2.19
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/CHANGELOG.md +11 -0
- package/lib/repositories/common/UserRepository.d.ts +6 -0
- package/lib/repositories/common/UserRepository.d.ts.map +1 -1
- package/lib/repositories/common/UserRepository.js +12 -0
- package/lib/repositories/common/UserRepository.js.map +1 -1
- package/lib/repositories/common/UserRepository.ts +13 -0
- package/lib/services/MigrationService.d.ts.map +1 -1
- package/lib/services/MigrationService.js +17 -352
- package/lib/services/MigrationService.js.map +1 -1
- package/lib/services/MigrationService.ts +18 -425
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 🔖 [4.2.19] (2026-03-18)
|
|
9
|
+
|
|
10
|
+
### ✅ Added
|
|
11
|
+
|
|
12
|
+
- Added `UserRepository.getUserByRefreshTokenHash()` to look up a user by a SHA-256 hashed refresh token stored in `auth.refreshTokenHashes`.
|
|
13
|
+
|
|
14
|
+
### 🏗️ Changed
|
|
15
|
+
|
|
16
|
+
- `MigrationService` updated to backfill `auth.refreshTokenHashes` for users missing the field; removed the previous large ObjectId-to-UUID migration logic.
|
|
17
|
+
|
|
8
18
|
## 🔖 [4.2.18] (2026-03-15)
|
|
9
19
|
|
|
10
20
|
### ✅ Added
|
|
@@ -338,6 +348,7 @@ Updated dependencies: now requires `@aneuhold/core-ts-db-lib@^3.0.0`, `@aneuhold
|
|
|
338
348
|
|
|
339
349
|
<!-- Link References -->
|
|
340
350
|
|
|
351
|
+
[4.2.19]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v4.2.18...be-ts-db-lib-v4.2.19
|
|
341
352
|
[4.2.18]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v4.2.17...be-ts-db-lib-v4.2.18
|
|
342
353
|
[4.2.17]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v4.2.16...be-ts-db-lib-v4.2.17
|
|
343
354
|
[4.2.16]: https://github.com/aneuhold/ts-libs/compare/be-ts-db-lib-v4.2.15...be-ts-db-lib-v4.2.16
|
|
@@ -14,6 +14,12 @@ export default class UserRepository extends BaseRepository<User> {
|
|
|
14
14
|
*/
|
|
15
15
|
static getRepo(): UserRepository;
|
|
16
16
|
getUserCTOByUsername(userName: string): Promise<UserCTO | null>;
|
|
17
|
+
/**
|
|
18
|
+
* Finds a user that has a refresh token with the given SHA-256 hash.
|
|
19
|
+
*
|
|
20
|
+
* @param tokenHash - The SHA-256 hash of the refresh token.
|
|
21
|
+
*/
|
|
22
|
+
getUserByRefreshTokenHash(tokenHash: string): Promise<User | null>;
|
|
17
23
|
getUserCTOsByIds(userIds: UUID[]): Promise<UserCTO[]>;
|
|
18
24
|
}
|
|
19
25
|
//# sourceMappingURL=UserRepository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserRepository.d.ts","sourceRoot":"","sources":["../../../src/repositories/common/UserRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAalD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,cAAc,CAAC,IAAI,CAAC;IAC9D,OAAO,CAAC,MAAM,CAAC,eAAe,CAAW;IAEzC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAA6B;IAE7D,OAAO;IAIP,SAAS,CAAC,gBAAgB,IAAI,IAAI;IAclC;;OAEG;IACH,MAAM,CAAC,OAAO,IAAI,cAAc;IAO1B,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"UserRepository.d.ts","sourceRoot":"","sources":["../../../src/repositories/common/UserRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAalD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,cAAc,CAAC,IAAI,CAAC;IAC9D,OAAO,CAAC,MAAM,CAAC,eAAe,CAAW;IAEzC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAA6B;IAE7D,OAAO;IAIP,SAAS,CAAC,gBAAgB,IAAI,IAAI;IAclC;;OAEG;IACH,MAAM,CAAC,OAAO,IAAI,cAAc;IAO1B,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAWrE;;;;OAIG;IACG,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAQlE,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;CAO5D"}
|
|
@@ -52,6 +52,18 @@ export default class UserRepository extends BaseRepository {
|
|
|
52
52
|
}
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Finds a user that has a refresh token with the given SHA-256 hash.
|
|
57
|
+
*
|
|
58
|
+
* @param tokenHash - The SHA-256 hash of the refresh token.
|
|
59
|
+
*/
|
|
60
|
+
async getUserByRefreshTokenHash(tokenHash) {
|
|
61
|
+
const collection = await this.getCollection();
|
|
62
|
+
const result = await collection.findOne({
|
|
63
|
+
'auth.refreshTokenHashes.tokenHash': tokenHash
|
|
64
|
+
});
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
55
67
|
async getUserCTOsByIds(userIds) {
|
|
56
68
|
const users = await this.getList(userIds);
|
|
57
69
|
return users.map((user) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserRepository.js","sourceRoot":"","sources":["../../../src/repositories/common/UserRepository.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UserRepository.js","sourceRoot":"","sources":["../../../src/repositories/common/UserRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -61,6 +61,19 @@ export default class UserRepository extends BaseRepository<User> {
|
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Finds a user that has a refresh token with the given SHA-256 hash.
|
|
66
|
+
*
|
|
67
|
+
* @param tokenHash - The SHA-256 hash of the refresh token.
|
|
68
|
+
*/
|
|
69
|
+
async getUserByRefreshTokenHash(tokenHash: string): Promise<User | null> {
|
|
70
|
+
const collection = await this.getCollection();
|
|
71
|
+
const result = await collection.findOne({
|
|
72
|
+
'auth.refreshTokenHashes.tokenHash': tokenHash
|
|
73
|
+
});
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
64
77
|
async getUserCTOsByIds(userIds: UUID[]): Promise<UserCTO[]> {
|
|
65
78
|
const users = await this.getList(userIds);
|
|
66
79
|
return users.map((user) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MigrationService.d.ts","sourceRoot":"","sources":["../../src/services/MigrationService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MigrationService.d.ts","sourceRoot":"","sources":["../../src/services/MigrationService.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACnC;;;;;;;;OAQG;WACU,SAAS,CAAC,MAAM,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAoCtD"}
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
// @ts-nocheck
|
|
3
|
-
import { ApiKey, DashboardTask, DashboardUserConfig, DocumentService, NonogramKatanaItem, NonogramKatanaUpgrade, User } from '@aneuhold/core-ts-db-lib';
|
|
4
3
|
import { DR } from '@aneuhold/core-ts-lib';
|
|
5
|
-
import { v7 as uuidv7 } from 'uuid';
|
|
6
|
-
import ApiKeyRepository from '../repositories/common/ApiKeyRepository.js';
|
|
7
4
|
import UserRepository from '../repositories/common/UserRepository.js';
|
|
8
|
-
import DashboardNonogramKatanaItemRepository from '../repositories/dashboard/DashboardNonogramKatanaItemRepository.js';
|
|
9
|
-
import DashboardNonogramKatanaUpgradeRepository from '../repositories/dashboard/DashboardNonogramKatanaUpgradeRepository.js';
|
|
10
|
-
import DashboardTaskRepository from '../repositories/dashboard/DashboardTaskRepository.js';
|
|
11
|
-
import DashboardUserConfigRepository from '../repositories/dashboard/DashboardUserConfigRepository.js';
|
|
12
5
|
/**
|
|
13
6
|
* A service for migrating the DB to a new state after an existing document
|
|
14
7
|
* change.
|
|
@@ -28,358 +21,30 @@ export default class MigrationService {
|
|
|
28
21
|
*/
|
|
29
22
|
static async migrateDb(dryRun = false) {
|
|
30
23
|
DR.logger.info('Starting migration...');
|
|
31
|
-
// 1. Load all documents
|
|
32
24
|
const userRepo = UserRepository.getRepo();
|
|
33
|
-
const apiKeyRepo = ApiKeyRepository.getRepo();
|
|
34
|
-
const taskRepo = DashboardTaskRepository.getRepo();
|
|
35
|
-
const configRepo = DashboardUserConfigRepository.getRepo();
|
|
36
|
-
const nonogramItemRepo = DashboardNonogramKatanaItemRepository.getRepo();
|
|
37
|
-
const nonogramUpgradesRepo = DashboardNonogramKatanaUpgradeRepository.getRepo();
|
|
38
25
|
const users = await userRepo.getAll();
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
DR.logger.info(`Loaded ${users.length} users, ${apiKeys.length} API keys, ${tasks.length} tasks, ${configs.length} configs, ${nonogramItems.length} nonogram items, and ${nonogramUpgrades.length} nonogram upgrades.`);
|
|
45
|
-
const countObjectIds = (docs) => {
|
|
46
|
-
docs.forEach((doc) => {
|
|
47
|
-
// Get the type of the _id field
|
|
48
|
-
console.log(`Document ID type: ${typeof doc._id}, value: ${doc._id}`);
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
// Create a map of document IDs to new UUIDs
|
|
52
|
-
const allDocs = [
|
|
53
|
-
...users,
|
|
54
|
-
...apiKeys,
|
|
55
|
-
...tasks,
|
|
56
|
-
...configs,
|
|
57
|
-
...nonogramItems,
|
|
58
|
-
...nonogramUpgrades
|
|
59
|
-
];
|
|
60
|
-
const legacyDocs = allDocs.filter((doc) => typeof doc._id === 'object');
|
|
61
|
-
const newDocs = allDocs.filter((doc) => typeof doc._id === 'string');
|
|
62
|
-
DR.logger.info(`Found ${legacyDocs.length} documents with legacy ObjectId IDs and ${newDocs.length} documents with string IDs.`);
|
|
63
|
-
// Delete all existing legacy docs (with object IDs)
|
|
64
|
-
if (legacyDocs.length > 0) {
|
|
65
|
-
const userIdsToDelete = users.filter((u) => typeof u._id === 'object').map((u) => u._id);
|
|
66
|
-
const apiKeyIdsToDelete = apiKeys.filter((k) => typeof k._id === 'object').map((k) => k._id);
|
|
67
|
-
const taskIdsToDelete = tasks.filter((t) => typeof t._id === 'object').map((t) => t._id);
|
|
68
|
-
const configIdsToDelete = configs.filter((c) => typeof c._id === 'object').map((c) => c._id);
|
|
69
|
-
const nonogramItemIdsToDelete = nonogramItems
|
|
70
|
-
.filter((i) => typeof i._id === 'object')
|
|
71
|
-
.map((i) => i._id);
|
|
72
|
-
const nonogramUpgradeIdsToDelete = nonogramUpgrades
|
|
73
|
-
.filter((u) => typeof u._id === 'object')
|
|
74
|
-
.map((u) => u._id);
|
|
75
|
-
if (!dryRun) {
|
|
76
|
-
DR.logger.info(`Deleting ${legacyDocs.length} legacy documents...`);
|
|
77
|
-
if (userIdsToDelete.length > 0) {
|
|
78
|
-
const result = await (await userRepo.getCollection()).deleteMany({ _id: { $in: userIdsToDelete } });
|
|
79
|
-
DR.logger.info(`Deleted ${result.deletedCount} users.`);
|
|
80
|
-
}
|
|
81
|
-
if (apiKeyIdsToDelete.length > 0) {
|
|
82
|
-
const result = await (await apiKeyRepo.getCollection()).deleteMany({ _id: { $in: apiKeyIdsToDelete } });
|
|
83
|
-
DR.logger.info(`Deleted ${result.deletedCount} API keys.`);
|
|
84
|
-
}
|
|
85
|
-
if (taskIdsToDelete.length > 0) {
|
|
86
|
-
const result = await (await taskRepo.getCollection()).deleteMany({ _id: { $in: taskIdsToDelete } });
|
|
87
|
-
DR.logger.info(`Deleted ${result.deletedCount} tasks.`);
|
|
88
|
-
}
|
|
89
|
-
if (configIdsToDelete.length > 0) {
|
|
90
|
-
const result = await (await configRepo.getCollection()).deleteMany({ _id: { $in: configIdsToDelete } });
|
|
91
|
-
DR.logger.info(`Deleted ${result.deletedCount} configs.`);
|
|
92
|
-
}
|
|
93
|
-
if (nonogramItemIdsToDelete.length > 0) {
|
|
94
|
-
const result = await (await nonogramItemRepo.getCollection()).deleteMany({ _id: { $in: nonogramItemIdsToDelete } });
|
|
95
|
-
DR.logger.info(`Deleted ${result.deletedCount} nonogram items.`);
|
|
96
|
-
}
|
|
97
|
-
if (nonogramUpgradeIdsToDelete.length > 0) {
|
|
98
|
-
const result = await (await nonogramUpgradesRepo.getCollection()).deleteMany({ _id: { $in: nonogramUpgradeIdsToDelete } });
|
|
99
|
-
DR.logger.info(`Deleted ${result.deletedCount} nonogram upgrades.`);
|
|
100
|
-
}
|
|
101
|
-
DR.logger.info('Deletion complete.');
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
DR.logger.info(`Dry run: Would delete ${legacyDocs.length} legacy documents.`);
|
|
105
|
-
if (userIdsToDelete.length > 0)
|
|
106
|
-
DR.logger.info(` - ${userIdsToDelete.length} users`);
|
|
107
|
-
if (apiKeyIdsToDelete.length > 0)
|
|
108
|
-
DR.logger.info(` - ${apiKeyIdsToDelete.length} API keys`);
|
|
109
|
-
if (taskIdsToDelete.length > 0)
|
|
110
|
-
DR.logger.info(` - ${taskIdsToDelete.length} tasks`);
|
|
111
|
-
if (configIdsToDelete.length > 0)
|
|
112
|
-
DR.logger.info(` - ${configIdsToDelete.length} configs`);
|
|
113
|
-
if (nonogramItemIdsToDelete.length > 0)
|
|
114
|
-
DR.logger.info(` - ${nonogramItemIdsToDelete.length} nonogram items`);
|
|
115
|
-
if (nonogramUpgradeIdsToDelete.length > 0)
|
|
116
|
-
DR.logger.info(` - ${nonogramUpgradeIdsToDelete.length} nonogram upgrades`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
// We started with 1204 docs
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
async function Migration2025UUID() {
|
|
123
|
-
// Now create the actual map
|
|
124
|
-
const mapFromObjectIdToUUID = new Map();
|
|
125
|
-
const newUsersToCreate = [];
|
|
126
|
-
const newApiKeysToCreate = [];
|
|
127
|
-
const newTasksToCreate = [];
|
|
128
|
-
const newConfigsToCreate = [];
|
|
129
|
-
const newNonogramItemsToCreate = [];
|
|
130
|
-
const newNonogramUpgradesToCreate = [];
|
|
131
|
-
/**
|
|
132
|
-
* Gets a UUID for a given legacy ObjectId. If one already exists in the map,
|
|
133
|
-
* it is returned. Otherwise, a new one is generated and added to the map.
|
|
134
|
-
*/
|
|
135
|
-
const getUUID = (oldId) => {
|
|
136
|
-
const oldIdStr = oldId.toString();
|
|
137
|
-
if (mapFromObjectIdToUUID.has(oldIdStr)) {
|
|
138
|
-
return mapFromObjectIdToUUID.get(oldIdStr);
|
|
139
|
-
}
|
|
140
|
-
const newId = uuidv7();
|
|
141
|
-
mapFromObjectIdToUUID.set(oldIdStr, newId);
|
|
142
|
-
return newId;
|
|
143
|
-
};
|
|
144
|
-
// Create a map of UUID to documents
|
|
145
|
-
const mapFromUUIDToDocument = new Map();
|
|
146
|
-
const verifyDoc = (oldDoc, newDoc) => {
|
|
147
|
-
const oldJson = JSON.stringify(oldDoc, null, 2);
|
|
148
|
-
const newJson = JSON.stringify(newDoc, null, 2);
|
|
149
|
-
const oldLines = oldJson.split('\n').length;
|
|
150
|
-
const newLines = newJson.split('\n').length;
|
|
151
|
-
if (newLines !== oldLines + 1) {
|
|
152
|
-
DR.logger.warn(`Line count mismatch for ${oldDoc.docType || 'doc'} ${oldDoc._id}: Old=${oldLines}, New=${newLines}.`);
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
function createNewDoc(oldDoc) {
|
|
156
|
-
if (mapFromObjectIdToUUID.has(oldDoc._id.toString()) &&
|
|
157
|
-
mapFromUUIDToDocument.has(mapFromObjectIdToUUID.get(oldDoc._id.toString()))) {
|
|
158
|
-
console.warn(`Skipping document ${oldDoc._id} as it was already migrated.` +
|
|
159
|
-
` The created document is ${JSON.stringify(mapFromUUIDToDocument.get(mapFromObjectIdToUUID.get(oldDoc._id.toString())))}`);
|
|
26
|
+
// Add refreshTokenHashes to users that don't have it yet
|
|
27
|
+
const usersToUpdate = users.filter((user) => !user.auth.refreshTokenHashes);
|
|
28
|
+
DR.logger.info(`Found ${usersToUpdate.length} of ${users.length} users missing auth.refreshTokenHashes.`);
|
|
29
|
+
if (usersToUpdate.length === 0) {
|
|
30
|
+
DR.logger.success('No migration needed.');
|
|
160
31
|
return;
|
|
161
32
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return newDoc;
|
|
168
|
-
}
|
|
169
|
-
// Filter to only legacy users with the chosen usernames
|
|
170
|
-
// const userNames = ['demoUser1', 'demoUser2', "testUser", "testUser2"];
|
|
171
|
-
// const userNames = ["testUser"];
|
|
172
|
-
// const legacyUsers = users.filter((u) => userNames.includes(u.userName) && typeof u._id === 'object');
|
|
173
|
-
const legacyUsers = users.filter((u) => typeof u._id === 'object');
|
|
174
|
-
DR.logger.info(`Found ${legacyUsers.length} legacy users to migrate.`);
|
|
175
|
-
// First, create all user documents and generate their UUIDs that way relationships between
|
|
176
|
-
// users in the collaborators field can be mapped correctly.
|
|
177
|
-
legacyUsers.forEach((oldUserDoc) => {
|
|
178
|
-
const newUser = createNewDoc(oldUserDoc);
|
|
179
|
-
if (!newUser)
|
|
33
|
+
if (dryRun) {
|
|
34
|
+
DR.logger.info('Dry run: Would update the following users:');
|
|
35
|
+
usersToUpdate.forEach((user) => {
|
|
36
|
+
DR.logger.info(` - ${user.userName} (${user._id})`);
|
|
37
|
+
});
|
|
180
38
|
return;
|
|
181
|
-
verifyDoc(oldUserDoc, newUser);
|
|
182
|
-
newUsersToCreate.push(newUser);
|
|
183
|
-
});
|
|
184
|
-
// Create the function that will flesh out new documents for a given user document
|
|
185
|
-
function createNewDocsForUser(oldUserDoc) {
|
|
186
|
-
const oldUserIdStr = oldUserDoc._id.toString();
|
|
187
|
-
const newUserId = getUUID(oldUserDoc._id);
|
|
188
|
-
// 1. Create new API keys
|
|
189
|
-
const userApiKeys = apiKeys.filter((k) => k.userId.toString() === oldUserIdStr);
|
|
190
|
-
userApiKeys.forEach((oldKey) => {
|
|
191
|
-
const newKey = createNewDoc(oldKey);
|
|
192
|
-
if (!newKey)
|
|
193
|
-
return;
|
|
194
|
-
newKey.userId = newUserId;
|
|
195
|
-
verifyDoc(oldKey, newKey);
|
|
196
|
-
newApiKeysToCreate.push(newKey);
|
|
197
|
-
});
|
|
198
|
-
// 2. Create new Tasks
|
|
199
|
-
const userTasks = tasks.filter((t) => t.userId.toString() === oldUserIdStr);
|
|
200
|
-
const taskChildrenMap = new Map();
|
|
201
|
-
const rootTasks = [];
|
|
202
|
-
const userTaskIds = new Set(userTasks.map((t) => t._id.toString()));
|
|
203
|
-
userTasks.forEach((task) => {
|
|
204
|
-
const pId = task.parentTaskId?.toString();
|
|
205
|
-
if (pId && userTaskIds.has(pId)) {
|
|
206
|
-
if (!taskChildrenMap.has(pId)) {
|
|
207
|
-
taskChildrenMap.set(pId, []);
|
|
208
|
-
}
|
|
209
|
-
taskChildrenMap.get(pId).push(task);
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
rootTasks.push(task);
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
const queue = [...rootTasks];
|
|
216
|
-
while (queue.length > 0) {
|
|
217
|
-
const oldTask = queue.shift();
|
|
218
|
-
const newTask = createNewDoc(oldTask);
|
|
219
|
-
if (newTask) {
|
|
220
|
-
newTask.userId = newUserId;
|
|
221
|
-
// Map user IDs in tags keys
|
|
222
|
-
const newTags = {};
|
|
223
|
-
Object.keys(oldTask.tags).forEach((tagUserId) => {
|
|
224
|
-
const newTagUserId = getUUID(tagUserId);
|
|
225
|
-
newTags[newTagUserId] = oldTask.tags[tagUserId];
|
|
226
|
-
});
|
|
227
|
-
newTask.tags = newTags;
|
|
228
|
-
// Map user IDs in filterSettings keys
|
|
229
|
-
const newFilterSettings = {};
|
|
230
|
-
Object.keys(oldTask.filterSettings).forEach((fsUserId) => {
|
|
231
|
-
const newFsUserId = getUUID(fsUserId);
|
|
232
|
-
newFilterSettings[newFsUserId] = {
|
|
233
|
-
...oldTask.filterSettings[fsUserId],
|
|
234
|
-
userId: newFsUserId
|
|
235
|
-
};
|
|
236
|
-
});
|
|
237
|
-
newTask.filterSettings = newFilterSettings;
|
|
238
|
-
// Map user IDs in sortSettings keys
|
|
239
|
-
const newSortSettings = {};
|
|
240
|
-
Object.keys(oldTask.sortSettings).forEach((ssUserId) => {
|
|
241
|
-
const newSsUserId = getUUID(ssUserId);
|
|
242
|
-
newSortSettings[newSsUserId] = {
|
|
243
|
-
...oldTask.sortSettings[ssUserId],
|
|
244
|
-
userId: newSsUserId
|
|
245
|
-
};
|
|
246
|
-
});
|
|
247
|
-
newTask.sortSettings = newSortSettings;
|
|
248
|
-
newTask.sharedWith = oldTask.sharedWith.map((id) => getUUID(id));
|
|
249
|
-
if (oldTask.assignedTo) {
|
|
250
|
-
newTask.assignedTo = getUUID(oldTask.assignedTo);
|
|
251
|
-
}
|
|
252
|
-
if (oldTask.parentTaskId) {
|
|
253
|
-
newTask.parentTaskId = getUUID(oldTask.parentTaskId);
|
|
254
|
-
}
|
|
255
|
-
if (oldTask.parentRecurringTaskInfo) {
|
|
256
|
-
newTask.parentRecurringTaskInfo = {
|
|
257
|
-
...oldTask.parentRecurringTaskInfo,
|
|
258
|
-
taskId: getUUID(oldTask.parentRecurringTaskInfo.taskId)
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
verifyDoc(oldTask, newTask);
|
|
262
|
-
newTasksToCreate.push(newTask);
|
|
263
|
-
}
|
|
264
|
-
const children = taskChildrenMap.get(oldTask._id.toString());
|
|
265
|
-
if (children) {
|
|
266
|
-
queue.push(...children);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
// 3. Create new Configs
|
|
270
|
-
const userConfigs = configs.filter((c) => c.userId.toString() === oldUserIdStr);
|
|
271
|
-
userConfigs.forEach((oldConfig) => {
|
|
272
|
-
const newConfig = createNewDoc(oldConfig);
|
|
273
|
-
if (!newConfig)
|
|
274
|
-
return;
|
|
275
|
-
newConfig.userId = newUserId;
|
|
276
|
-
newConfig.collaborators = (oldConfig.collaborators || []).map((id) => getUUID(id));
|
|
277
|
-
if (newConfig.taskListSortSettings) {
|
|
278
|
-
Object.entries(newConfig.taskListSortSettings).forEach(([category, settings]) => {
|
|
279
|
-
const newSettings = {
|
|
280
|
-
userId: getUUID(settings.userId),
|
|
281
|
-
sortList: settings.sortList
|
|
282
|
-
};
|
|
283
|
-
newConfig.taskListSortSettings[category] = newSettings;
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
if (newConfig.taskListFilterSettings) {
|
|
287
|
-
Object.entries(newConfig.taskListFilterSettings).forEach(([category, settings]) => {
|
|
288
|
-
const newSettings = {
|
|
289
|
-
...settings,
|
|
290
|
-
userId: getUUID(settings.userId)
|
|
291
|
-
};
|
|
292
|
-
newConfig.taskListFilterSettings[category] = newSettings;
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
verifyDoc(oldConfig, newConfig);
|
|
296
|
-
newConfigsToCreate.push(newConfig);
|
|
297
|
-
});
|
|
298
|
-
// 4. Nonogram Items
|
|
299
|
-
const userNonogramItems = nonogramItems.filter((i) => i.userId.toString() === oldUserIdStr);
|
|
300
|
-
userNonogramItems.forEach((oldItem) => {
|
|
301
|
-
const newItem = createNewDoc(oldItem);
|
|
302
|
-
if (!newItem)
|
|
303
|
-
return;
|
|
304
|
-
newItem.userId = newUserId;
|
|
305
|
-
verifyDoc(oldItem, newItem);
|
|
306
|
-
newNonogramItemsToCreate.push(newItem);
|
|
307
|
-
});
|
|
308
|
-
// 5. Nonogram Upgrades
|
|
309
|
-
const userNonogramUpgrades = nonogramUpgrades.filter((u) => u.userId.toString() === oldUserIdStr);
|
|
310
|
-
userNonogramUpgrades.forEach((oldUpgrade) => {
|
|
311
|
-
const newUpgrade = createNewDoc(oldUpgrade);
|
|
312
|
-
if (!newUpgrade)
|
|
313
|
-
return;
|
|
314
|
-
newUpgrade.userId = newUserId;
|
|
315
|
-
verifyDoc(oldUpgrade, newUpgrade);
|
|
316
|
-
newNonogramUpgradesToCreate.push(newUpgrade);
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
// Now create related documents for each user
|
|
320
|
-
legacyUsers.forEach((u) => createNewDocsForUser(u));
|
|
321
|
-
const legacyUserIds = new Set(legacyUsers.map((u) => u._id.toString()));
|
|
322
|
-
const expectedUsers = legacyUsers.length;
|
|
323
|
-
const expectedApiKeys = apiKeys.filter((k) => legacyUserIds.has(k.userId.toString())).length;
|
|
324
|
-
const expectedTasks = tasks.filter((t) => legacyUserIds.has(t.userId.toString())).length;
|
|
325
|
-
const expectedConfigs = configs.filter((c) => legacyUserIds.has(c.userId.toString())).length;
|
|
326
|
-
const expectedNonogramItems = nonogramItems.filter((i) => legacyUserIds.has(i.userId.toString())).length;
|
|
327
|
-
const expectedNonogramUpgrades = nonogramUpgrades.filter((u) => legacyUserIds.has(u.userId.toString())).length;
|
|
328
|
-
if (newUsersToCreate.length !== expectedUsers) {
|
|
329
|
-
DR.logger.error(`Expected ${expectedUsers} users, but prepped ${newUsersToCreate.length}.`);
|
|
330
|
-
}
|
|
331
|
-
if (newApiKeysToCreate.length !== expectedApiKeys) {
|
|
332
|
-
DR.logger.error(`Expected ${expectedApiKeys} API keys, but prepped ${newApiKeysToCreate.length}.`);
|
|
333
|
-
}
|
|
334
|
-
if (newTasksToCreate.length !== expectedTasks) {
|
|
335
|
-
DR.logger.error(`Expected ${expectedTasks} tasks, but prepped ${newTasksToCreate.length}.`);
|
|
336
|
-
}
|
|
337
|
-
if (newConfigsToCreate.length !== expectedConfigs) {
|
|
338
|
-
DR.logger.error(`Expected ${expectedConfigs} configs, but prepped ${newConfigsToCreate.length}.`);
|
|
339
|
-
}
|
|
340
|
-
if (newNonogramItemsToCreate.length !== expectedNonogramItems) {
|
|
341
|
-
DR.logger.error(`Expected ${expectedNonogramItems} nonogram items, but prepped ${newNonogramItemsToCreate.length}.`);
|
|
342
|
-
}
|
|
343
|
-
if (newNonogramUpgradesToCreate.length !== expectedNonogramUpgrades) {
|
|
344
|
-
DR.logger.error(`Expected ${expectedNonogramUpgrades} nonogram upgrades, but prepped ${newNonogramUpgradesToCreate.length}.`);
|
|
345
|
-
}
|
|
346
|
-
DR.logger.info(`Prepped ${newUsersToCreate.length} new users.`);
|
|
347
|
-
DR.logger.info(`Prepped ${newApiKeysToCreate.length} new API keys.`);
|
|
348
|
-
DR.logger.info(`Prepped ${newTasksToCreate.length} new tasks.`);
|
|
349
|
-
DR.logger.info(`Prepped ${newConfigsToCreate.length} new configs.`);
|
|
350
|
-
DR.logger.info(`Prepped ${newNonogramItemsToCreate.length} new nonogram items.`);
|
|
351
|
-
DR.logger.info(`Prepped ${newNonogramUpgradesToCreate.length} new nonogram upgrades.`);
|
|
352
|
-
DR.logger.info(`In total, prepped ${newUsersToCreate.length +
|
|
353
|
-
newApiKeysToCreate.length +
|
|
354
|
-
newTasksToCreate.length +
|
|
355
|
-
newConfigsToCreate.length +
|
|
356
|
-
newNonogramItemsToCreate.length +
|
|
357
|
-
newNonogramUpgradesToCreate.length} documents for insertion.`);
|
|
358
|
-
if (!dryRun) {
|
|
359
|
-
DR.logger.info('Dry run is false. Inserting documents into DB...');
|
|
360
|
-
if (newUsersToCreate.length > 0) {
|
|
361
|
-
await (await userRepo.getCollection()).insertMany(newUsersToCreate);
|
|
362
39
|
}
|
|
363
|
-
|
|
364
|
-
await
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
if (newConfigsToCreate.length > 0) {
|
|
370
|
-
await (await configRepo.getCollection()).insertMany(newConfigsToCreate);
|
|
371
|
-
}
|
|
372
|
-
if (newNonogramItemsToCreate.length > 0) {
|
|
373
|
-
await (await nonogramItemRepo.getCollection()).insertMany(newNonogramItemsToCreate);
|
|
374
|
-
}
|
|
375
|
-
if (newNonogramUpgradesToCreate.length > 0) {
|
|
376
|
-
await (await nonogramUpgradesRepo.getCollection()).insertMany(newNonogramUpgradesToCreate);
|
|
40
|
+
for (const user of usersToUpdate) {
|
|
41
|
+
await userRepo.update({
|
|
42
|
+
_id: user._id,
|
|
43
|
+
auth: { ...user.auth, refreshTokenHashes: [] }
|
|
44
|
+
});
|
|
45
|
+
DR.logger.info(`Updated user ${user.userName} (${user._id})`);
|
|
377
46
|
}
|
|
378
|
-
DR.logger.
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
DR.logger.info('Dry run is true. Skipping DB insertion.');
|
|
47
|
+
DR.logger.success(`Migration complete. Updated ${usersToUpdate.length} users.`);
|
|
382
48
|
}
|
|
383
|
-
return;
|
|
384
49
|
}
|
|
385
50
|
//# sourceMappingURL=MigrationService.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MigrationService.js","sourceRoot":"","sources":["../../src/services/MigrationService.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MigrationService.js","sourceRoot":"","sources":["../../src/services/MigrationService.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,cAAc;AACd,OAAO,EAAE,EAAE,EAAE,MAAM,uBAAuB,CAAC;AAC3C,OAAO,cAAc,MAAM,0CAA0C,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACnC;;;;;;;;OAQG;IACH,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,KAAK;QACnC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAExC,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC;QAEtC,yDAAyD;QACzD,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAE5E,EAAE,CAAC,MAAM,CAAC,IAAI,CACZ,SAAS,aAAa,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM,yCAAyC,CAC1F,CAAC;QAEF,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;YAC1C,OAAO;QACT,CAAC;QAED,IAAI,MAAM,EAAE,CAAC;YACX,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;YAC7D,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;gBAC7B,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;YACvD,CAAC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;YACjC,MAAM,QAAQ,CAAC,MAAM,CAAC;gBACpB,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE;aAC/C,CAAC,CAAC;YACH,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;QAChE,CAAC;QAED,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,+BAA+B,aAAa,CAAC,MAAM,SAAS,CAAC,CAAC;IAClF,CAAC;CACF"}
|
|
@@ -1,22 +1,7 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
// @ts-nocheck
|
|
3
|
-
import {
|
|
4
|
-
ApiKey,
|
|
5
|
-
DashboardTask,
|
|
6
|
-
DashboardUserConfig,
|
|
7
|
-
DocumentService,
|
|
8
|
-
NonogramKatanaItem,
|
|
9
|
-
NonogramKatanaUpgrade,
|
|
10
|
-
User
|
|
11
|
-
} from '@aneuhold/core-ts-db-lib';
|
|
12
3
|
import { DR } from '@aneuhold/core-ts-lib';
|
|
13
|
-
import { v7 as uuidv7 } from 'uuid';
|
|
14
|
-
import ApiKeyRepository from '../repositories/common/ApiKeyRepository.js';
|
|
15
4
|
import UserRepository from '../repositories/common/UserRepository.js';
|
|
16
|
-
import DashboardNonogramKatanaItemRepository from '../repositories/dashboard/DashboardNonogramKatanaItemRepository.js';
|
|
17
|
-
import DashboardNonogramKatanaUpgradeRepository from '../repositories/dashboard/DashboardNonogramKatanaUpgradeRepository.js';
|
|
18
|
-
import DashboardTaskRepository from '../repositories/dashboard/DashboardTaskRepository.js';
|
|
19
|
-
import DashboardUserConfigRepository from '../repositories/dashboard/DashboardUserConfigRepository.js';
|
|
20
5
|
|
|
21
6
|
/**
|
|
22
7
|
* A service for migrating the DB to a new state after an existing document
|
|
@@ -38,429 +23,37 @@ export default class MigrationService {
|
|
|
38
23
|
static async migrateDb(dryRun = false): Promise<void> {
|
|
39
24
|
DR.logger.info('Starting migration...');
|
|
40
25
|
|
|
41
|
-
// 1. Load all documents
|
|
42
26
|
const userRepo = UserRepository.getRepo();
|
|
43
|
-
const apiKeyRepo = ApiKeyRepository.getRepo();
|
|
44
|
-
const taskRepo = DashboardTaskRepository.getRepo();
|
|
45
|
-
const configRepo = DashboardUserConfigRepository.getRepo();
|
|
46
|
-
const nonogramItemRepo = DashboardNonogramKatanaItemRepository.getRepo();
|
|
47
|
-
const nonogramUpgradesRepo = DashboardNonogramKatanaUpgradeRepository.getRepo();
|
|
48
|
-
|
|
49
27
|
const users = await userRepo.getAll();
|
|
50
|
-
const apiKeys = await apiKeyRepo.getAll();
|
|
51
|
-
const tasks = await taskRepo.getAll();
|
|
52
|
-
const configs = await configRepo.getAll();
|
|
53
|
-
const nonogramItems = await nonogramItemRepo.getAll();
|
|
54
|
-
const nonogramUpgrades = await nonogramUpgradesRepo.getAll();
|
|
55
|
-
|
|
56
|
-
DR.logger.info(
|
|
57
|
-
`Loaded ${users.length} users, ${apiKeys.length} API keys, ${tasks.length} tasks, ${configs.length} configs, ${nonogramItems.length} nonogram items, and ${nonogramUpgrades.length} nonogram upgrades.`
|
|
58
|
-
);
|
|
59
28
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// Get the type of the _id field
|
|
63
|
-
console.log(`Document ID type: ${typeof doc._id}, value: ${doc._id}`);
|
|
64
|
-
});
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// Create a map of document IDs to new UUIDs
|
|
68
|
-
const allDocs = [
|
|
69
|
-
...users,
|
|
70
|
-
...apiKeys,
|
|
71
|
-
...tasks,
|
|
72
|
-
...configs,
|
|
73
|
-
...nonogramItems,
|
|
74
|
-
...nonogramUpgrades
|
|
75
|
-
];
|
|
76
|
-
const legacyDocs = allDocs.filter((doc) => typeof doc._id === 'object');
|
|
77
|
-
const newDocs = allDocs.filter((doc) => typeof doc._id === 'string');
|
|
29
|
+
// Add refreshTokenHashes to users that don't have it yet
|
|
30
|
+
const usersToUpdate = users.filter((user) => !user.auth.refreshTokenHashes);
|
|
78
31
|
|
|
79
32
|
DR.logger.info(
|
|
80
|
-
`Found ${
|
|
33
|
+
`Found ${usersToUpdate.length} of ${users.length} users missing auth.refreshTokenHashes.`
|
|
81
34
|
);
|
|
82
35
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const apiKeyIdsToDelete = apiKeys.filter((k) => typeof k._id === 'object').map((k) => k._id);
|
|
87
|
-
const taskIdsToDelete = tasks.filter((t) => typeof t._id === 'object').map((t) => t._id);
|
|
88
|
-
const configIdsToDelete = configs.filter((c) => typeof c._id === 'object').map((c) => c._id);
|
|
89
|
-
const nonogramItemIdsToDelete = nonogramItems
|
|
90
|
-
.filter((i) => typeof i._id === 'object')
|
|
91
|
-
.map((i) => i._id);
|
|
92
|
-
const nonogramUpgradeIdsToDelete = nonogramUpgrades
|
|
93
|
-
.filter((u) => typeof u._id === 'object')
|
|
94
|
-
.map((u) => u._id);
|
|
95
|
-
|
|
96
|
-
if (!dryRun) {
|
|
97
|
-
DR.logger.info(`Deleting ${legacyDocs.length} legacy documents...`);
|
|
98
|
-
|
|
99
|
-
if (userIdsToDelete.length > 0) {
|
|
100
|
-
const result = await (
|
|
101
|
-
await userRepo.getCollection()
|
|
102
|
-
).deleteMany({ _id: { $in: userIdsToDelete } });
|
|
103
|
-
DR.logger.info(`Deleted ${result.deletedCount} users.`);
|
|
104
|
-
}
|
|
105
|
-
if (apiKeyIdsToDelete.length > 0) {
|
|
106
|
-
const result = await (
|
|
107
|
-
await apiKeyRepo.getCollection()
|
|
108
|
-
).deleteMany({ _id: { $in: apiKeyIdsToDelete } });
|
|
109
|
-
DR.logger.info(`Deleted ${result.deletedCount} API keys.`);
|
|
110
|
-
}
|
|
111
|
-
if (taskIdsToDelete.length > 0) {
|
|
112
|
-
const result = await (
|
|
113
|
-
await taskRepo.getCollection()
|
|
114
|
-
).deleteMany({ _id: { $in: taskIdsToDelete } });
|
|
115
|
-
DR.logger.info(`Deleted ${result.deletedCount} tasks.`);
|
|
116
|
-
}
|
|
117
|
-
if (configIdsToDelete.length > 0) {
|
|
118
|
-
const result = await (
|
|
119
|
-
await configRepo.getCollection()
|
|
120
|
-
).deleteMany({ _id: { $in: configIdsToDelete } });
|
|
121
|
-
DR.logger.info(`Deleted ${result.deletedCount} configs.`);
|
|
122
|
-
}
|
|
123
|
-
if (nonogramItemIdsToDelete.length > 0) {
|
|
124
|
-
const result = await (
|
|
125
|
-
await nonogramItemRepo.getCollection()
|
|
126
|
-
).deleteMany({ _id: { $in: nonogramItemIdsToDelete } });
|
|
127
|
-
DR.logger.info(`Deleted ${result.deletedCount} nonogram items.`);
|
|
128
|
-
}
|
|
129
|
-
if (nonogramUpgradeIdsToDelete.length > 0) {
|
|
130
|
-
const result = await (
|
|
131
|
-
await nonogramUpgradesRepo.getCollection()
|
|
132
|
-
).deleteMany({ _id: { $in: nonogramUpgradeIdsToDelete } });
|
|
133
|
-
DR.logger.info(`Deleted ${result.deletedCount} nonogram upgrades.`);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
DR.logger.info('Deletion complete.');
|
|
137
|
-
} else {
|
|
138
|
-
DR.logger.info(`Dry run: Would delete ${legacyDocs.length} legacy documents.`);
|
|
139
|
-
if (userIdsToDelete.length > 0) DR.logger.info(` - ${userIdsToDelete.length} users`);
|
|
140
|
-
if (apiKeyIdsToDelete.length > 0)
|
|
141
|
-
DR.logger.info(` - ${apiKeyIdsToDelete.length} API keys`);
|
|
142
|
-
if (taskIdsToDelete.length > 0) DR.logger.info(` - ${taskIdsToDelete.length} tasks`);
|
|
143
|
-
if (configIdsToDelete.length > 0) DR.logger.info(` - ${configIdsToDelete.length} configs`);
|
|
144
|
-
if (nonogramItemIdsToDelete.length > 0)
|
|
145
|
-
DR.logger.info(` - ${nonogramItemIdsToDelete.length} nonogram items`);
|
|
146
|
-
if (nonogramUpgradeIdsToDelete.length > 0)
|
|
147
|
-
DR.logger.info(` - ${nonogramUpgradeIdsToDelete.length} nonogram upgrades`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
// We started with 1204 docs
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async function Migration2025UUID() {
|
|
155
|
-
// Now create the actual map
|
|
156
|
-
const mapFromObjectIdToUUID = new Map<string, UUID>();
|
|
157
|
-
|
|
158
|
-
const newUsersToCreate: User[] = [];
|
|
159
|
-
const newApiKeysToCreate: ApiKey[] = [];
|
|
160
|
-
const newTasksToCreate: DashboardTask[] = [];
|
|
161
|
-
const newConfigsToCreate: DashboardUserConfig[] = [];
|
|
162
|
-
const newNonogramItemsToCreate: NonogramKatanaItem[] = [];
|
|
163
|
-
const newNonogramUpgradesToCreate: NonogramKatanaUpgrade[] = [];
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Gets a UUID for a given legacy ObjectId. If one already exists in the map,
|
|
167
|
-
* it is returned. Otherwise, a new one is generated and added to the map.
|
|
168
|
-
*/
|
|
169
|
-
const getUUID = (oldId: ObjectId): UUID => {
|
|
170
|
-
const oldIdStr = oldId.toString();
|
|
171
|
-
if (mapFromObjectIdToUUID.has(oldIdStr)) {
|
|
172
|
-
return mapFromObjectIdToUUID.get(oldIdStr);
|
|
173
|
-
}
|
|
174
|
-
const newId = uuidv7();
|
|
175
|
-
mapFromObjectIdToUUID.set(oldIdStr, newId);
|
|
176
|
-
return newId;
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
// Create a map of UUID to documents
|
|
180
|
-
const mapFromUUIDToDocument = new Map<UUID, any>();
|
|
181
|
-
|
|
182
|
-
const verifyDoc = (oldDoc: any, newDoc: any) => {
|
|
183
|
-
const oldJson = JSON.stringify(oldDoc, null, 2);
|
|
184
|
-
const newJson = JSON.stringify(newDoc, null, 2);
|
|
185
|
-
const oldLines = oldJson.split('\n').length;
|
|
186
|
-
const newLines = newJson.split('\n').length;
|
|
187
|
-
if (newLines !== oldLines + 1) {
|
|
188
|
-
DR.logger.warn(
|
|
189
|
-
`Line count mismatch for ${oldDoc.docType || 'doc'} ${oldDoc._id}: Old=${oldLines}, New=${newLines}.`
|
|
190
|
-
);
|
|
36
|
+
if (usersToUpdate.length === 0) {
|
|
37
|
+
DR.logger.success('No migration needed.');
|
|
38
|
+
return;
|
|
191
39
|
}
|
|
192
|
-
};
|
|
193
40
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
console.warn(
|
|
200
|
-
`Skipping document ${oldDoc._id} as it was already migrated.` +
|
|
201
|
-
` The created document is ${JSON.stringify(mapFromUUIDToDocument.get(mapFromObjectIdToUUID.get(oldDoc._id.toString())))}`
|
|
202
|
-
);
|
|
41
|
+
if (dryRun) {
|
|
42
|
+
DR.logger.info('Dry run: Would update the following users:');
|
|
43
|
+
usersToUpdate.forEach((user) => {
|
|
44
|
+
DR.logger.info(` - ${user.userName} (${user._id})`);
|
|
45
|
+
});
|
|
203
46
|
return;
|
|
204
47
|
}
|
|
205
|
-
const newDocId = getUUID(oldDoc._id);
|
|
206
|
-
const newDoc = DocumentService.deepCopy(oldDoc);
|
|
207
|
-
newDoc._id = newDocId;
|
|
208
|
-
newDoc.oldOId = oldDoc._id.toString();
|
|
209
|
-
mapFromUUIDToDocument.set(newDocId, newDoc);
|
|
210
|
-
return newDoc;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Filter to only legacy users with the chosen usernames
|
|
214
|
-
// const userNames = ['demoUser1', 'demoUser2', "testUser", "testUser2"];
|
|
215
|
-
// const userNames = ["testUser"];
|
|
216
|
-
// const legacyUsers = users.filter((u) => userNames.includes(u.userName) && typeof u._id === 'object');
|
|
217
|
-
const legacyUsers = users.filter((u) => typeof u._id === 'object');
|
|
218
|
-
DR.logger.info(`Found ${legacyUsers.length} legacy users to migrate.`);
|
|
219
|
-
|
|
220
|
-
// First, create all user documents and generate their UUIDs that way relationships between
|
|
221
|
-
// users in the collaborators field can be mapped correctly.
|
|
222
|
-
legacyUsers.forEach((oldUserDoc) => {
|
|
223
|
-
const newUser = createNewDoc(oldUserDoc);
|
|
224
|
-
if (!newUser) return;
|
|
225
|
-
verifyDoc(oldUserDoc, newUser);
|
|
226
|
-
newUsersToCreate.push(newUser);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
// Create the function that will flesh out new documents for a given user document
|
|
230
|
-
function createNewDocsForUser(oldUserDoc: User) {
|
|
231
|
-
const oldUserIdStr = oldUserDoc._id.toString();
|
|
232
|
-
const newUserId = getUUID(oldUserDoc._id);
|
|
233
|
-
|
|
234
|
-
// 1. Create new API keys
|
|
235
|
-
const userApiKeys = apiKeys.filter((k) => k.userId.toString() === oldUserIdStr);
|
|
236
|
-
userApiKeys.forEach((oldKey) => {
|
|
237
|
-
const newKey = createNewDoc(oldKey);
|
|
238
|
-
if (!newKey) return;
|
|
239
|
-
newKey.userId = newUserId;
|
|
240
|
-
verifyDoc(oldKey, newKey);
|
|
241
|
-
newApiKeysToCreate.push(newKey);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// 2. Create new Tasks
|
|
245
|
-
const userTasks = tasks.filter((t) => t.userId.toString() === oldUserIdStr);
|
|
246
|
-
|
|
247
|
-
const taskChildrenMap = new Map<string, DashboardTask[]>();
|
|
248
|
-
const rootTasks: DashboardTask[] = [];
|
|
249
|
-
const userTaskIds = new Set(userTasks.map((t) => t._id.toString()));
|
|
250
|
-
|
|
251
|
-
userTasks.forEach((task) => {
|
|
252
|
-
const pId = task.parentTaskId?.toString();
|
|
253
|
-
if (pId && userTaskIds.has(pId)) {
|
|
254
|
-
if (!taskChildrenMap.has(pId)) {
|
|
255
|
-
taskChildrenMap.set(pId, []);
|
|
256
|
-
}
|
|
257
|
-
taskChildrenMap.get(pId).push(task);
|
|
258
|
-
} else {
|
|
259
|
-
rootTasks.push(task);
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
const queue = [...rootTasks];
|
|
264
|
-
while (queue.length > 0) {
|
|
265
|
-
const oldTask = queue.shift()!;
|
|
266
|
-
const newTask = createNewDoc(oldTask);
|
|
267
|
-
|
|
268
|
-
if (newTask) {
|
|
269
|
-
newTask.userId = newUserId;
|
|
270
48
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
newTask.tags = newTags;
|
|
278
|
-
|
|
279
|
-
// Map user IDs in filterSettings keys
|
|
280
|
-
const newFilterSettings: any = {};
|
|
281
|
-
Object.keys(oldTask.filterSettings).forEach((fsUserId) => {
|
|
282
|
-
const newFsUserId = getUUID(fsUserId);
|
|
283
|
-
newFilterSettings[newFsUserId] = {
|
|
284
|
-
...oldTask.filterSettings[fsUserId],
|
|
285
|
-
userId: newFsUserId
|
|
286
|
-
};
|
|
287
|
-
});
|
|
288
|
-
newTask.filterSettings = newFilterSettings;
|
|
289
|
-
|
|
290
|
-
// Map user IDs in sortSettings keys
|
|
291
|
-
const newSortSettings: any = {};
|
|
292
|
-
Object.keys(oldTask.sortSettings).forEach((ssUserId) => {
|
|
293
|
-
const newSsUserId = getUUID(ssUserId);
|
|
294
|
-
newSortSettings[newSsUserId] = {
|
|
295
|
-
...oldTask.sortSettings[ssUserId],
|
|
296
|
-
userId: newSsUserId
|
|
297
|
-
};
|
|
298
|
-
});
|
|
299
|
-
newTask.sortSettings = newSortSettings;
|
|
300
|
-
|
|
301
|
-
newTask.sharedWith = oldTask.sharedWith.map((id: any) => getUUID(id));
|
|
302
|
-
if (oldTask.assignedTo) {
|
|
303
|
-
newTask.assignedTo = getUUID(oldTask.assignedTo);
|
|
304
|
-
}
|
|
305
|
-
if (oldTask.parentTaskId) {
|
|
306
|
-
newTask.parentTaskId = getUUID(oldTask.parentTaskId);
|
|
307
|
-
}
|
|
308
|
-
if (oldTask.parentRecurringTaskInfo) {
|
|
309
|
-
newTask.parentRecurringTaskInfo = {
|
|
310
|
-
...oldTask.parentRecurringTaskInfo,
|
|
311
|
-
taskId: getUUID(oldTask.parentRecurringTaskInfo.taskId)
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
verifyDoc(oldTask, newTask);
|
|
316
|
-
newTasksToCreate.push(newTask);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const children = taskChildrenMap.get(oldTask._id.toString());
|
|
320
|
-
if (children) {
|
|
321
|
-
queue.push(...children);
|
|
322
|
-
}
|
|
49
|
+
for (const user of usersToUpdate) {
|
|
50
|
+
await userRepo.update({
|
|
51
|
+
_id: user._id,
|
|
52
|
+
auth: { ...user.auth, refreshTokenHashes: [] }
|
|
53
|
+
});
|
|
54
|
+
DR.logger.info(`Updated user ${user.userName} (${user._id})`);
|
|
323
55
|
}
|
|
324
56
|
|
|
325
|
-
|
|
326
|
-
const userConfigs = configs.filter((c) => c.userId.toString() === oldUserIdStr);
|
|
327
|
-
userConfigs.forEach((oldConfig) => {
|
|
328
|
-
const newConfig = createNewDoc(oldConfig);
|
|
329
|
-
if (!newConfig) return;
|
|
330
|
-
newConfig.userId = newUserId;
|
|
331
|
-
newConfig.collaborators = (oldConfig.collaborators || []).map((id) => getUUID(id));
|
|
332
|
-
if (newConfig.taskListSortSettings) {
|
|
333
|
-
Object.entries(newConfig.taskListSortSettings).forEach(
|
|
334
|
-
([category, settings]: [string, any]) => {
|
|
335
|
-
const newSettings = {
|
|
336
|
-
userId: getUUID(settings.userId),
|
|
337
|
-
sortList: settings.sortList
|
|
338
|
-
};
|
|
339
|
-
newConfig.taskListSortSettings[category] = newSettings;
|
|
340
|
-
}
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
if (newConfig.taskListFilterSettings) {
|
|
344
|
-
Object.entries(newConfig.taskListFilterSettings).forEach(
|
|
345
|
-
([category, settings]: [string, any]) => {
|
|
346
|
-
const newSettings = {
|
|
347
|
-
...settings,
|
|
348
|
-
userId: getUUID(settings.userId)
|
|
349
|
-
};
|
|
350
|
-
newConfig.taskListFilterSettings[category] = newSettings;
|
|
351
|
-
}
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
verifyDoc(oldConfig, newConfig);
|
|
355
|
-
newConfigsToCreate.push(newConfig);
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
// 4. Nonogram Items
|
|
359
|
-
const userNonogramItems = nonogramItems.filter((i) => i.userId.toString() === oldUserIdStr);
|
|
360
|
-
userNonogramItems.forEach((oldItem) => {
|
|
361
|
-
const newItem = createNewDoc(oldItem);
|
|
362
|
-
if (!newItem) return;
|
|
363
|
-
newItem.userId = newUserId;
|
|
364
|
-
verifyDoc(oldItem, newItem);
|
|
365
|
-
newNonogramItemsToCreate.push(newItem);
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
// 5. Nonogram Upgrades
|
|
369
|
-
const userNonogramUpgrades = nonogramUpgrades.filter(
|
|
370
|
-
(u) => u.userId.toString() === oldUserIdStr
|
|
371
|
-
);
|
|
372
|
-
userNonogramUpgrades.forEach((oldUpgrade) => {
|
|
373
|
-
const newUpgrade = createNewDoc(oldUpgrade);
|
|
374
|
-
if (!newUpgrade) return;
|
|
375
|
-
newUpgrade.userId = newUserId;
|
|
376
|
-
verifyDoc(oldUpgrade, newUpgrade);
|
|
377
|
-
newNonogramUpgradesToCreate.push(newUpgrade);
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Now create related documents for each user
|
|
382
|
-
legacyUsers.forEach((u) => createNewDocsForUser(u));
|
|
383
|
-
|
|
384
|
-
const legacyUserIds = new Set(legacyUsers.map((u) => u._id.toString()));
|
|
385
|
-
const expectedUsers = legacyUsers.length;
|
|
386
|
-
const expectedApiKeys = apiKeys.filter((k) => legacyUserIds.has(k.userId.toString())).length;
|
|
387
|
-
const expectedTasks = tasks.filter((t) => legacyUserIds.has(t.userId.toString())).length;
|
|
388
|
-
const expectedConfigs = configs.filter((c) => legacyUserIds.has(c.userId.toString())).length;
|
|
389
|
-
const expectedNonogramItems = nonogramItems.filter((i) =>
|
|
390
|
-
legacyUserIds.has(i.userId.toString())
|
|
391
|
-
).length;
|
|
392
|
-
const expectedNonogramUpgrades = nonogramUpgrades.filter((u) =>
|
|
393
|
-
legacyUserIds.has(u.userId.toString())
|
|
394
|
-
).length;
|
|
395
|
-
|
|
396
|
-
if (newUsersToCreate.length !== expectedUsers) {
|
|
397
|
-
DR.logger.error(`Expected ${expectedUsers} users, but prepped ${newUsersToCreate.length}.`);
|
|
398
|
-
}
|
|
399
|
-
if (newApiKeysToCreate.length !== expectedApiKeys) {
|
|
400
|
-
DR.logger.error(
|
|
401
|
-
`Expected ${expectedApiKeys} API keys, but prepped ${newApiKeysToCreate.length}.`
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
if (newTasksToCreate.length !== expectedTasks) {
|
|
405
|
-
DR.logger.error(`Expected ${expectedTasks} tasks, but prepped ${newTasksToCreate.length}.`);
|
|
406
|
-
}
|
|
407
|
-
if (newConfigsToCreate.length !== expectedConfigs) {
|
|
408
|
-
DR.logger.error(
|
|
409
|
-
`Expected ${expectedConfigs} configs, but prepped ${newConfigsToCreate.length}.`
|
|
410
|
-
);
|
|
411
|
-
}
|
|
412
|
-
if (newNonogramItemsToCreate.length !== expectedNonogramItems) {
|
|
413
|
-
DR.logger.error(
|
|
414
|
-
`Expected ${expectedNonogramItems} nonogram items, but prepped ${newNonogramItemsToCreate.length}.`
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
|
-
if (newNonogramUpgradesToCreate.length !== expectedNonogramUpgrades) {
|
|
418
|
-
DR.logger.error(
|
|
419
|
-
`Expected ${expectedNonogramUpgrades} nonogram upgrades, but prepped ${newNonogramUpgradesToCreate.length}.`
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
DR.logger.info(`Prepped ${newUsersToCreate.length} new users.`);
|
|
424
|
-
DR.logger.info(`Prepped ${newApiKeysToCreate.length} new API keys.`);
|
|
425
|
-
DR.logger.info(`Prepped ${newTasksToCreate.length} new tasks.`);
|
|
426
|
-
DR.logger.info(`Prepped ${newConfigsToCreate.length} new configs.`);
|
|
427
|
-
DR.logger.info(`Prepped ${newNonogramItemsToCreate.length} new nonogram items.`);
|
|
428
|
-
DR.logger.info(`Prepped ${newNonogramUpgradesToCreate.length} new nonogram upgrades.`);
|
|
429
|
-
DR.logger.info(
|
|
430
|
-
`In total, prepped ${
|
|
431
|
-
newUsersToCreate.length +
|
|
432
|
-
newApiKeysToCreate.length +
|
|
433
|
-
newTasksToCreate.length +
|
|
434
|
-
newConfigsToCreate.length +
|
|
435
|
-
newNonogramItemsToCreate.length +
|
|
436
|
-
newNonogramUpgradesToCreate.length
|
|
437
|
-
} documents for insertion.`
|
|
438
|
-
);
|
|
439
|
-
|
|
440
|
-
if (!dryRun) {
|
|
441
|
-
DR.logger.info('Dry run is false. Inserting documents into DB...');
|
|
442
|
-
if (newUsersToCreate.length > 0) {
|
|
443
|
-
await(await userRepo.getCollection()).insertMany(newUsersToCreate);
|
|
444
|
-
}
|
|
445
|
-
if (newApiKeysToCreate.length > 0) {
|
|
446
|
-
await(await apiKeyRepo.getCollection()).insertMany(newApiKeysToCreate);
|
|
447
|
-
}
|
|
448
|
-
if (newTasksToCreate.length > 0) {
|
|
449
|
-
await(await taskRepo.getCollection()).insertMany(newTasksToCreate);
|
|
450
|
-
}
|
|
451
|
-
if (newConfigsToCreate.length > 0) {
|
|
452
|
-
await(await configRepo.getCollection()).insertMany(newConfigsToCreate);
|
|
453
|
-
}
|
|
454
|
-
if (newNonogramItemsToCreate.length > 0) {
|
|
455
|
-
await(await nonogramItemRepo.getCollection()).insertMany(newNonogramItemsToCreate);
|
|
456
|
-
}
|
|
457
|
-
if (newNonogramUpgradesToCreate.length > 0) {
|
|
458
|
-
await(await nonogramUpgradesRepo.getCollection()).insertMany(newNonogramUpgradesToCreate);
|
|
459
|
-
}
|
|
460
|
-
DR.logger.info('Insertion complete.');
|
|
461
|
-
} else {
|
|
462
|
-
DR.logger.info('Dry run is true. Skipping DB insertion.');
|
|
57
|
+
DR.logger.success(`Migration complete. Updated ${usersToUpdate.length} users.`);
|
|
463
58
|
}
|
|
464
|
-
|
|
465
|
-
return;
|
|
466
59
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@aneuhold/be-ts-db-lib",
|
|
3
3
|
"author": "Anton G. Neuhold Jr.",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "4.2.
|
|
5
|
+
"version": "4.2.19",
|
|
6
6
|
"description": "A backend database library meant to actually interact with various databases in personal projects",
|
|
7
7
|
"packageManager": "pnpm@10.25.0",
|
|
8
8
|
"type": "module",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"MongoDB"
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@aneuhold/be-ts-lib": "^3.1.
|
|
61
|
+
"@aneuhold/be-ts-lib": "^3.1.6",
|
|
62
62
|
"@aneuhold/core-ts-db-lib": "^5.0.1",
|
|
63
63
|
"@aneuhold/core-ts-lib": "^2.4.3",
|
|
64
64
|
"bson": "^7.0.0",
|