@friggframework/core 2.0.0--canary.490.dfd8df5.0 → 2.0.0--canary.490.1d4d68c.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/credential/repositories/credential-repository-factory.js +11 -13
- package/credential/repositories/{credential-repository-documentdb.js → credential-repository-mongodb-native.js} +4 -4
- package/database/repositories/health-check-repository-factory.js +10 -33
- package/database/repositories/{health-check-repository-documentdb.js → health-check-repository-mongodb-native.js} +4 -4
- package/integrations/repositories/integration-mapping-repository-factory.js +5 -30
- package/integrations/repositories/{integration-mapping-repository-documentdb.js → integration-mapping-repository-mongodb-native.js} +4 -4
- package/integrations/repositories/integration-repository-factory.js +11 -12
- package/integrations/repositories/{integration-repository-documentdb.js → integration-repository-mongodb-native.js} +12 -4
- package/integrations/repositories/process-repository-factory.js +5 -30
- package/integrations/repositories/{process-repository-documentdb.js → process-repository-mongodb-native.js} +4 -4
- package/modules/repositories/module-repository-factory.js +5 -11
- package/modules/repositories/{module-repository-documentdb.js → module-repository-mongodb-native.js} +4 -4
- package/package.json +5 -5
- package/syncs/repositories/sync-repository-factory.js +5 -11
- package/syncs/repositories/{sync-repository-documentdb.js → sync-repository-mongodb-native.js} +4 -4
- package/token/repositories/token-repository-factory.js +5 -11
- package/token/repositories/{token-repository-documentdb.js → token-repository-mongodb-native.js} +4 -4
- package/user/repositories/user-repository-factory.js +7 -32
- package/user/repositories/{user-repository-documentdb.js → user-repository-mongodb-native.js} +4 -4
- package/websocket/repositories/websocket-connection-repository-factory.js +5 -11
- package/websocket/repositories/{websocket-connection-repository-documentdb.js → websocket-connection-repository-mongodb-native.js} +4 -4
- package/credential/repositories/credential-repository-mongo.js +0 -311
- package/integrations/repositories/integration-mapping-repository-mongo.js +0 -165
- package/integrations/repositories/integration-repository-mongo.js +0 -307
- package/integrations/repositories/process-repository-mongo.js +0 -194
- package/modules/repositories/module-repository-mongo.js +0 -381
- package/syncs/repositories/sync-repository-mongo.js +0 -243
- package/token/repositories/token-repository-mongo.js +0 -216
- package/user/repositories/user-repository-mongo.js +0 -295
- package/websocket/repositories/websocket-connection-repository-mongo.js +0 -160
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
const { prisma } = require('../../database/prisma');
|
|
2
|
-
const bcrypt = require('bcryptjs');
|
|
3
|
-
const { TokenRepositoryInterface } = require('./token-repository-interface');
|
|
4
|
-
const { BaseRepositoryMongoDB } = require('../../database/repositories/base-repository-mongodb');
|
|
5
|
-
|
|
6
|
-
const BCRYPT_ROUNDS = 10;
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* MongoDB Token Repository Adapter
|
|
10
|
-
* Handles persistence of authentication tokens with bcrypt hashing
|
|
11
|
-
*
|
|
12
|
-
* MongoDB-specific characteristics:
|
|
13
|
-
* - Uses String IDs (ObjectId)
|
|
14
|
-
* - No ID conversion needed (IDs are already strings)
|
|
15
|
-
* - Bcrypt hashing handled in repository layer
|
|
16
|
-
*/
|
|
17
|
-
class TokenRepositoryMongo extends TokenRepositoryInterface {
|
|
18
|
-
constructor() {
|
|
19
|
-
super();
|
|
20
|
-
|
|
21
|
-
// Use MongoDB base repository for DocumentDB compatibility
|
|
22
|
-
const mongoBase = new BaseRepositoryMongoDB({ prismaClient: prisma });
|
|
23
|
-
this.prisma = mongoBase.prisma;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Create a token with expiration
|
|
28
|
-
* Replaces: Token.createTokenWithExpire(userId, rawToken, minutes)
|
|
29
|
-
*
|
|
30
|
-
* @param {string} userId - The user ID
|
|
31
|
-
* @param {string} rawToken - The raw (unhashed) token string
|
|
32
|
-
* @param {number} minutes - Minutes until expiration
|
|
33
|
-
* @returns {Promise<Object>} The created token record with string IDs
|
|
34
|
-
*/
|
|
35
|
-
async createTokenWithExpire(userId, rawToken, minutes) {
|
|
36
|
-
// Hash the token with bcrypt
|
|
37
|
-
const tokenHash = await bcrypt.hash(rawToken, BCRYPT_ROUNDS);
|
|
38
|
-
|
|
39
|
-
// Calculate expiration time
|
|
40
|
-
const expires = new Date(Date.now() + minutes * 60000);
|
|
41
|
-
|
|
42
|
-
return await this.prisma.token.create({
|
|
43
|
-
data: {
|
|
44
|
-
token: tokenHash,
|
|
45
|
-
expires,
|
|
46
|
-
userId,
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Validate and retrieve token from JSON token object
|
|
53
|
-
* Replaces: Token.validateAndGetTokenFromJSONToken(tokenObj)
|
|
54
|
-
*
|
|
55
|
-
* @param {Object} tokenObj - Object with id and token properties
|
|
56
|
-
* @returns {Promise<Object>} The validated token record with string IDs
|
|
57
|
-
* @throws {Error} If token is invalid, expired, or doesn't exist
|
|
58
|
-
*/
|
|
59
|
-
async validateAndGetToken(tokenObj) {
|
|
60
|
-
const sessionToken = await this.prisma.token.findUnique({
|
|
61
|
-
where: { id: tokenObj.id },
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
if (!sessionToken) {
|
|
65
|
-
throw new Error('Invalid Token: Token does not exist');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Verify token hash matches
|
|
69
|
-
const isValid = await bcrypt.compare(
|
|
70
|
-
tokenObj.token,
|
|
71
|
-
sessionToken.token
|
|
72
|
-
);
|
|
73
|
-
if (!isValid) {
|
|
74
|
-
throw new Error('Invalid Token: Token does not match');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Check if token is expired
|
|
78
|
-
if (
|
|
79
|
-
sessionToken.expires &&
|
|
80
|
-
new Date(sessionToken.expires) < new Date()
|
|
81
|
-
) {
|
|
82
|
-
throw new Error('Invalid Token: Token is expired');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return sessionToken;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Find a token by ID
|
|
90
|
-
* Replaces: Token.findById(tokenId)
|
|
91
|
-
*
|
|
92
|
-
* @param {string} tokenId - The token ID
|
|
93
|
-
* @returns {Promise<Object|null>} The token record with string IDs or null
|
|
94
|
-
*/
|
|
95
|
-
async findTokenById(tokenId) {
|
|
96
|
-
return await this.prisma.token.findUnique({
|
|
97
|
-
where: { id: tokenId },
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Find tokens by user ID
|
|
103
|
-
* Replaces: Token.find({ user: userId })
|
|
104
|
-
*
|
|
105
|
-
* @param {string} userId - The user ID
|
|
106
|
-
* @returns {Promise<Array>} Array of token records with string IDs
|
|
107
|
-
*/
|
|
108
|
-
async findTokensByUserId(userId) {
|
|
109
|
-
return await this.prisma.token.findMany({
|
|
110
|
-
where: { userId },
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Delete a token by ID
|
|
116
|
-
* Replaces: Token.deleteOne({ _id: tokenId })
|
|
117
|
-
*
|
|
118
|
-
* @param {string} tokenId - The token ID
|
|
119
|
-
* @returns {Promise<Object>} The deletion result
|
|
120
|
-
*/
|
|
121
|
-
async deleteToken(tokenId) {
|
|
122
|
-
try {
|
|
123
|
-
await this.prisma.token.delete({
|
|
124
|
-
where: { id: tokenId },
|
|
125
|
-
});
|
|
126
|
-
return { acknowledged: true, deletedCount: 1 };
|
|
127
|
-
} catch (error) {
|
|
128
|
-
if (error.code === 'P2025') {
|
|
129
|
-
// Record not found
|
|
130
|
-
return { acknowledged: true, deletedCount: 0 };
|
|
131
|
-
}
|
|
132
|
-
throw error;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Delete expired tokens
|
|
138
|
-
* Replaces: Token.deleteMany({ expires: { $lt: new Date() } })
|
|
139
|
-
*
|
|
140
|
-
* @returns {Promise<Object>} The deletion result with count
|
|
141
|
-
*/
|
|
142
|
-
async deleteExpiredTokens() {
|
|
143
|
-
const result = await this.prisma.token.deleteMany({
|
|
144
|
-
where: {
|
|
145
|
-
expires: {
|
|
146
|
-
lt: new Date(),
|
|
147
|
-
},
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
acknowledged: true,
|
|
153
|
-
deletedCount: result.count,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Delete all tokens for a user
|
|
159
|
-
* Replaces: Token.deleteMany({ user: userId })
|
|
160
|
-
*
|
|
161
|
-
* @param {string} userId - The user ID
|
|
162
|
-
* @returns {Promise<Object>} The deletion result
|
|
163
|
-
*/
|
|
164
|
-
async deleteTokensByUserId(userId) {
|
|
165
|
-
const result = await this.prisma.token.deleteMany({
|
|
166
|
-
where: { userId },
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
acknowledged: true,
|
|
171
|
-
deletedCount: result.count,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Create JSON token string from token object and raw token
|
|
177
|
-
* Replaces: Token.createJSONToken(token, rawToken)
|
|
178
|
-
*
|
|
179
|
-
* @param {Object} token - The token record
|
|
180
|
-
* @param {string} rawToken - The raw token string
|
|
181
|
-
* @returns {string} JSON string with id and token
|
|
182
|
-
*/
|
|
183
|
-
createJSONToken(token, rawToken) {
|
|
184
|
-
return JSON.stringify({
|
|
185
|
-
id: token.id,
|
|
186
|
-
token: rawToken,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Create base64 encoded buffer token
|
|
192
|
-
* Replaces: Token.createBase64BufferToken(token, rawToken)
|
|
193
|
-
*
|
|
194
|
-
* @param {Object} token - The token record
|
|
195
|
-
* @param {string} rawToken - The raw token string
|
|
196
|
-
* @returns {string} Base64 encoded token
|
|
197
|
-
*/
|
|
198
|
-
createBase64BufferToken(token, rawToken) {
|
|
199
|
-
const jsonVal = this.createJSONToken(token, rawToken);
|
|
200
|
-
return Buffer.from(jsonVal).toString('base64');
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Parse JSON token from base64 buffer
|
|
205
|
-
* Replaces: Token.getJSONTokenFromBase64BufferToken(buffer)
|
|
206
|
-
*
|
|
207
|
-
* @param {string} buffer - Base64 encoded token string
|
|
208
|
-
* @returns {Object} Parsed token object with id and token
|
|
209
|
-
*/
|
|
210
|
-
getJSONTokenFromBase64BufferToken(buffer) {
|
|
211
|
-
const tokenStr = Buffer.from(buffer.trim(), 'base64').toString('ascii');
|
|
212
|
-
return JSON.parse(tokenStr);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
module.exports = { TokenRepositoryMongo };
|
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
const bcrypt = require('bcryptjs');
|
|
2
|
-
const { prisma } = require('../../database/prisma');
|
|
3
|
-
const {
|
|
4
|
-
createTokenRepository,
|
|
5
|
-
} = require('../../token/repositories/token-repository-factory');
|
|
6
|
-
const { UserRepositoryInterface } = require('./user-repository-interface');
|
|
7
|
-
const { BaseRepositoryMongoDB } = require('../../database/repositories/base-repository-mongodb');
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* MongoDB User Repository Adapter
|
|
11
|
-
* Handles user operations with discriminator pattern support
|
|
12
|
-
*
|
|
13
|
-
* MongoDB-specific characteristics:
|
|
14
|
-
* - Uses String IDs (ObjectId)
|
|
15
|
-
* - No ID conversion needed (IDs are already strings)
|
|
16
|
-
* - IndividualUser/OrganizationUser discriminators → User model with type field
|
|
17
|
-
*/
|
|
18
|
-
class UserRepositoryMongo extends UserRepositoryInterface {
|
|
19
|
-
constructor() {
|
|
20
|
-
super();
|
|
21
|
-
|
|
22
|
-
// Use MongoDB base repository for DocumentDB compatibility
|
|
23
|
-
const mongoBase = new BaseRepositoryMongoDB({ prismaClient: prisma });
|
|
24
|
-
this.prisma = mongoBase.prisma;
|
|
25
|
-
this.tokenRepository = createTokenRepository();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Get session token from base64 buffer token
|
|
30
|
-
* Delegates to TokenRepository
|
|
31
|
-
*
|
|
32
|
-
* @param {string} token - Base64 buffer token
|
|
33
|
-
* @returns {Promise<Object>} Session token object with string IDs
|
|
34
|
-
*/
|
|
35
|
-
async getSessionToken(token) {
|
|
36
|
-
const jsonToken =
|
|
37
|
-
this.tokenRepository.getJSONTokenFromBase64BufferToken(token);
|
|
38
|
-
const sessionToken = await this.tokenRepository.validateAndGetToken(
|
|
39
|
-
jsonToken
|
|
40
|
-
);
|
|
41
|
-
return sessionToken;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Find organization user by ID
|
|
46
|
-
* Replaces: OrganizationUser.findById(userId)
|
|
47
|
-
*
|
|
48
|
-
* @param {string} userId - User ID
|
|
49
|
-
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
50
|
-
*/
|
|
51
|
-
async findOrganizationUserById(userId) {
|
|
52
|
-
return await this.prisma.user.findFirst({
|
|
53
|
-
where: {
|
|
54
|
-
id: userId,
|
|
55
|
-
type: 'ORGANIZATION',
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Find individual user by ID
|
|
62
|
-
* Replaces: IndividualUser.findById(userId)
|
|
63
|
-
*
|
|
64
|
-
* @param {string} userId - User ID
|
|
65
|
-
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
66
|
-
*/
|
|
67
|
-
async findIndividualUserById(userId) {
|
|
68
|
-
return await this.prisma.user.findFirst({
|
|
69
|
-
where: {
|
|
70
|
-
id: userId,
|
|
71
|
-
type: 'INDIVIDUAL',
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Create token with expiration
|
|
78
|
-
* Delegates to TokenRepository
|
|
79
|
-
*
|
|
80
|
-
* @param {string} userId - User ID
|
|
81
|
-
* @param {string} rawToken - Raw unhashed token
|
|
82
|
-
* @param {number} minutes - Minutes until expiration (default 120)
|
|
83
|
-
* @returns {Promise<string>} Base64 buffer token
|
|
84
|
-
*/
|
|
85
|
-
async createToken(userId, rawToken, minutes = 120) {
|
|
86
|
-
const createdToken = await this.tokenRepository.createTokenWithExpire(
|
|
87
|
-
userId,
|
|
88
|
-
rawToken,
|
|
89
|
-
minutes
|
|
90
|
-
);
|
|
91
|
-
return this.tokenRepository.createBase64BufferToken(
|
|
92
|
-
createdToken,
|
|
93
|
-
rawToken
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Create individual user
|
|
99
|
-
* Replaces: IndividualUser.create(params)
|
|
100
|
-
*
|
|
101
|
-
* @param {Object} params - User creation parameters
|
|
102
|
-
* @param {string} [params.hashword] - Plain text password (will be bcrypt hashed automatically)
|
|
103
|
-
* @returns {Promise<Object>} Created user object with string IDs
|
|
104
|
-
*/
|
|
105
|
-
async createIndividualUser(params) {
|
|
106
|
-
const data = {
|
|
107
|
-
type: 'INDIVIDUAL',
|
|
108
|
-
email: params.email,
|
|
109
|
-
username: params.username,
|
|
110
|
-
appUserId: params.appUserId,
|
|
111
|
-
organizationId: params.organization || params.organizationId,
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
params.hashword !== undefined &&
|
|
116
|
-
params.hashword !== null &&
|
|
117
|
-
params.hashword !== ''
|
|
118
|
-
) {
|
|
119
|
-
if (typeof params.hashword !== 'string') {
|
|
120
|
-
throw new Error('Password must be a string');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
|
|
124
|
-
if (params.hashword.startsWith('$2')) {
|
|
125
|
-
throw new Error(
|
|
126
|
-
'Password appears to be already hashed. Pass plain text password only.'
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
data.hashword = await bcrypt.hash(params.hashword, 10);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return await this.prisma.user.create({ data });
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Create organization user
|
|
138
|
-
* Replaces: OrganizationUser.create(params)
|
|
139
|
-
*
|
|
140
|
-
* @param {Object} params - Organization creation parameters
|
|
141
|
-
* @returns {Promise<Object>} Created organization object with string IDs
|
|
142
|
-
*/
|
|
143
|
-
async createOrganizationUser(params) {
|
|
144
|
-
return await this.prisma.user.create({
|
|
145
|
-
data: {
|
|
146
|
-
type: 'ORGANIZATION',
|
|
147
|
-
appOrgId: params.appOrgId,
|
|
148
|
-
name: params.name,
|
|
149
|
-
},
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Find individual user by username
|
|
155
|
-
* Replaces: IndividualUser.findOne({ username })
|
|
156
|
-
*
|
|
157
|
-
* @param {string} username - Username to search for
|
|
158
|
-
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
159
|
-
*/
|
|
160
|
-
async findIndividualUserByUsername(username) {
|
|
161
|
-
return await this.prisma.user.findFirst({
|
|
162
|
-
where: {
|
|
163
|
-
type: 'INDIVIDUAL',
|
|
164
|
-
username,
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Find individual user by app user ID
|
|
171
|
-
* Replaces: IndividualUser.getUserByAppUserId(appUserId)
|
|
172
|
-
*
|
|
173
|
-
* @param {string} appUserId - App user ID to search for
|
|
174
|
-
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
175
|
-
*/
|
|
176
|
-
async findIndividualUserByAppUserId(appUserId) {
|
|
177
|
-
return await this.prisma.user.findFirst({
|
|
178
|
-
where: {
|
|
179
|
-
type: 'INDIVIDUAL',
|
|
180
|
-
appUserId,
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Find organization user by app org ID
|
|
187
|
-
* Replaces: OrganizationUser.getUserByAppOrgId(appOrgId)
|
|
188
|
-
*
|
|
189
|
-
* @param {string} appOrgId - App organization ID to search for
|
|
190
|
-
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
191
|
-
*/
|
|
192
|
-
async findOrganizationUserByAppOrgId(appOrgId) {
|
|
193
|
-
return await this.prisma.user.findFirst({
|
|
194
|
-
where: {
|
|
195
|
-
type: 'ORGANIZATION',
|
|
196
|
-
appOrgId,
|
|
197
|
-
},
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Find user by ID (any type)
|
|
203
|
-
* @param {string} userId - User ID
|
|
204
|
-
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
205
|
-
*/
|
|
206
|
-
async findUserById(userId) {
|
|
207
|
-
return await this.prisma.user.findUnique({
|
|
208
|
-
where: { id: userId },
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Find individual user by email
|
|
214
|
-
* @param {string} email - Email to search for
|
|
215
|
-
* @returns {Promise<Object|null>} User object with string IDs or null
|
|
216
|
-
*/
|
|
217
|
-
async findIndividualUserByEmail(email) {
|
|
218
|
-
return await this.prisma.user.findFirst({
|
|
219
|
-
where: {
|
|
220
|
-
type: 'INDIVIDUAL',
|
|
221
|
-
email,
|
|
222
|
-
},
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Update individual user
|
|
228
|
-
* @param {string} userId - User ID
|
|
229
|
-
* @param {Object} updates - Fields to update
|
|
230
|
-
* @param {string} [updates.hashword] - Plain text password (will be bcrypt hashed automatically)
|
|
231
|
-
* @returns {Promise<Object>} Updated user object with string IDs
|
|
232
|
-
*/
|
|
233
|
-
async updateIndividualUser(userId, updates) {
|
|
234
|
-
const data = { ...updates };
|
|
235
|
-
|
|
236
|
-
if (
|
|
237
|
-
data.hashword !== undefined &&
|
|
238
|
-
data.hashword !== null &&
|
|
239
|
-
data.hashword !== ''
|
|
240
|
-
) {
|
|
241
|
-
if (typeof data.hashword !== 'string') {
|
|
242
|
-
throw new Error('Password must be a string');
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Prevent double-hashing: bcrypt hashes start with $2a$ or $2b$
|
|
246
|
-
if (data.hashword.startsWith('$2')) {
|
|
247
|
-
throw new Error(
|
|
248
|
-
'Password appears to be already hashed. Pass plain text password only.'
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
data.hashword = await bcrypt.hash(data.hashword, 10);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return await this.prisma.user.update({
|
|
256
|
-
where: { id: userId },
|
|
257
|
-
data,
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Update organization user
|
|
263
|
-
* @param {string} userId - User ID
|
|
264
|
-
* @param {Object} updates - Fields to update
|
|
265
|
-
* @returns {Promise<Object>} Updated user object with string IDs
|
|
266
|
-
*/
|
|
267
|
-
async updateOrganizationUser(userId, updates) {
|
|
268
|
-
return await this.prisma.user.update({
|
|
269
|
-
where: { id: userId },
|
|
270
|
-
data: updates,
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Delete user by ID
|
|
276
|
-
* @param {string} userId - User ID to delete
|
|
277
|
-
* @returns {Promise<boolean>} True if deleted successfully
|
|
278
|
-
*/
|
|
279
|
-
async deleteUser(userId) {
|
|
280
|
-
try {
|
|
281
|
-
await this.prisma.user.delete({
|
|
282
|
-
where: { id: userId },
|
|
283
|
-
});
|
|
284
|
-
return true;
|
|
285
|
-
} catch (error) {
|
|
286
|
-
if (error.code === 'P2025') {
|
|
287
|
-
// Record not found
|
|
288
|
-
return false;
|
|
289
|
-
}
|
|
290
|
-
throw error;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
module.exports = { UserRepositoryMongo };
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
const { prisma } = require('../../database/prisma');
|
|
2
|
-
const {
|
|
3
|
-
ApiGatewayManagementApiClient,
|
|
4
|
-
PostToConnectionCommand,
|
|
5
|
-
} = require('@aws-sdk/client-apigatewaymanagementapi');
|
|
6
|
-
const {
|
|
7
|
-
WebsocketConnectionRepositoryInterface,
|
|
8
|
-
} = require('./websocket-connection-repository-interface');
|
|
9
|
-
const { BaseRepositoryMongoDB } = require('../../database/repositories/base-repository-mongodb');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* MongoDB WebSocket Connection Repository Adapter
|
|
13
|
-
* Handles persistence of active WebSocket connections
|
|
14
|
-
*
|
|
15
|
-
* MongoDB-specific characteristics:
|
|
16
|
-
* - Uses String IDs (ObjectId)
|
|
17
|
-
* - No ID conversion needed (IDs are already strings)
|
|
18
|
-
* - AWS API Gateway Management API integration preserved
|
|
19
|
-
*/
|
|
20
|
-
class WebsocketConnectionRepositoryMongo extends WebsocketConnectionRepositoryInterface {
|
|
21
|
-
constructor() {
|
|
22
|
-
super();
|
|
23
|
-
|
|
24
|
-
// Use MongoDB base repository for DocumentDB compatibility
|
|
25
|
-
const mongoBase = new BaseRepositoryMongoDB({ prismaClient: prisma });
|
|
26
|
-
this.prisma = mongoBase.prisma;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Create a new WebSocket connection record
|
|
31
|
-
* Replaces: WebsocketConnection.create({ connectionId })
|
|
32
|
-
*
|
|
33
|
-
* @param {string} connectionId - The WebSocket connection ID
|
|
34
|
-
* @returns {Promise<Object>} The created connection record with string IDs
|
|
35
|
-
*/
|
|
36
|
-
async createConnection(connectionId) {
|
|
37
|
-
return await this.prisma.websocketConnection.create({
|
|
38
|
-
data: { connectionId },
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Delete a WebSocket connection record
|
|
44
|
-
* Replaces: WebsocketConnection.deleteOne({ connectionId })
|
|
45
|
-
*
|
|
46
|
-
* @param {string} connectionId - The WebSocket connection ID to delete
|
|
47
|
-
* @returns {Promise<Object>} The deletion result
|
|
48
|
-
*/
|
|
49
|
-
async deleteConnection(connectionId) {
|
|
50
|
-
try {
|
|
51
|
-
await this.prisma.websocketConnection.delete({
|
|
52
|
-
where: { connectionId },
|
|
53
|
-
});
|
|
54
|
-
return { acknowledged: true, deletedCount: 1 };
|
|
55
|
-
} catch (error) {
|
|
56
|
-
if (error.code === 'P2025') {
|
|
57
|
-
// Record not found
|
|
58
|
-
return { acknowledged: true, deletedCount: 0 };
|
|
59
|
-
}
|
|
60
|
-
throw error;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Get all active WebSocket connections with send capability
|
|
66
|
-
* Replaces: WebsocketConnection.getActiveConnections()
|
|
67
|
-
*
|
|
68
|
-
* @returns {Promise<Array>} Array of active connection objects with send capability
|
|
69
|
-
*/
|
|
70
|
-
async getActiveConnections() {
|
|
71
|
-
try {
|
|
72
|
-
// Return empty array if websockets are not configured
|
|
73
|
-
if (!process.env.WEBSOCKET_API_ENDPOINT) {
|
|
74
|
-
return [];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const connections = await this.prisma.websocketConnection.findMany({
|
|
78
|
-
select: { connectionId: true },
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
return connections.map((conn) => ({
|
|
82
|
-
connectionId: conn.connectionId,
|
|
83
|
-
send: async (data) => {
|
|
84
|
-
const apigwManagementApi = new ApiGatewayManagementApiClient({
|
|
85
|
-
endpoint: process.env.WEBSOCKET_API_ENDPOINT,
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const command = new PostToConnectionCommand({
|
|
90
|
-
ConnectionId: conn.connectionId,
|
|
91
|
-
Data: JSON.stringify(data),
|
|
92
|
-
});
|
|
93
|
-
await apigwManagementApi.send(command);
|
|
94
|
-
} catch (error) {
|
|
95
|
-
if (error.statusCode === 410 || error.$metadata?.httpStatusCode === 410) {
|
|
96
|
-
console.log(
|
|
97
|
-
`Stale connection ${conn.connectionId}`
|
|
98
|
-
);
|
|
99
|
-
// Delete stale connection
|
|
100
|
-
await this.prisma.websocketConnection.deleteMany({
|
|
101
|
-
where: { connectionId: conn.connectionId },
|
|
102
|
-
});
|
|
103
|
-
} else {
|
|
104
|
-
throw error;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
}));
|
|
109
|
-
} catch (error) {
|
|
110
|
-
console.error('Error getting active connections:', error);
|
|
111
|
-
throw error;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Find a connection by connection ID
|
|
117
|
-
* Replaces: WebsocketConnection.findOne({ connectionId })
|
|
118
|
-
*
|
|
119
|
-
* @param {string} connectionId - The WebSocket connection ID
|
|
120
|
-
* @returns {Promise<Object|null>} The connection record with string IDs or null
|
|
121
|
-
*/
|
|
122
|
-
async findConnection(connectionId) {
|
|
123
|
-
return await this.prisma.websocketConnection.findFirst({
|
|
124
|
-
where: { connectionId },
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Find connection by internal ID
|
|
130
|
-
* @param {string} id - The internal connection ID
|
|
131
|
-
* @returns {Promise<Object|null>} The connection record with string IDs or null
|
|
132
|
-
*/
|
|
133
|
-
async findConnectionById(id) {
|
|
134
|
-
return await this.prisma.websocketConnection.findUnique({
|
|
135
|
-
where: { id },
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Get all connections
|
|
141
|
-
* @returns {Promise<Array>} Array of all connection records with string IDs
|
|
142
|
-
*/
|
|
143
|
-
async getAllConnections() {
|
|
144
|
-
return await this.prisma.websocketConnection.findMany();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Delete all connections
|
|
149
|
-
* @returns {Promise<Object>} The deletion result
|
|
150
|
-
*/
|
|
151
|
-
async deleteAllConnections() {
|
|
152
|
-
const result = await this.prisma.websocketConnection.deleteMany();
|
|
153
|
-
return {
|
|
154
|
-
acknowledged: true,
|
|
155
|
-
deletedCount: result.count,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
module.exports = { WebsocketConnectionRepositoryMongo };
|