propro-utils 1.7.0 → 1.7.2
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 +65 -182
|
@@ -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.2",
|
|
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 {
|
|
@@ -83,13 +83,13 @@ const setChromeExtensionCookie = (details) => {
|
|
|
83
83
|
*/
|
|
84
84
|
const setAuthCookies = async (res, tokens, account, user, appUrl) => {
|
|
85
85
|
if (!tokens?.refresh?.token || !tokens?.access?.token) {
|
|
86
|
-
throw new Error(
|
|
86
|
+
throw new Error('Invalid tokens object');
|
|
87
87
|
}
|
|
88
88
|
if (!account) {
|
|
89
|
-
throw new Error(
|
|
89
|
+
throw new Error('Invalid account object');
|
|
90
90
|
}
|
|
91
91
|
if (!user) {
|
|
92
|
-
throw new Error(
|
|
92
|
+
throw new Error('Invalid user object');
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
const currentDateTime = new Date();
|
|
@@ -100,78 +100,39 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
|
|
|
100
100
|
|
|
101
101
|
// Domain configuration
|
|
102
102
|
let domain;
|
|
103
|
-
let isSecureConnection = false;
|
|
104
103
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
appUrl &&
|
|
109
|
-
!appUrl.startsWith("http://") &&
|
|
110
|
-
!appUrl.startsWith("https://")
|
|
111
|
-
) {
|
|
112
|
-
processedAppUrl = `https://${appUrl}`;
|
|
104
|
+
domain = appUrl ? new URL(appUrl).hostname : undefined;
|
|
105
|
+
if (domain?.includes('mapmap.app')) {
|
|
106
|
+
domain = '.mapmap.app';
|
|
113
107
|
}
|
|
114
|
-
|
|
115
|
-
const urlObj = new URL(processedAppUrl);
|
|
116
|
-
domain = urlObj.hostname;
|
|
117
|
-
isSecureConnection = urlObj.protocol === "https:";
|
|
118
|
-
|
|
119
|
-
if (domain?.includes("mapmap.app")) {
|
|
120
|
-
domain = ".mapmap.app";
|
|
121
|
-
}
|
|
122
|
-
if (domain?.includes("localhost")) {
|
|
108
|
+
if (domain?.includes('localhost')) {
|
|
123
109
|
domain = undefined;
|
|
124
|
-
isSecureConnection = false;
|
|
125
110
|
}
|
|
126
|
-
if (domain?.includes(
|
|
127
|
-
|
|
128
|
-
domain = ".propro.so";
|
|
111
|
+
if (domain?.includes('propro.so')) {
|
|
112
|
+
domain = 'propro.so';
|
|
129
113
|
}
|
|
130
|
-
|
|
131
|
-
console.log("Cookie configuration:", {
|
|
132
|
-
domain,
|
|
133
|
-
isSecure: isSecureConnection,
|
|
134
|
-
protocol: urlObj.protocol,
|
|
135
|
-
originalUrl: appUrl,
|
|
136
|
-
processedUrl: processedAppUrl,
|
|
137
|
-
hostname: urlObj.hostname,
|
|
138
|
-
});
|
|
139
114
|
} catch (error) {
|
|
140
|
-
console.error(
|
|
115
|
+
console.error('Invalid appUrl:', { error, appUrl });
|
|
141
116
|
domain = undefined;
|
|
142
|
-
isSecureConnection = false;
|
|
143
117
|
}
|
|
144
118
|
|
|
145
|
-
// Determine if we're in a local development environment
|
|
146
|
-
const isLocalhost =
|
|
147
|
-
!domain || domain === "localhost" || domain.includes("localhost");
|
|
148
|
-
|
|
149
|
-
// Base attributes that work across domains
|
|
150
119
|
const commonAttributes = {
|
|
151
|
-
secure: true,
|
|
152
|
-
sameSite:
|
|
153
|
-
domain
|
|
154
|
-
path:
|
|
155
|
-
httpOnly: false,
|
|
156
|
-
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000),
|
|
120
|
+
secure: true,
|
|
121
|
+
sameSite: 'None',
|
|
122
|
+
domain,
|
|
123
|
+
path: '/',
|
|
157
124
|
};
|
|
158
125
|
|
|
159
126
|
const httpOnlyCookies = {
|
|
160
|
-
|
|
127
|
+
'x-refresh-token': {
|
|
161
128
|
value: tokens.refresh.token,
|
|
162
129
|
maxAge: refreshMaxAge,
|
|
163
130
|
httpOnly: true,
|
|
164
|
-
secure: true,
|
|
165
|
-
sameSite: "None",
|
|
166
|
-
domain: domain,
|
|
167
131
|
},
|
|
168
|
-
|
|
132
|
+
'x-access-token': {
|
|
169
133
|
value: tokens.access.token,
|
|
170
134
|
maxAge: accessMaxAge,
|
|
171
135
|
httpOnly: true,
|
|
172
|
-
secure: true,
|
|
173
|
-
sameSite: "None",
|
|
174
|
-
domain: domain,
|
|
175
136
|
},
|
|
176
137
|
};
|
|
177
138
|
|
|
@@ -183,80 +144,47 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
|
|
|
183
144
|
user: {
|
|
184
145
|
value: safeStringify(sanitizedUser),
|
|
185
146
|
maxAge: refreshMaxAge,
|
|
186
|
-
httpOnly: false,
|
|
187
|
-
secure: true,
|
|
188
|
-
sameSite: "None",
|
|
189
|
-
domain: domain,
|
|
190
147
|
},
|
|
191
148
|
account: {
|
|
192
149
|
value: safeStringify(sanitizedAccount),
|
|
193
150
|
maxAge: refreshMaxAge,
|
|
194
|
-
httpOnly: false,
|
|
195
|
-
secure: true,
|
|
196
|
-
sameSite: "None",
|
|
197
|
-
domain: domain,
|
|
198
151
|
},
|
|
199
152
|
has_account_token: {
|
|
200
|
-
value: JSON.stringify({ value:
|
|
153
|
+
value: JSON.stringify({ value: 'true', expires: accessMaxAge }),
|
|
201
154
|
maxAge: accessMaxAge,
|
|
202
|
-
httpOnly: false,
|
|
203
|
-
secure: true,
|
|
204
|
-
sameSite: "None",
|
|
205
|
-
domain: domain,
|
|
206
155
|
},
|
|
207
156
|
};
|
|
208
157
|
|
|
209
158
|
try {
|
|
210
|
-
// Set each cookie individually with full attributes
|
|
211
159
|
Object.entries({ ...httpOnlyCookies, ...regularCookies }).forEach(
|
|
212
160
|
([name, config]) => {
|
|
213
|
-
|
|
214
|
-
const cookieConfig = {
|
|
161
|
+
res.cookie(name, config.value, {
|
|
215
162
|
...commonAttributes,
|
|
216
163
|
...config,
|
|
217
|
-
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
res.cookie(name, config.value, cookieConfig);
|
|
221
|
-
|
|
222
|
-
// Also try setting without domain for root domain
|
|
223
|
-
if (domain === ".propro.so") {
|
|
224
|
-
const rootConfig = { ...cookieConfig };
|
|
225
|
-
delete rootConfig.domain;
|
|
226
|
-
res.cookie(name, config.value, rootConfig);
|
|
227
|
-
}
|
|
164
|
+
});
|
|
228
165
|
}
|
|
229
166
|
);
|
|
230
167
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
sameSite: "no_restriction",
|
|
246
|
-
path: "/",
|
|
247
|
-
expirationDate: Math.floor((Date.now() + config.maxAge) / 1000),
|
|
248
|
-
domain: domain,
|
|
249
|
-
});
|
|
168
|
+
const extensionCookiePromises = Object.entries({
|
|
169
|
+
...httpOnlyCookies,
|
|
170
|
+
...regularCookies,
|
|
171
|
+
}).map(([name, config]) => {
|
|
172
|
+
return setChromeExtensionCookie({
|
|
173
|
+
url: `https://${domain || 'propro.so'}`,
|
|
174
|
+
name,
|
|
175
|
+
value: config.value,
|
|
176
|
+
secure: true,
|
|
177
|
+
httpOnly: !!config.httpOnly,
|
|
178
|
+
sameSite: 'no_restriction',
|
|
179
|
+
path: '/',
|
|
180
|
+
expirationDate: Math.floor((Date.now() + config.maxAge) / 1000),
|
|
181
|
+
domain: domain?.startsWith('.') ? domain : `.${domain || 'propro.so'}`,
|
|
250
182
|
});
|
|
183
|
+
});
|
|
251
184
|
|
|
252
|
-
|
|
253
|
-
// Ignore extension errors
|
|
254
|
-
});
|
|
255
|
-
} catch (error) {
|
|
256
|
-
// Ignore extension errors
|
|
257
|
-
}
|
|
185
|
+
await Promise.allSettled(extensionCookiePromises);
|
|
258
186
|
|
|
259
|
-
console.log(
|
|
187
|
+
console.log('Auth cookies set successfully', {
|
|
260
188
|
domain,
|
|
261
189
|
sameSite: commonAttributes.sameSite,
|
|
262
190
|
cookieNames: [
|
|
@@ -265,11 +193,11 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
|
|
|
265
193
|
],
|
|
266
194
|
});
|
|
267
195
|
} catch (error) {
|
|
268
|
-
console.error(
|
|
196
|
+
console.error('Error setting cookies:', {
|
|
269
197
|
error: error.message,
|
|
270
198
|
stack: error.stack,
|
|
271
199
|
});
|
|
272
|
-
throw new Error(
|
|
200
|
+
throw new Error('Failed to set authentication cookies');
|
|
273
201
|
}
|
|
274
202
|
};
|
|
275
203
|
|
|
@@ -278,104 +206,59 @@ const setAuthCookies = async (res, tokens, account, user, appUrl) => {
|
|
|
278
206
|
*/
|
|
279
207
|
const clearAuthCookies = async (res, appUrl) => {
|
|
280
208
|
let domain;
|
|
281
|
-
let isSecureConnection = false;
|
|
282
209
|
try {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
appUrl &&
|
|
287
|
-
!appUrl.startsWith("http://") &&
|
|
288
|
-
!appUrl.startsWith("https://")
|
|
289
|
-
) {
|
|
290
|
-
processedAppUrl = `https://${appUrl}`;
|
|
210
|
+
domain = appUrl ? new URL(appUrl).hostname : undefined;
|
|
211
|
+
if (domain?.includes('mapmap.app')) {
|
|
212
|
+
domain = '.mapmap.app';
|
|
291
213
|
}
|
|
292
|
-
|
|
293
|
-
const urlObj = new URL(processedAppUrl);
|
|
294
|
-
domain = urlObj.hostname;
|
|
295
|
-
isSecureConnection = urlObj.protocol === "https:";
|
|
296
|
-
|
|
297
|
-
if (domain?.includes("mapmap.app")) {
|
|
298
|
-
domain = ".mapmap.app";
|
|
299
|
-
}
|
|
300
|
-
if (domain?.includes("localhost")) {
|
|
214
|
+
if (domain?.includes('localhost')) {
|
|
301
215
|
domain = undefined;
|
|
302
|
-
isSecureConnection = false;
|
|
303
|
-
}
|
|
304
|
-
if (domain?.includes("propro.so")) {
|
|
305
|
-
domain = ".propro.so";
|
|
306
216
|
}
|
|
307
|
-
|
|
308
|
-
console.log("Clear cookies configuration:", {
|
|
309
|
-
domain,
|
|
310
|
-
isSecure: isSecureConnection,
|
|
311
|
-
protocol: urlObj.protocol,
|
|
312
|
-
originalUrl: appUrl,
|
|
313
|
-
processedUrl: processedAppUrl,
|
|
314
|
-
hostname: urlObj.hostname,
|
|
315
|
-
});
|
|
316
217
|
} catch (error) {
|
|
317
|
-
console.error(
|
|
218
|
+
console.error('Invalid appUrl:', error);
|
|
318
219
|
domain = undefined;
|
|
319
|
-
isSecureConnection = false;
|
|
320
220
|
}
|
|
321
221
|
|
|
322
222
|
const commonAttributes = {
|
|
323
223
|
secure: true,
|
|
324
|
-
sameSite:
|
|
325
|
-
domain
|
|
326
|
-
path:
|
|
327
|
-
httpOnly: false,
|
|
328
|
-
expires: new Date(0),
|
|
224
|
+
sameSite: 'None',
|
|
225
|
+
domain,
|
|
226
|
+
path: '/',
|
|
329
227
|
};
|
|
330
228
|
|
|
331
229
|
const cookieNames = [
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
230
|
+
'x-refresh-token',
|
|
231
|
+
'x-access-token',
|
|
232
|
+
'user',
|
|
233
|
+
'account',
|
|
234
|
+
'has_account_token',
|
|
337
235
|
];
|
|
338
236
|
|
|
339
|
-
// Clear cookies
|
|
340
|
-
cookieNames.forEach(
|
|
237
|
+
// Clear web cookies
|
|
238
|
+
cookieNames.forEach(cookieName => {
|
|
341
239
|
res.clearCookie(cookieName, commonAttributes);
|
|
342
|
-
|
|
343
|
-
// Also try clearing without domain for root domain
|
|
344
|
-
if (domain === ".propro.so") {
|
|
345
|
-
const rootAttributes = { ...commonAttributes };
|
|
346
|
-
delete rootAttributes.domain;
|
|
347
|
-
res.clearCookie(cookieName, rootAttributes);
|
|
348
|
-
}
|
|
349
240
|
});
|
|
350
241
|
|
|
351
242
|
try {
|
|
352
|
-
// Skip extension cookie clearing if not in extension context
|
|
353
243
|
const extensionClearPromises = cookieNames.map(
|
|
354
|
-
|
|
355
|
-
new Promise(
|
|
244
|
+
name =>
|
|
245
|
+
new Promise(resolve => {
|
|
356
246
|
chrome.cookies.remove(
|
|
357
247
|
{
|
|
358
|
-
url: `https://${
|
|
359
|
-
domain?.startsWith(".")
|
|
360
|
-
? domain.slice(1)
|
|
361
|
-
: domain || "propro.so"
|
|
362
|
-
}`,
|
|
248
|
+
url: `https://${domain || 'mapmap.app'}`,
|
|
363
249
|
name,
|
|
364
|
-
domain: domain,
|
|
365
250
|
},
|
|
366
251
|
resolve
|
|
367
252
|
);
|
|
368
253
|
})
|
|
369
254
|
);
|
|
370
255
|
|
|
371
|
-
Promise.allSettled(extensionClearPromises)
|
|
372
|
-
// Ignore extension errors
|
|
373
|
-
});
|
|
256
|
+
await Promise.allSettled(extensionClearPromises);
|
|
374
257
|
} catch (error) {
|
|
375
258
|
// Not in extension context, ignore
|
|
376
259
|
}
|
|
377
260
|
|
|
378
|
-
console.log(
|
|
261
|
+
console.log('Auth cookies cleared successfully', {
|
|
379
262
|
domain,
|
|
380
263
|
cookieNames,
|
|
381
264
|
sameSite: commonAttributes.sameSite,
|