propro-utils 1.7.1 → 1.7.3
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/middlewares/account_info.js +149 -129
- package/package.json +1 -2
- package/src/server/index.js +1 -1
- package/src/server/middleware/cookieUtils.js +76 -112
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
require(
|
|
2
|
-
const axios = require(
|
|
3
|
-
const { getOrSetCache } = require(
|
|
4
|
-
const { v4: uuidv4 } = require(
|
|
5
|
-
const ServiceManager = require(
|
|
1
|
+
require('dotenv').config();
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const { getOrSetCache } = require('../utils/redis');
|
|
4
|
+
const { v4: uuidv4 } = require('uuid');
|
|
5
|
+
const ServiceManager = require('../utils/serviceManager');
|
|
6
6
|
const defaultUserGlobalStyleShortcuts =
|
|
7
|
-
require(
|
|
8
|
-
const defaultFolders = require(
|
|
7
|
+
require('./defaultUserGlobalStyleShortcuts.json').defaultGlobalStyleShortcuts;
|
|
8
|
+
const defaultFolders = require('./defaultFolders.json').defaultFolders;
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Retrieves the account profile data from the authentication server and caches it using Redis.
|
|
@@ -20,10 +20,10 @@ const defaultFolders = require("./defaultFolders.json").defaultFolders;
|
|
|
20
20
|
const getAccountProfile = async (redisClient, userSchema, accountId) => {
|
|
21
21
|
try {
|
|
22
22
|
const accessToken =
|
|
23
|
-
req.cookies[
|
|
23
|
+
req.cookies['x-access-token'] || req.headers.authorization?.split(' ')[1];
|
|
24
24
|
|
|
25
25
|
if (!accessToken) {
|
|
26
|
-
throw new Error(
|
|
26
|
+
throw new Error('Access token is required');
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
const fetchPermission = async () => {
|
|
@@ -49,7 +49,7 @@ const getAccountProfile = async (redisClient, userSchema, accountId) => {
|
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
if (!profileData) {
|
|
52
|
-
throw new Error(
|
|
52
|
+
throw new Error('Invalid permissions');
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
return profileData;
|
|
@@ -57,10 +57,58 @@ const getAccountProfile = async (redisClient, userSchema, accountId) => {
|
|
|
57
57
|
if (error.response && error.response.status) {
|
|
58
58
|
throw new Error(error.response.data.message);
|
|
59
59
|
}
|
|
60
|
-
throw new Error(
|
|
60
|
+
throw new Error('Error validating token');
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Consolidates multiple UserGlobalStyles documents for an account into one
|
|
66
|
+
* @param {string} accountId - The account ID to consolidate styles for
|
|
67
|
+
* @returns {Promise<Object>} The consolidated UserGlobalStyles document
|
|
68
|
+
*/
|
|
69
|
+
async function consolidateGlobalStyles(accountId) {
|
|
70
|
+
const userStyleSchema = await ServiceManager.getService('UserStyleSchema');
|
|
71
|
+
|
|
72
|
+
// Find all global styles for this account
|
|
73
|
+
const allStyles = await userStyleSchema
|
|
74
|
+
.find({ accountId })
|
|
75
|
+
.sort({ createdAt: 1 });
|
|
76
|
+
|
|
77
|
+
if (allStyles.length <= 1) {
|
|
78
|
+
return allStyles[0];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Use the first document as the main one and merge shortcuts from others
|
|
82
|
+
const mainStyle = allStyles[0];
|
|
83
|
+
const uniqueShortcuts = new Map();
|
|
84
|
+
|
|
85
|
+
// Merge all shortcuts, keeping the latest version of each shortcut by ID
|
|
86
|
+
allStyles.forEach(style => {
|
|
87
|
+
style.styleShortcuts.forEach(shortcut => {
|
|
88
|
+
uniqueShortcuts.set(shortcut.id.toString(), shortcut);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Update the main style document with consolidated shortcuts
|
|
93
|
+
mainStyle.styleShortcuts = Array.from(uniqueShortcuts.values());
|
|
94
|
+
await mainStyle.save();
|
|
95
|
+
|
|
96
|
+
// Delete other style documents
|
|
97
|
+
await userStyleSchema.deleteMany({
|
|
98
|
+
accountId,
|
|
99
|
+
_id: { $ne: mainStyle._id },
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Update user reference if needed
|
|
103
|
+
const userSchema = await ServiceManager.getService('UserSchema');
|
|
104
|
+
await userSchema.updateMany(
|
|
105
|
+
{ accountId },
|
|
106
|
+
{ userGlobalStyles: mainStyle._id }
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
return mainStyle;
|
|
110
|
+
}
|
|
111
|
+
|
|
64
112
|
/**
|
|
65
113
|
* Creates a new user global styles document with default style shortcuts.
|
|
66
114
|
*
|
|
@@ -69,6 +117,13 @@ const getAccountProfile = async (redisClient, userSchema, accountId) => {
|
|
|
69
117
|
* @returns {Promise<Object>} A promise that resolves to the created user global styles object.
|
|
70
118
|
*/
|
|
71
119
|
async function createUserGlobalStyles(userStyleSchema, accountId) {
|
|
120
|
+
// First check if any existing styles need to be consolidated
|
|
121
|
+
const existingStyles = await userStyleSchema.find({ accountId });
|
|
122
|
+
|
|
123
|
+
if (existingStyles.length > 0) {
|
|
124
|
+
return await consolidateGlobalStyles(accountId);
|
|
125
|
+
}
|
|
126
|
+
|
|
72
127
|
return await userStyleSchema.create({
|
|
73
128
|
id: uuidv4(),
|
|
74
129
|
styleShortcuts: defaultUserGlobalStyleShortcuts,
|
|
@@ -85,8 +140,8 @@ async function createUserGlobalStyles(userStyleSchema, accountId) {
|
|
|
85
140
|
*/
|
|
86
141
|
async function createDefaultFolders(folderSchema, accountId) {
|
|
87
142
|
try {
|
|
88
|
-
console.log(
|
|
89
|
-
const folderPromises = defaultFolders.map(
|
|
143
|
+
console.log('Creating default folders for user:', accountId);
|
|
144
|
+
const folderPromises = defaultFolders.map(folder =>
|
|
90
145
|
folderSchema.create({
|
|
91
146
|
...folder,
|
|
92
147
|
user_id: accountId,
|
|
@@ -94,17 +149,17 @@ async function createDefaultFolders(folderSchema, accountId) {
|
|
|
94
149
|
);
|
|
95
150
|
return Promise.all(folderPromises);
|
|
96
151
|
} catch (error) {
|
|
97
|
-
console.error(
|
|
98
|
-
throw new Error(
|
|
152
|
+
console.error('Error in createDefaultFolders:', error);
|
|
153
|
+
throw new Error('Failed to create default folders');
|
|
99
154
|
}
|
|
100
155
|
}
|
|
101
156
|
|
|
102
157
|
const DEFAULT_THEME = {
|
|
103
|
-
canvasBackground:
|
|
158
|
+
canvasBackground: '#1E1D1D',
|
|
104
159
|
defaultItemWidth: 200,
|
|
105
|
-
defaultColor:
|
|
106
|
-
fontSize:
|
|
107
|
-
name:
|
|
160
|
+
defaultColor: '#ffffff',
|
|
161
|
+
fontSize: '16px',
|
|
162
|
+
name: 'Default Theme',
|
|
108
163
|
isDefault: true,
|
|
109
164
|
};
|
|
110
165
|
|
|
@@ -116,152 +171,79 @@ const DEFAULT_THEME = {
|
|
|
116
171
|
* @returns {Promise<Object>} A promise that resolves to the complete user object
|
|
117
172
|
* @throws {Error} If there's an issue with database operations or invalid input
|
|
118
173
|
*/
|
|
119
|
-
const checkIfUserExists = async
|
|
120
|
-
console.log("Starting checkIfUserExists for accountId:", accountId);
|
|
121
|
-
|
|
174
|
+
const checkIfUserExists = async accountId => {
|
|
122
175
|
// Input validation
|
|
123
|
-
if (!accountId || typeof accountId !==
|
|
124
|
-
console.warn(
|
|
125
|
-
throw new Error(
|
|
176
|
+
if (!accountId || typeof accountId !== 'string') {
|
|
177
|
+
console.warn('Invalid accountId provided:', accountId);
|
|
178
|
+
throw new Error('Invalid accountId provided');
|
|
126
179
|
}
|
|
127
180
|
|
|
128
181
|
try {
|
|
129
|
-
console.log("Loading schemas...");
|
|
130
|
-
// Load all required schemas in parallel
|
|
131
182
|
const schemaResults = await Promise.all([
|
|
132
|
-
ServiceManager.getService(
|
|
133
|
-
ServiceManager.getService(
|
|
134
|
-
ServiceManager.getService(
|
|
135
|
-
ServiceManager.getService(
|
|
183
|
+
ServiceManager.getService('UserSchema'),
|
|
184
|
+
ServiceManager.getService('UserStyleSchema'),
|
|
185
|
+
ServiceManager.getService('FolderSchema'),
|
|
186
|
+
ServiceManager.getService('ThemeSchema'),
|
|
136
187
|
]);
|
|
137
188
|
|
|
138
189
|
const [userSchema, userStyleSchema, folderSchema, themeSchema] =
|
|
139
190
|
schemaResults;
|
|
140
191
|
|
|
141
|
-
// Log the status of each schema
|
|
142
|
-
console.log("Schema loading results:", {
|
|
143
|
-
userSchema: userSchema ? "Loaded" : "Not available",
|
|
144
|
-
userStyleSchema: userStyleSchema ? "Loaded" : "Not available",
|
|
145
|
-
folderSchema: folderSchema ? "Loaded" : "Not available",
|
|
146
|
-
themeSchema: themeSchema ? "Loaded" : "Not available",
|
|
147
|
-
});
|
|
148
|
-
|
|
149
192
|
if (!userSchema) {
|
|
150
|
-
throw new Error(
|
|
193
|
+
throw new Error('UserSchema service not available');
|
|
151
194
|
}
|
|
152
195
|
|
|
153
196
|
// Optional schemas warning
|
|
154
197
|
if (!userStyleSchema) {
|
|
155
198
|
console.warn(
|
|
156
|
-
|
|
199
|
+
'UserStyleSchema service not available - style features will be skipped'
|
|
157
200
|
);
|
|
158
201
|
}
|
|
159
202
|
if (!folderSchema) {
|
|
160
203
|
console.warn(
|
|
161
|
-
|
|
204
|
+
'FolderSchema service not available - folder features will be skipped'
|
|
162
205
|
);
|
|
163
206
|
}
|
|
164
207
|
if (!themeSchema) {
|
|
165
208
|
console.warn(
|
|
166
|
-
|
|
209
|
+
'ThemeSchema service not available - theme features will be skipped'
|
|
167
210
|
);
|
|
168
211
|
}
|
|
169
212
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
console.log("User lookup result:", user ? "User found" : "User not found");
|
|
213
|
+
// Find existing user with populated fields
|
|
214
|
+
const user = await userSchema
|
|
215
|
+
.findOne({ accountId })
|
|
216
|
+
.select('-theme -folderList -userGlobalStyles -mapList')
|
|
217
|
+
.lean();
|
|
176
218
|
|
|
177
219
|
if (user) {
|
|
178
|
-
console.log("Updating existing user data...");
|
|
179
220
|
const updates = [];
|
|
180
221
|
|
|
181
|
-
// Handle userGlobalStyles
|
|
222
|
+
// Handle userGlobalStyles
|
|
182
223
|
if (userStyleSchema) {
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (hasStyleReference) {
|
|
187
|
-
// Verify the referenced style document actually exists
|
|
188
|
-
const styleExists = await userStyleSchema.exists({
|
|
189
|
-
_id: user.userGlobalStyles,
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
if (!styleExists) {
|
|
193
|
-
console.log(
|
|
194
|
-
"User has style reference but document not found, creating new styles..."
|
|
195
|
-
);
|
|
196
|
-
updates.push(
|
|
197
|
-
createUserGlobalStyles(userStyleSchema, accountId).then(
|
|
198
|
-
async (styles) => {
|
|
199
|
-
console.log("UserGlobalStyles created successfully");
|
|
200
|
-
await userSchema.updateOne(
|
|
201
|
-
{ _id: user._id },
|
|
202
|
-
{ userGlobalStyles: styles._id }
|
|
203
|
-
);
|
|
204
|
-
return styles;
|
|
205
|
-
}
|
|
206
|
-
)
|
|
207
|
-
);
|
|
208
|
-
} else {
|
|
209
|
-
// Style exists, check if it has shortcuts
|
|
210
|
-
const styleDoc = await userStyleSchema.findById(
|
|
211
|
-
user.userGlobalStyles
|
|
212
|
-
);
|
|
213
|
-
if (
|
|
214
|
-
!styleDoc.styleShortcuts ||
|
|
215
|
-
styleDoc.styleShortcuts.length === 0
|
|
216
|
-
) {
|
|
217
|
-
console.log("Updating empty styleShortcuts with defaults...");
|
|
218
|
-
updates.push(
|
|
219
|
-
userStyleSchema.updateOne(
|
|
220
|
-
{ _id: user.userGlobalStyles },
|
|
221
|
-
{ styleShortcuts: defaultUserGlobalStyleShortcuts }
|
|
222
|
-
)
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
} else {
|
|
227
|
-
// No style reference, create new styles
|
|
228
|
-
console.log("Creating missing userGlobalStyles...");
|
|
229
|
-
updates.push(
|
|
230
|
-
createUserGlobalStyles(userStyleSchema, accountId).then(
|
|
231
|
-
async (styles) => {
|
|
232
|
-
console.log("UserGlobalStyles created successfully");
|
|
233
|
-
await userSchema.updateOne(
|
|
234
|
-
{ _id: user._id },
|
|
235
|
-
{ userGlobalStyles: styles._id }
|
|
236
|
-
);
|
|
237
|
-
return styles;
|
|
238
|
-
}
|
|
239
|
-
)
|
|
240
|
-
);
|
|
241
|
-
}
|
|
224
|
+
// Consolidate any duplicate global styles
|
|
225
|
+
await consolidateGlobalStyles(accountId);
|
|
242
226
|
}
|
|
243
227
|
|
|
244
228
|
// Handle folders
|
|
245
229
|
if (folderSchema) {
|
|
246
|
-
console.log("Checking folders...");
|
|
247
230
|
updates.push(
|
|
248
231
|
(async () => {
|
|
249
232
|
const existingFolders = await folderSchema
|
|
250
233
|
.find({ user_id: user.id })
|
|
251
|
-
.select(
|
|
234
|
+
.select('name')
|
|
252
235
|
.lean();
|
|
253
236
|
|
|
254
|
-
console.log("Existing folders count:", existingFolders.length);
|
|
255
237
|
const existingFolderNames = new Set(
|
|
256
|
-
existingFolders.map(
|
|
238
|
+
existingFolders.map(f => f.name)
|
|
257
239
|
);
|
|
258
240
|
const foldersToCreate = defaultFolders.filter(
|
|
259
|
-
|
|
241
|
+
folder => !existingFolderNames.has(folder.name)
|
|
260
242
|
);
|
|
261
243
|
|
|
262
244
|
if (foldersToCreate.length > 0) {
|
|
263
245
|
return folderSchema.insertMany(
|
|
264
|
-
foldersToCreate.map(
|
|
246
|
+
foldersToCreate.map(folder => ({
|
|
265
247
|
...folder,
|
|
266
248
|
user_id: user.id,
|
|
267
249
|
}))
|
|
@@ -273,15 +255,13 @@ const checkIfUserExists = async (accountId) => {
|
|
|
273
255
|
|
|
274
256
|
// Handle themes
|
|
275
257
|
if (themeSchema) {
|
|
276
|
-
console.log("Checking themes...");
|
|
277
258
|
updates.push(
|
|
278
259
|
(async () => {
|
|
279
260
|
const defaultThemeExists = await themeSchema.exists({
|
|
280
|
-
name:
|
|
261
|
+
name: 'Default Theme',
|
|
281
262
|
accountId,
|
|
282
263
|
});
|
|
283
264
|
|
|
284
|
-
console.log("Default theme exists:", defaultThemeExists);
|
|
285
265
|
if (!defaultThemeExists) {
|
|
286
266
|
const defaultTheme = await themeSchema.create({
|
|
287
267
|
...DEFAULT_THEME,
|
|
@@ -301,12 +281,13 @@ const checkIfUserExists = async (accountId) => {
|
|
|
301
281
|
}
|
|
302
282
|
|
|
303
283
|
// Wait for all updates to complete
|
|
304
|
-
console.log("Waiting for all updates to complete...");
|
|
305
284
|
await Promise.all(updates);
|
|
306
|
-
console.log("All updates completed successfully");
|
|
307
285
|
|
|
308
|
-
// Return fresh user data after updates
|
|
309
|
-
return userSchema
|
|
286
|
+
// Return fresh user data after updates, excluding specified fields
|
|
287
|
+
return userSchema
|
|
288
|
+
.findOne({ accountId })
|
|
289
|
+
.select('-theme -folderList -userGlobalStyles -mapList')
|
|
290
|
+
.lean();
|
|
310
291
|
}
|
|
311
292
|
|
|
312
293
|
// Create new user with all associated data
|
|
@@ -317,7 +298,7 @@ const checkIfUserExists = async (accountId) => {
|
|
|
317
298
|
let userGlobalStyles;
|
|
318
299
|
if (userStyleSchema) {
|
|
319
300
|
creationTasks.push(
|
|
320
|
-
createUserGlobalStyles(userStyleSchema, accountId).then(
|
|
301
|
+
createUserGlobalStyles(userStyleSchema, accountId).then(styles => {
|
|
321
302
|
userGlobalStyles = styles;
|
|
322
303
|
return styles;
|
|
323
304
|
})
|
|
@@ -335,8 +316,8 @@ const checkIfUserExists = async (accountId) => {
|
|
|
335
316
|
userId,
|
|
336
317
|
id: uuidv4(),
|
|
337
318
|
})
|
|
338
|
-
.then(
|
|
339
|
-
console.log(
|
|
319
|
+
.then(theme => {
|
|
320
|
+
console.log('Created theme:', theme);
|
|
340
321
|
defaultTheme = theme;
|
|
341
322
|
return theme;
|
|
342
323
|
})
|
|
@@ -360,10 +341,12 @@ const checkIfUserExists = async (accountId) => {
|
|
|
360
341
|
await createDefaultFolders(folderSchema, newUser.id);
|
|
361
342
|
}
|
|
362
343
|
|
|
363
|
-
|
|
364
|
-
|
|
344
|
+
return userSchema
|
|
345
|
+
.findById(newUser._id)
|
|
346
|
+
.select('-theme -folderList -userGlobalStyles -mapList')
|
|
347
|
+
.lean();
|
|
365
348
|
} catch (error) {
|
|
366
|
-
console.error(
|
|
349
|
+
console.error('Detailed error in checkIfUserExists:', {
|
|
367
350
|
accountId,
|
|
368
351
|
errorName: error.name,
|
|
369
352
|
errorMessage: error.message,
|
|
@@ -374,7 +357,44 @@ const checkIfUserExists = async (accountId) => {
|
|
|
374
357
|
}
|
|
375
358
|
};
|
|
376
359
|
|
|
360
|
+
const updateUserGlobalStyleShortcuts = async (userId, shortcutId, shortcut) => {
|
|
361
|
+
try {
|
|
362
|
+
const user = await userSchema
|
|
363
|
+
.findOne({ id: userId })
|
|
364
|
+
.populate('userGlobalStyles');
|
|
365
|
+
if (!user) {
|
|
366
|
+
throw new Error('User not found');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Ensure we have consolidated styles
|
|
370
|
+
const consolidatedStyles = await consolidateGlobalStyles(user.accountId);
|
|
371
|
+
|
|
372
|
+
// Find the index of the shortcut to update
|
|
373
|
+
const shortcutIndex = consolidatedStyles.styleShortcuts.findIndex(
|
|
374
|
+
s => s.id.toString() === shortcutId
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
if (shortcutIndex === -1) {
|
|
378
|
+
// If the shortcut doesn't exist, add it
|
|
379
|
+
consolidatedStyles.styleShortcuts.push(shortcut);
|
|
380
|
+
} else {
|
|
381
|
+
// If the shortcut exists, update it
|
|
382
|
+
consolidatedStyles.styleShortcuts[shortcutIndex] = {
|
|
383
|
+
...consolidatedStyles.styleShortcuts[shortcutIndex],
|
|
384
|
+
...shortcut,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
await consolidatedStyles.save();
|
|
389
|
+
return consolidatedStyles;
|
|
390
|
+
} catch (error) {
|
|
391
|
+
console.error('Error updating user global style shortcuts:', error);
|
|
392
|
+
throw error;
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
|
|
377
396
|
module.exports = {
|
|
378
397
|
getAccountProfile,
|
|
379
398
|
checkIfUserExists,
|
|
399
|
+
updateUserGlobalStyleShortcuts,
|
|
380
400
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "propro-utils",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.3",
|
|
4
4
|
"description": "Auth middleware for propro-auth",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -46,7 +46,6 @@
|
|
|
46
46
|
"jest-mock-axios": "^4.7.3",
|
|
47
47
|
"jsonwebtoken": "^9.0.2",
|
|
48
48
|
"multer": "^1.4.5-lts.1",
|
|
49
|
-
"neverthrow": "^8.2.0",
|
|
50
49
|
"nodemailer": "^6.9.7",
|
|
51
50
|
"nodemailer-mailgun-transport": "^2.1.5",
|
|
52
51
|
"querystring": "^0.2.1",
|
package/src/server/index.js
CHANGED
|
@@ -168,7 +168,7 @@ class AuthMiddleware {
|
|
|
168
168
|
|
|
169
169
|
setAuthCookies(res, tokens, account, user, this.options.appUrl);
|
|
170
170
|
|
|
171
|
-
res.redirect(formatRedirectUrl(this.options.
|
|
171
|
+
res.redirect(formatRedirectUrl(this.options.redirectUri));
|
|
172
172
|
} catch (error) {
|
|
173
173
|
console.error('Error in callback:', error);
|
|
174
174
|
res.status(500).send('Internal Server Error');
|
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
* This module provides functions for setting and clearing authentication cookies.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const { URL } = require(
|
|
6
|
+
const { URL } = require('url');
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Safely stringify an object, handling circular references
|
|
10
10
|
* @param {Object} obj - The object to stringify
|
|
11
11
|
* @return {string} A JSON string representation of the object
|
|
12
12
|
*/
|
|
13
|
-
const safeStringify =
|
|
13
|
+
const safeStringify = obj => {
|
|
14
14
|
const seen = new WeakSet();
|
|
15
15
|
return JSON.stringify(obj, (key, value) => {
|
|
16
|
-
if (typeof value ===
|
|
16
|
+
if (typeof value === 'object' && value !== null) {
|
|
17
17
|
if (seen.has(value)) {
|
|
18
|
-
return
|
|
18
|
+
return '[Circular]';
|
|
19
19
|
}
|
|
20
20
|
seen.add(value);
|
|
21
21
|
}
|
|
@@ -28,7 +28,7 @@ const safeStringify = (obj) => {
|
|
|
28
28
|
* @param {Object} user - The user object to sanitize
|
|
29
29
|
* @return {Object} A sanitized version of the user object
|
|
30
30
|
*/
|
|
31
|
-
const sanitizeUser =
|
|
31
|
+
const sanitizeUser = user => {
|
|
32
32
|
const sanitized = { ...user };
|
|
33
33
|
|
|
34
34
|
delete sanitized.password;
|
|
@@ -61,10 +61,10 @@ const sanitizeUser = (user) => {
|
|
|
61
61
|
* @param {Object} details - Cookie details
|
|
62
62
|
* @returns {Promise} Promise that resolves when cookie is set
|
|
63
63
|
*/
|
|
64
|
-
const setChromeExtensionCookie =
|
|
64
|
+
const setChromeExtensionCookie = details => {
|
|
65
65
|
return new Promise((resolve, reject) => {
|
|
66
66
|
try {
|
|
67
|
-
chrome.cookies.set(details,
|
|
67
|
+
chrome.cookies.set(details, cookie => {
|
|
68
68
|
if (chrome.runtime.lastError) {
|
|
69
69
|
reject(chrome.runtime.lastError);
|
|
70
70
|
} else {
|
|
@@ -72,6 +72,7 @@ const setChromeExtensionCookie = (details) => {
|
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
} catch (error) {
|
|
75
|
+
console.log('Error setting cookie: setChromeExtensionCookie', error);
|
|
75
76
|
// Not in extension context
|
|
76
77
|
resolve(null);
|
|
77
78
|
}
|
|
@@ -83,13 +84,13 @@ const setChromeExtensionCookie = (details) => {
|
|
|
83
84
|
*/
|
|
84
85
|
const setAuthCookies = async (res, tokens, account, user, appUrl) => {
|
|
85
86
|
if (!tokens?.refresh?.token || !tokens?.access?.token) {
|
|
86
|
-
throw new Error(
|
|
87
|
+
throw new Error('Invalid tokens object');
|
|
87
88
|
}
|
|
88
89
|
if (!account) {
|
|
89
|
-
throw new Error(
|
|
90
|
+
throw new Error('Invalid account object');
|
|
90
91
|
}
|
|
91
92
|
if (!user) {
|
|
92
|
-
throw new Error(
|
|
93
|
+
throw new Error('Invalid user object');
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
const currentDateTime = new Date();
|
|
@@ -99,47 +100,37 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
|
|
|
99
100
|
new Date(tokens.access.expires).getTime() - currentDateTime.getTime();
|
|
100
101
|
|
|
101
102
|
// Domain configuration
|
|
102
|
-
let
|
|
103
|
+
let domain;
|
|
103
104
|
try {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
domain = appUrl ? new URL(appUrl).hostname : undefined;
|
|
106
|
+
if (domain?.includes('mapmap.app')) {
|
|
107
|
+
domain = '.mapmap.app';
|
|
108
|
+
}
|
|
109
|
+
if (domain?.includes('localhost')) {
|
|
110
|
+
domain = undefined;
|
|
111
|
+
}
|
|
112
|
+
if (domain?.includes('propro.so')) {
|
|
113
|
+
domain = 'propro.so';
|
|
112
114
|
}
|
|
113
|
-
|
|
114
|
-
const urlObj = new URL(processedAppUrl);
|
|
115
|
-
isSecureConnection = urlObj.protocol === "https:";
|
|
116
|
-
|
|
117
|
-
console.log("Cookie configuration:", {
|
|
118
|
-
isSecure: isSecureConnection,
|
|
119
|
-
protocol: urlObj.protocol,
|
|
120
|
-
originalUrl: appUrl,
|
|
121
|
-
processedUrl: processedAppUrl,
|
|
122
|
-
hostname: urlObj.hostname,
|
|
123
|
-
});
|
|
124
115
|
} catch (error) {
|
|
125
|
-
console.error(
|
|
126
|
-
|
|
116
|
+
console.error('Invalid appUrl:', { error, appUrl });
|
|
117
|
+
domain = undefined;
|
|
127
118
|
}
|
|
128
119
|
|
|
129
|
-
// Base cookie attributes without domain specification
|
|
130
120
|
const commonAttributes = {
|
|
131
121
|
secure: true,
|
|
132
|
-
sameSite:
|
|
133
|
-
|
|
122
|
+
sameSite: 'None',
|
|
123
|
+
domain,
|
|
124
|
+
path: '/',
|
|
134
125
|
};
|
|
135
126
|
|
|
136
127
|
const httpOnlyCookies = {
|
|
137
|
-
|
|
128
|
+
'x-refresh-token': {
|
|
138
129
|
value: tokens.refresh.token,
|
|
139
130
|
maxAge: refreshMaxAge,
|
|
140
131
|
httpOnly: true,
|
|
141
132
|
},
|
|
142
|
-
|
|
133
|
+
'x-access-token': {
|
|
143
134
|
value: tokens.access.token,
|
|
144
135
|
maxAge: accessMaxAge,
|
|
145
136
|
httpOnly: true,
|
|
@@ -154,59 +145,48 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
|
|
|
154
145
|
user: {
|
|
155
146
|
value: safeStringify(sanitizedUser),
|
|
156
147
|
maxAge: refreshMaxAge,
|
|
157
|
-
httpOnly: false,
|
|
158
148
|
},
|
|
159
149
|
account: {
|
|
160
150
|
value: safeStringify(sanitizedAccount),
|
|
161
151
|
maxAge: refreshMaxAge,
|
|
162
|
-
httpOnly: false,
|
|
163
152
|
},
|
|
164
153
|
has_account_token: {
|
|
165
|
-
value: JSON.stringify({ value:
|
|
154
|
+
value: JSON.stringify({ value: 'true', expires: accessMaxAge }),
|
|
166
155
|
maxAge: accessMaxAge,
|
|
167
|
-
httpOnly: false,
|
|
168
156
|
},
|
|
169
157
|
};
|
|
170
158
|
|
|
171
159
|
try {
|
|
172
|
-
// Set each cookie individually
|
|
173
160
|
Object.entries({ ...httpOnlyCookies, ...regularCookies }).forEach(
|
|
174
161
|
([name, config]) => {
|
|
175
|
-
|
|
162
|
+
res.cookie(name, config.value, {
|
|
176
163
|
...commonAttributes,
|
|
177
164
|
...config,
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
res.cookie(name, config.value, cookieConfig);
|
|
165
|
+
});
|
|
181
166
|
}
|
|
182
167
|
);
|
|
183
168
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
expirationDate: Math.floor((Date.now() + config.maxAge) / 1000),
|
|
199
|
-
});
|
|
169
|
+
const extensionCookiePromises = Object.entries({
|
|
170
|
+
...httpOnlyCookies,
|
|
171
|
+
...regularCookies,
|
|
172
|
+
}).map(([name, config]) => {
|
|
173
|
+
return setChromeExtensionCookie({
|
|
174
|
+
url: `https://${domain || 'propro.so'}`,
|
|
175
|
+
name,
|
|
176
|
+
value: config.value,
|
|
177
|
+
secure: true,
|
|
178
|
+
httpOnly: !!config.httpOnly,
|
|
179
|
+
sameSite: 'no_restriction',
|
|
180
|
+
path: '/',
|
|
181
|
+
expirationDate: Math.floor((Date.now() + config.maxAge) / 1000),
|
|
182
|
+
domain: domain?.startsWith('.') ? domain : `.${domain || 'propro.so'}`,
|
|
200
183
|
});
|
|
184
|
+
});
|
|
201
185
|
|
|
202
|
-
|
|
203
|
-
// Ignore extension errors
|
|
204
|
-
});
|
|
205
|
-
} catch (error) {
|
|
206
|
-
// Ignore extension errors
|
|
207
|
-
}
|
|
186
|
+
await Promise.allSettled(extensionCookiePromises);
|
|
208
187
|
|
|
209
|
-
console.log(
|
|
188
|
+
console.log('Auth cookies set successfully', {
|
|
189
|
+
domain,
|
|
210
190
|
sameSite: commonAttributes.sameSite,
|
|
211
191
|
cookieNames: [
|
|
212
192
|
...Object.keys(httpOnlyCookies),
|
|
@@ -214,11 +194,11 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
|
|
|
214
194
|
],
|
|
215
195
|
});
|
|
216
196
|
} catch (error) {
|
|
217
|
-
console.error(
|
|
197
|
+
console.error('Error setting cookies:', {
|
|
218
198
|
error: error.message,
|
|
219
199
|
stack: error.stack,
|
|
220
200
|
});
|
|
221
|
-
throw new Error(
|
|
201
|
+
throw new Error('Failed to set authentication cookies');
|
|
222
202
|
}
|
|
223
203
|
};
|
|
224
204
|
|
|
@@ -226,77 +206,61 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
|
|
|
226
206
|
* Clears cookies from both web and extension contexts
|
|
227
207
|
*/
|
|
228
208
|
const clearAuthCookies = async (res, appUrl) => {
|
|
229
|
-
let
|
|
209
|
+
let domain;
|
|
230
210
|
try {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
) {
|
|
238
|
-
processedAppUrl = `https://${appUrl}`;
|
|
211
|
+
domain = appUrl ? new URL(appUrl).hostname : undefined;
|
|
212
|
+
if (domain?.includes('mapmap.app')) {
|
|
213
|
+
domain = '.mapmap.app';
|
|
214
|
+
}
|
|
215
|
+
if (domain?.includes('localhost')) {
|
|
216
|
+
domain = undefined;
|
|
239
217
|
}
|
|
240
|
-
|
|
241
|
-
const urlObj = new URL(processedAppUrl);
|
|
242
|
-
isSecureConnection = urlObj.protocol === "https:";
|
|
243
|
-
|
|
244
|
-
console.log("Clear cookies configuration:", {
|
|
245
|
-
isSecure: isSecureConnection,
|
|
246
|
-
protocol: urlObj.protocol,
|
|
247
|
-
originalUrl: appUrl,
|
|
248
|
-
processedUrl: processedAppUrl,
|
|
249
|
-
hostname: urlObj.hostname,
|
|
250
|
-
});
|
|
251
218
|
} catch (error) {
|
|
252
|
-
console.error(
|
|
253
|
-
|
|
219
|
+
console.error('Invalid appUrl:', error);
|
|
220
|
+
domain = undefined;
|
|
254
221
|
}
|
|
255
222
|
|
|
256
223
|
const commonAttributes = {
|
|
257
224
|
secure: true,
|
|
258
|
-
sameSite:
|
|
259
|
-
|
|
260
|
-
|
|
225
|
+
sameSite: 'None',
|
|
226
|
+
domain,
|
|
227
|
+
path: '/',
|
|
261
228
|
};
|
|
262
229
|
|
|
263
230
|
const cookieNames = [
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
231
|
+
'x-refresh-token',
|
|
232
|
+
'x-access-token',
|
|
233
|
+
'user',
|
|
234
|
+
'account',
|
|
235
|
+
'has_account_token',
|
|
269
236
|
];
|
|
270
237
|
|
|
271
|
-
// Clear cookies
|
|
272
|
-
cookieNames.forEach(
|
|
238
|
+
// Clear web cookies
|
|
239
|
+
cookieNames.forEach(cookieName => {
|
|
273
240
|
res.clearCookie(cookieName, commonAttributes);
|
|
274
241
|
});
|
|
275
242
|
|
|
276
243
|
try {
|
|
277
|
-
// Handle extension cookies if in extension context
|
|
278
244
|
const extensionClearPromises = cookieNames.map(
|
|
279
|
-
|
|
280
|
-
new Promise(
|
|
245
|
+
name =>
|
|
246
|
+
new Promise(resolve => {
|
|
281
247
|
chrome.cookies.remove(
|
|
282
248
|
{
|
|
283
|
-
url:
|
|
249
|
+
url: `https://${domain || 'mapmap.app'}`,
|
|
284
250
|
name,
|
|
285
|
-
secure: true,
|
|
286
251
|
},
|
|
287
252
|
resolve
|
|
288
253
|
);
|
|
289
254
|
})
|
|
290
255
|
);
|
|
291
256
|
|
|
292
|
-
Promise.allSettled(extensionClearPromises)
|
|
293
|
-
// Ignore extension errors
|
|
294
|
-
});
|
|
257
|
+
await Promise.allSettled(extensionClearPromises);
|
|
295
258
|
} catch (error) {
|
|
296
259
|
// Not in extension context, ignore
|
|
297
260
|
}
|
|
298
261
|
|
|
299
|
-
console.log(
|
|
262
|
+
console.log('Auth cookies cleared successfully', {
|
|
263
|
+
domain,
|
|
300
264
|
cookieNames,
|
|
301
265
|
sameSite: commonAttributes.sameSite,
|
|
302
266
|
});
|