coffeeinabit 0.0.47 → 0.0.49
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/linkedin_automation.js +226 -11
- package/package.json +2 -1
- package/server.js +50 -3
- package/state/app_state.js +62 -3
- package/tools/get_daily_linkedin_connections.js +144 -83
- package/tools/get_new_messages.js +468 -323
package/linkedin_automation.js
CHANGED
|
@@ -12,7 +12,6 @@ import { executeSendMessages } from './tools/send_messages.js';
|
|
|
12
12
|
import { executeCommentPost } from './tools/comment_post.js';
|
|
13
13
|
import { executeGetMessages, closeAllConversationBubbles } from './tools/get_messages.js';
|
|
14
14
|
import { executeLinkedInSearch } from './tools/get_linkedin_search_results.js';
|
|
15
|
-
import { executeGetDailyLinkedInConnections } from './tools/get_daily_linkedin_connections.js';
|
|
16
15
|
import { executeGetNewMessages } from './tools/get_new_messages.js';
|
|
17
16
|
import { executeGetLinkedInUpdates } from './tools/get_linkedin_updates.js';
|
|
18
17
|
import { safeGoto } from './tools/navigation.js';
|
|
@@ -42,6 +41,16 @@ export class LinkedInAutomation {
|
|
|
42
41
|
this.ambientMouseTimeout = null;
|
|
43
42
|
this.cloudAuth = new CloudAuth();
|
|
44
43
|
this._currentRefreshToken = null;
|
|
44
|
+
this._currentAccessToken = null; // Track access token explicitly
|
|
45
|
+
|
|
46
|
+
// Authentication failure tracking
|
|
47
|
+
this.consecutiveAuthFailures = 0;
|
|
48
|
+
this.maxAuthFailures = 3; // Stop polling after 3 consecutive failures
|
|
49
|
+
this.authFailurePaused = false;
|
|
50
|
+
this.lastAuthFailureTime = null;
|
|
51
|
+
|
|
52
|
+
// Token re-sync callback (set externally if available)
|
|
53
|
+
this.tokenResyncCallback = null;
|
|
45
54
|
|
|
46
55
|
// Lifecycle phases
|
|
47
56
|
this.PHASE_IDLE = 'idle';
|
|
@@ -350,6 +359,7 @@ export class LinkedInAutomation {
|
|
|
350
359
|
});
|
|
351
360
|
|
|
352
361
|
this.page.on('framenavigated', (frame) => {
|
|
362
|
+
if (!this.page) return;
|
|
353
363
|
if (frame === this.page.mainFrame()) {
|
|
354
364
|
this.updatePageInfo();
|
|
355
365
|
}
|
|
@@ -730,13 +740,43 @@ export class LinkedInAutomation {
|
|
|
730
740
|
if (!this.ensureBrowserAvailable('pollForActions')) {
|
|
731
741
|
return;
|
|
732
742
|
}
|
|
743
|
+
|
|
744
|
+
// Check if polling is paused due to auth failures
|
|
745
|
+
if (this.authFailurePaused) {
|
|
746
|
+
console.log('[LinkedInAutomation] Polling paused due to authentication failures. Attempting recovery...');
|
|
747
|
+
|
|
748
|
+
// Try to re-sync tokens and recover
|
|
749
|
+
const idToken = await this.getAccessToken();
|
|
750
|
+
if (idToken && !this.cloudAuth.isTokenExpired(idToken)) {
|
|
751
|
+
console.log('[LinkedInAutomation] Authentication recovered, resuming polling');
|
|
752
|
+
this.authFailurePaused = false;
|
|
753
|
+
this.consecutiveAuthFailures = 0;
|
|
754
|
+
// Continue with normal polling below
|
|
755
|
+
} else {
|
|
756
|
+
console.log('[LinkedInAutomation] Still unable to authenticate, skipping poll');
|
|
757
|
+
|
|
758
|
+
// Emit event to notify UI
|
|
759
|
+
if (this.io) {
|
|
760
|
+
this.io.emit('automation:auth-failure', {
|
|
761
|
+
message: 'Authentication failed. Please re-authenticate.',
|
|
762
|
+
paused: true,
|
|
763
|
+
consecutiveFailures: this.consecutiveAuthFailures,
|
|
764
|
+
lastFailureTime: this.lastAuthFailureTime
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
return false;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
733
772
|
try {
|
|
734
773
|
console.log('[LinkedInAutomation] Polling for actions from backend...');
|
|
735
774
|
|
|
736
775
|
const idToken = await this.getAccessToken();
|
|
737
776
|
if (!idToken) {
|
|
738
777
|
console.error('[LinkedInAutomation] No id_token available for authentication');
|
|
739
|
-
|
|
778
|
+
this.handleAuthFailure('No token available');
|
|
779
|
+
return false;
|
|
740
780
|
}
|
|
741
781
|
|
|
742
782
|
console.log('[LinkedInAutomation] Using id_token for authentication:', idToken.substring(0, 20) + '...');
|
|
@@ -760,25 +800,116 @@ export class LinkedInAutomation {
|
|
|
760
800
|
for (const action of actions) {
|
|
761
801
|
await this.executeAction(action);
|
|
762
802
|
}
|
|
803
|
+
// Reset failure counter on successful request
|
|
804
|
+
this.consecutiveAuthFailures = 0;
|
|
805
|
+
this.authFailurePaused = false;
|
|
763
806
|
// Indicate there was work
|
|
764
807
|
return true;
|
|
765
808
|
} else {
|
|
766
809
|
console.log('[LinkedInAutomation] No actions to execute, continuing to poll...');
|
|
810
|
+
// Reset failure counter on successful request (even with no actions)
|
|
811
|
+
this.consecutiveAuthFailures = 0;
|
|
812
|
+
this.authFailurePaused = false;
|
|
767
813
|
return false;
|
|
768
814
|
}
|
|
815
|
+
} else if (response.status === 401) {
|
|
816
|
+
// Handle 401 Unauthorized specifically
|
|
817
|
+
const errorText = await response.text();
|
|
818
|
+
let errorMessage;
|
|
819
|
+
try {
|
|
820
|
+
const errorJson = JSON.parse(errorText);
|
|
821
|
+
errorMessage = errorJson.message || 'Unauthorized';
|
|
822
|
+
} catch {
|
|
823
|
+
errorMessage = errorText || 'Unauthorized';
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
console.error('[LinkedInAutomation] Authentication failed (401 Unauthorized):', errorMessage);
|
|
827
|
+
|
|
828
|
+
// Clear current token since it's invalid
|
|
829
|
+
this._currentAccessToken = null;
|
|
830
|
+
|
|
831
|
+
// Try to re-sync tokens one more time
|
|
832
|
+
console.log('[LinkedInAutomation] Attempting token re-sync after 401 error...');
|
|
833
|
+
const resynced = await this.attemptTokenResync();
|
|
834
|
+
|
|
835
|
+
if (!resynced) {
|
|
836
|
+
this.handleAuthFailure(`401 Unauthorized: ${errorMessage}`);
|
|
837
|
+
} else {
|
|
838
|
+
// If re-sync worked, try to refresh the token
|
|
839
|
+
if (this.cloudAuth.isTokenExpired(this._currentAccessToken)) {
|
|
840
|
+
const refreshResult = await this.cloudAuth.refreshTokens(this._currentRefreshToken);
|
|
841
|
+
if (!refreshResult.success) {
|
|
842
|
+
this.handleAuthFailure(`401 Unauthorized: Token re-sync succeeded but refresh failed`);
|
|
843
|
+
} else {
|
|
844
|
+
this._currentAccessToken = refreshResult.tokens.idToken;
|
|
845
|
+
if (refreshResult.tokens.refreshToken) {
|
|
846
|
+
this._currentRefreshToken = refreshResult.tokens.refreshToken;
|
|
847
|
+
}
|
|
848
|
+
console.log('[LinkedInAutomation] Token re-sync and refresh succeeded after 401, resetting failure counter');
|
|
849
|
+
this.consecutiveAuthFailures = 0;
|
|
850
|
+
this.authFailurePaused = false;
|
|
851
|
+
}
|
|
852
|
+
} else {
|
|
853
|
+
console.log('[LinkedInAutomation] Token re-sync succeeded after 401, resetting failure counter');
|
|
854
|
+
this.consecutiveAuthFailures = 0;
|
|
855
|
+
this.authFailurePaused = false;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
return false;
|
|
769
860
|
} else {
|
|
770
861
|
console.error('[LinkedInAutomation] Failed to fetch actions. Status:', response.status, response.statusText);
|
|
771
862
|
const errorText = await response.text();
|
|
772
863
|
console.error('[LinkedInAutomation] Error response:', errorText);
|
|
864
|
+
// Non-auth errors don't count as auth failures
|
|
773
865
|
return false;
|
|
774
866
|
}
|
|
775
867
|
} catch (error) {
|
|
776
868
|
console.error('[LinkedInAutomation] Error polling for actions:', error.message);
|
|
777
869
|
console.error('[LinkedInAutomation] Full error:', error);
|
|
870
|
+
|
|
871
|
+
// Check if error is auth-related
|
|
872
|
+
if (error.message.includes('401') || error.message.includes('Unauthorized') || error.message.includes('token')) {
|
|
873
|
+
this.handleAuthFailure(`Network error: ${error.message}`);
|
|
874
|
+
}
|
|
875
|
+
|
|
778
876
|
return false;
|
|
779
877
|
}
|
|
780
878
|
}
|
|
781
879
|
|
|
880
|
+
/**
|
|
881
|
+
* Handle authentication failure - track consecutive failures and pause polling if threshold reached
|
|
882
|
+
* @param {string} reason - Reason for the auth failure
|
|
883
|
+
*/
|
|
884
|
+
handleAuthFailure(reason) {
|
|
885
|
+
this.consecutiveAuthFailures++;
|
|
886
|
+
this.lastAuthFailureTime = new Date().toISOString();
|
|
887
|
+
|
|
888
|
+
console.warn(`[LinkedInAutomation] Authentication failure #${this.consecutiveAuthFailures}: ${reason}`);
|
|
889
|
+
|
|
890
|
+
if (this.consecutiveAuthFailures >= this.maxAuthFailures) {
|
|
891
|
+
if (!this.authFailurePaused) {
|
|
892
|
+
this.authFailurePaused = true;
|
|
893
|
+
console.error(`[LinkedInAutomation] ⚠️ STOPPING polling after ${this.consecutiveAuthFailures} consecutive authentication failures`);
|
|
894
|
+
console.error('[LinkedInAutomation] Polling will resume when authentication is restored.');
|
|
895
|
+
console.error('[LinkedInAutomation] Please re-authenticate or check your session.');
|
|
896
|
+
|
|
897
|
+
// Emit event to notify UI
|
|
898
|
+
if (this.io) {
|
|
899
|
+
this.io.emit('automation:auth-failure', {
|
|
900
|
+
message: `Authentication failed after ${this.consecutiveAuthFailures} attempts. Polling paused.`,
|
|
901
|
+
paused: true,
|
|
902
|
+
consecutiveFailures: this.consecutiveAuthFailures,
|
|
903
|
+
lastFailureTime: this.lastAuthFailureTime,
|
|
904
|
+
reason: reason
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
} else {
|
|
909
|
+
console.warn(`[LinkedInAutomation] Will pause polling after ${this.maxAuthFailures - this.consecutiveAuthFailures} more failures`);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
782
913
|
async executeAction(action) {
|
|
783
914
|
const actionLogs = [];
|
|
784
915
|
const originalConsoleLog = console.log;
|
|
@@ -858,10 +989,6 @@ export class LinkedInAutomation {
|
|
|
858
989
|
console.log('[LinkedInAutomation] Executing get_linkedin_search_results action...');
|
|
859
990
|
result = await executeLinkedInSearch(this.page, action);
|
|
860
991
|
break;
|
|
861
|
-
case 'get_daily_linkedin_connections':
|
|
862
|
-
console.log('[LinkedInAutomation] Executing get_daily_linkedin_connections action...');
|
|
863
|
-
result = await executeGetDailyLinkedInConnections(this.page, action);
|
|
864
|
-
break;
|
|
865
992
|
case 'get_new_messages':
|
|
866
993
|
console.log('[LinkedInAutomation] Executing get_new_messages action...');
|
|
867
994
|
result = await executeGetNewMessages(this.page, action, await this.getAccessToken());
|
|
@@ -1053,11 +1180,64 @@ export class LinkedInAutomation {
|
|
|
1053
1180
|
}
|
|
1054
1181
|
}
|
|
1055
1182
|
|
|
1183
|
+
/**
|
|
1184
|
+
* Set a callback function to re-sync tokens from external source (e.g., session store)
|
|
1185
|
+
* @param {Function} callback - Function that returns Promise<{idToken, refreshToken}> or null
|
|
1186
|
+
*/
|
|
1187
|
+
setTokenResyncCallback(callback) {
|
|
1188
|
+
this.tokenResyncCallback = callback;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* Attempt to re-sync tokens from external source (session, etc.)
|
|
1193
|
+
* @returns {Promise<boolean>} true if tokens were successfully re-synced
|
|
1194
|
+
*/
|
|
1195
|
+
async attemptTokenResync() {
|
|
1196
|
+
if (!this.tokenResyncCallback) {
|
|
1197
|
+
return false;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
try {
|
|
1201
|
+
const tokens = await this.tokenResyncCallback();
|
|
1202
|
+
if (tokens && tokens.idToken && tokens.refreshToken) {
|
|
1203
|
+
console.log('[LinkedInAutomation] Tokens re-synced from external source');
|
|
1204
|
+
this._currentAccessToken = tokens.idToken;
|
|
1205
|
+
this._currentRefreshToken = tokens.refreshToken;
|
|
1206
|
+
|
|
1207
|
+
// If tokens are expired, try to refresh them
|
|
1208
|
+
if (this.cloudAuth.isTokenExpired(this._currentAccessToken)) {
|
|
1209
|
+
console.log('[LinkedInAutomation] Re-synced tokens are expired, attempting refresh...');
|
|
1210
|
+
const result = await this.cloudAuth.refreshTokens(this._currentRefreshToken);
|
|
1211
|
+
if (result.success) {
|
|
1212
|
+
this._currentAccessToken = result.tokens.idToken;
|
|
1213
|
+
if (result.tokens.refreshToken) {
|
|
1214
|
+
this._currentRefreshToken = result.tokens.refreshToken;
|
|
1215
|
+
}
|
|
1216
|
+
} else {
|
|
1217
|
+
console.error('[LinkedInAutomation] Failed to refresh re-synced tokens:', result.error);
|
|
1218
|
+
return false;
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
return true;
|
|
1223
|
+
}
|
|
1224
|
+
} catch (error) {
|
|
1225
|
+
console.error('[LinkedInAutomation] Error during token re-sync:', error.message);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
return false;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1056
1231
|
async getAccessToken() {
|
|
1057
|
-
//
|
|
1232
|
+
// First, try to get current token
|
|
1058
1233
|
if (!this._currentAccessToken) {
|
|
1059
|
-
console.
|
|
1060
|
-
|
|
1234
|
+
console.log('[LinkedInAutomation] No access token available, attempting re-sync...');
|
|
1235
|
+
const resynced = await this.attemptTokenResync();
|
|
1236
|
+
if (!resynced) {
|
|
1237
|
+
console.error('[LinkedInAutomation] No access token available and re-sync failed');
|
|
1238
|
+
return null;
|
|
1239
|
+
}
|
|
1240
|
+
// Token was re-synced, continue to validation below
|
|
1061
1241
|
}
|
|
1062
1242
|
|
|
1063
1243
|
// Check if token is expired or expiring soon (within 5 minutes)
|
|
@@ -1066,8 +1246,12 @@ export class LinkedInAutomation {
|
|
|
1066
1246
|
|
|
1067
1247
|
// Check if refresh token is available
|
|
1068
1248
|
if (!this._currentRefreshToken) {
|
|
1069
|
-
console.
|
|
1070
|
-
|
|
1249
|
+
console.log('[LinkedInAutomation] No refresh token available, attempting re-sync...');
|
|
1250
|
+
const resynced = await this.attemptTokenResync();
|
|
1251
|
+
if (!resynced || !this._currentRefreshToken) {
|
|
1252
|
+
console.error('[LinkedInAutomation] No refresh token available and re-sync failed');
|
|
1253
|
+
return null;
|
|
1254
|
+
}
|
|
1071
1255
|
}
|
|
1072
1256
|
|
|
1073
1257
|
// Attempt to refresh the token
|
|
@@ -1081,17 +1265,48 @@ export class LinkedInAutomation {
|
|
|
1081
1265
|
if (result.tokens.refreshToken) {
|
|
1082
1266
|
this._currentRefreshToken = result.tokens.refreshToken;
|
|
1083
1267
|
}
|
|
1268
|
+
// Reset failure counter on successful refresh
|
|
1269
|
+
this.consecutiveAuthFailures = 0;
|
|
1270
|
+
this.authFailurePaused = false;
|
|
1084
1271
|
return this._currentAccessToken;
|
|
1085
1272
|
} else {
|
|
1086
1273
|
console.error('[LinkedInAutomation] Token refresh failed:', result.error);
|
|
1274
|
+
|
|
1275
|
+
// Try one more time to re-sync before giving up
|
|
1276
|
+
console.log('[LinkedInAutomation] Attempting token re-sync after refresh failure...');
|
|
1277
|
+
const resynced = await this.attemptTokenResync();
|
|
1278
|
+
if (resynced && !this.cloudAuth.isTokenExpired(this._currentAccessToken)) {
|
|
1279
|
+
console.log('[LinkedInAutomation] Token re-synced successfully after refresh failure');
|
|
1280
|
+
this.consecutiveAuthFailures = 0;
|
|
1281
|
+
this.authFailurePaused = false;
|
|
1282
|
+
return this._currentAccessToken;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1087
1285
|
return null;
|
|
1088
1286
|
}
|
|
1089
1287
|
} catch (error) {
|
|
1090
1288
|
console.error('[LinkedInAutomation] Error during token refresh:', error.message);
|
|
1289
|
+
|
|
1290
|
+
// Try re-sync one more time
|
|
1291
|
+
const resynced = await this.attemptTokenResync();
|
|
1292
|
+
if (resynced && !this.cloudAuth.isTokenExpired(this._currentAccessToken)) {
|
|
1293
|
+
console.log('[LinkedInAutomation] Token re-synced successfully after refresh error');
|
|
1294
|
+
this.consecutiveAuthFailures = 0;
|
|
1295
|
+
this.authFailurePaused = false;
|
|
1296
|
+
return this._currentAccessToken;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1091
1299
|
return null;
|
|
1092
1300
|
}
|
|
1093
1301
|
}
|
|
1094
1302
|
|
|
1303
|
+
// Token is valid, reset failure counter
|
|
1304
|
+
if (this.consecutiveAuthFailures > 0) {
|
|
1305
|
+
console.log('[LinkedInAutomation] Authentication recovered, resetting failure counter');
|
|
1306
|
+
this.consecutiveAuthFailures = 0;
|
|
1307
|
+
this.authFailurePaused = false;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1095
1310
|
return this._currentAccessToken;
|
|
1096
1311
|
}
|
|
1097
1312
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coffeeinabit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.49",
|
|
4
4
|
"description": "coffeeinabit app",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"type": "module",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"axios": "^1.6.0",
|
|
25
|
+
"coffeeinabit": "^0.0.47",
|
|
25
26
|
"dotenv": "^16.3.1",
|
|
26
27
|
"express": "^4.18.2",
|
|
27
28
|
"express-session": "^1.17.3",
|
package/server.js
CHANGED
|
@@ -37,9 +37,53 @@ const appState = new AppState();
|
|
|
37
37
|
// Legacy single automation instance (for backward compatibility during migration)
|
|
38
38
|
// TODO: Remove after full migration to multi-process state
|
|
39
39
|
const linkedinAutomation = new LinkedInAutomation(io);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
|
|
41
|
+
// Set up token re-sync callback for legacy automation instance
|
|
42
|
+
// This allows the automation to re-sync tokens from the session store when they expire
|
|
43
|
+
linkedinAutomation.setTokenResyncCallback(async () => {
|
|
44
|
+
try {
|
|
45
|
+
// Try to get tokens from session store for the user
|
|
46
|
+
if (!linkedinAutomation._currentUserEmail) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Get all sessions and find one for this user
|
|
51
|
+
if (typeof sessionStore.all === 'function') {
|
|
52
|
+
return new Promise((resolve) => {
|
|
53
|
+
sessionStore.all((err, sessions) => {
|
|
54
|
+
if (err || !sessions) {
|
|
55
|
+
resolve(null);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Find a session for this user
|
|
60
|
+
for (const sessionId in sessions) {
|
|
61
|
+
const session = sessions[sessionId];
|
|
62
|
+
if (session && session.user && session.user.email === linkedinAutomation._currentUserEmail) {
|
|
63
|
+
// Ensure tokens are valid
|
|
64
|
+
cloudAuth.ensureValidToken(session).then(result => {
|
|
65
|
+
if (result.valid && session.tokens) {
|
|
66
|
+
resolve({
|
|
67
|
+
idToken: session.tokens.idToken,
|
|
68
|
+
refreshToken: session.tokens.refreshToken
|
|
69
|
+
});
|
|
70
|
+
} else {
|
|
71
|
+
resolve(null);
|
|
72
|
+
}
|
|
73
|
+
}).catch(() => resolve(null));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
resolve(null);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('[Server] Error in token re-sync callback:', error.message);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
});
|
|
43
87
|
|
|
44
88
|
// Initialize session store
|
|
45
89
|
const FileStoreSession = FileStore(session);
|
|
@@ -95,6 +139,9 @@ app.use('/auth', createAuthRoutes(cloudAuth, PORT, appState, linkedinAutomation)
|
|
|
95
139
|
app.use('/api/automation', createAutomationRoutes(cloudAuth, appState, linkedinAutomation, io));
|
|
96
140
|
app.use('/api/state', createStateRoutes(cloudAuth, appState));
|
|
97
141
|
|
|
142
|
+
// Set up token re-sync dependencies for AppState
|
|
143
|
+
appState.setTokenResyncDependencies(cloudAuth, sessionStore);
|
|
144
|
+
|
|
98
145
|
// Setup WebSocket handlers
|
|
99
146
|
setupSocketHandlers(io, appState);
|
|
100
147
|
|
package/state/app_state.js
CHANGED
|
@@ -18,6 +18,10 @@ export class AppState {
|
|
|
18
18
|
// Private state: Map of socketId -> process state
|
|
19
19
|
this._processes = new Map();
|
|
20
20
|
|
|
21
|
+
// Dependencies for token re-sync (set externally)
|
|
22
|
+
this._cloudAuth = null;
|
|
23
|
+
this._sessionStore = null;
|
|
24
|
+
|
|
21
25
|
// Statistics
|
|
22
26
|
this._stats = {
|
|
23
27
|
totalProcessesCreated: 0,
|
|
@@ -26,6 +30,16 @@ export class AppState {
|
|
|
26
30
|
};
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Set dependencies for token re-sync
|
|
35
|
+
* @param {object} cloudAuth - CloudAuth instance
|
|
36
|
+
* @param {object} sessionStore - Session store instance
|
|
37
|
+
*/
|
|
38
|
+
setTokenResyncDependencies(cloudAuth, sessionStore) {
|
|
39
|
+
this._cloudAuth = cloudAuth;
|
|
40
|
+
this._sessionStore = sessionStore;
|
|
41
|
+
}
|
|
42
|
+
|
|
29
43
|
/**
|
|
30
44
|
* Create a new automation process for a socket connection
|
|
31
45
|
* @param {string} socketId - WebSocket connection ID
|
|
@@ -40,9 +54,54 @@ export class AppState {
|
|
|
40
54
|
}
|
|
41
55
|
|
|
42
56
|
const automation = new LinkedInAutomation(io);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
57
|
+
|
|
58
|
+
// Set up token re-sync callback if dependencies are available
|
|
59
|
+
if (this._cloudAuth && this._sessionStore) {
|
|
60
|
+
const userEmail = session?.user?.email;
|
|
61
|
+
automation.setTokenResyncCallback(async () => {
|
|
62
|
+
try {
|
|
63
|
+
if (!userEmail) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Try to get tokens from session store for this user
|
|
68
|
+
if (typeof this._sessionStore.all === 'function') {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
this._sessionStore.all((err, sessions) => {
|
|
71
|
+
if (err || !sessions) {
|
|
72
|
+
resolve(null);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Find a session for this user
|
|
77
|
+
for (const sessionId in sessions) {
|
|
78
|
+
const sess = sessions[sessionId];
|
|
79
|
+
if (sess && sess.user && sess.user.email === userEmail) {
|
|
80
|
+
// Ensure tokens are valid
|
|
81
|
+
this._cloudAuth.ensureValidToken(sess).then(result => {
|
|
82
|
+
if (result.valid && sess.tokens) {
|
|
83
|
+
resolve({
|
|
84
|
+
idToken: sess.tokens.idToken,
|
|
85
|
+
refreshToken: sess.tokens.refreshToken
|
|
86
|
+
});
|
|
87
|
+
} else {
|
|
88
|
+
resolve(null);
|
|
89
|
+
}
|
|
90
|
+
}).catch(() => resolve(null));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
resolve(null);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('[AppState] Error in token re-sync callback:', error.message);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
46
105
|
|
|
47
106
|
const processState = {
|
|
48
107
|
socketId,
|