Skip to content

Commit 2865a80

Browse files
committed
Finalize session management
1 parent 2addd9b commit 2865a80

14 files changed

Lines changed: 2100 additions & 92 deletions

File tree

backend/ml/__init__.py

Whitespace-only changes.
-18.1 MB
Binary file not shown.
-2.65 MB
Binary file not shown.

backend/services/detections/__init__.py

Whitespace-only changes.

backend/services/downloaders/__init__.py

Whitespace-only changes.
Lines changed: 126 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import '../styles/index.scss';
22
import { FacebookPostObserver } from './observer';
33
import { FloatingChatWindow } from './ui/components/ChatWindow';
4-
import { metricsManager } from './metrics/MetricsManager';
54
import { analytics } from '@/shared/analytics';
5+
import { SessionManager } from '@/shared/SessionManager';
6+
import { initializeGlobalGate, protectedExecute } from '@/shared/InitializationGate';
7+
import { createNavigationWatcher } from './utils/NavigationWatcher';
68
import { isInAllowedGroupNow } from '@/content/utils/group';
9+
import { log, error } from '@/shared/logger';
710

811
declare const __DEV__: boolean;
912
if (!__DEV__) {
@@ -14,64 +17,134 @@ if (!__DEV__) {
1417
console.error = noop;
1518
}
1619

17-
// Entry bootstrap: initialize metrics, observer and chat UI
18-
(async () => {
20+
// Global instances
21+
let navigationWatcher: ReturnType<typeof createNavigationWatcher> | null = null;
22+
let postObserver: FacebookPostObserver | null = null;
23+
let chatWindow: FloatingChatWindow | null = null;
24+
25+
/**
26+
* Initialize extension functionality - only called after session validation
27+
*/
28+
async function initializeExtensionFeatures(): Promise<void> {
1929
try {
20-
// Check if we're in an allowed group
21-
const allowed = isInAllowedGroupNow();
22-
23-
// CRITICAL: Only enable analytics and metrics AFTER user/session verification
24-
if (allowed) {
25-
// First initialize metrics manager which will verify/init user and session
26-
await metricsManager.initialize();
30+
await protectedExecute(async () => {
31+
log('Initializing extension features');
32+
33+
// Initialize post observer with protection
34+
postObserver = new FacebookPostObserver();
35+
36+
// Initialize chat window with protection
37+
chatWindow = new FloatingChatWindow();
2738

28-
// Only after successful verification, enable Mixpanel analytics
39+
// Enable and initialize analytics
2940
analytics.setEnabled(true);
3041
analytics.init();
31-
} else {
32-
// Not in allowed group - disable analytics
33-
analytics.setEnabled(false);
34-
// Observe future SPA navigations to enter allowed context
35-
const wrapHistory = (method: 'pushState' | 'replaceState') => {
36-
type PushReplace = (data: unknown, unused: string, url?: string | URL | null) => unknown;
37-
const orig = history[method].bind(history) as PushReplace;
38-
(history as unknown as Record<string, unknown>)[method] = ((
39-
data: unknown,
40-
unused: string,
41-
url?: string | URL | null
42-
) => {
43-
const ret = orig(data, unused, url);
44-
window.dispatchEvent(new Event('locationchange'));
45-
return ret as unknown as void;
46-
}) as History[typeof method];
47-
};
48-
wrapHistory('pushState');
49-
wrapHistory('replaceState');
50-
window.addEventListener('popstate', () => window.dispatchEvent(new Event('locationchange')));
51-
let chatInitialized = false;
52-
window.addEventListener('locationchange', async () => {
53-
if (isInAllowedGroupNow()) {
54-
// Initialize metrics manager first (verifies user/session)
55-
await metricsManager.initialize().catch(() => {});
56-
57-
// Only after verification, enable analytics
58-
analytics.setEnabled(true);
59-
analytics.init();
60-
61-
if (!chatInitialized) {
62-
new FloatingChatWindow();
63-
chatInitialized = true;
64-
}
65-
}
42+
43+
log('Extension features initialized successfully');
44+
}, 'initializeExtensionFeatures');
45+
} catch (err) {
46+
error('Failed to initialize extension features', err);
47+
throw err;
48+
}
49+
}
50+
51+
/**
52+
* Set up navigation watcher for URL monitoring
53+
*/
54+
function setupNavigationWatcher(sessionManager: SessionManager): void {
55+
if (navigationWatcher) {
56+
navigationWatcher.destroy();
57+
}
58+
59+
navigationWatcher = createNavigationWatcher(sessionManager, {
60+
enableLogging: __DEV__,
61+
debounceMs: 100
62+
});
63+
64+
log('Navigation watcher initialized');
65+
}
66+
67+
/**
68+
* Clean up extension resources
69+
*/
70+
function cleanupExtension(): void {
71+
if (navigationWatcher) {
72+
navigationWatcher.destroy();
73+
navigationWatcher = null;
74+
}
75+
76+
if (postObserver) {
77+
// FacebookPostObserver doesn't have destroy method yet - will add in next update
78+
postObserver = null;
79+
}
80+
81+
if (chatWindow) {
82+
// FloatingChatWindow doesn't have destroy method yet - will add in next update
83+
chatWindow = null;
84+
}
85+
86+
analytics.setEnabled(false);
87+
log('Extension resources cleaned up');
88+
}
89+
90+
// Main entry point with new architecture
91+
(async () => {
92+
try {
93+
log('Content script starting with new architecture');
94+
95+
// STEP 1: Check if we're in an allowed group
96+
const isInAllowedGroup = isInAllowedGroupNow();
97+
98+
if (!isInAllowedGroup) {
99+
log('Not in allowed group, setting up navigation watcher only');
100+
101+
// Create session manager and navigation watcher for monitoring
102+
const sessionManager = SessionManager.getInstance({
103+
requireValidSession: true,
104+
enableLogging: __DEV__
66105
});
106+
107+
setupNavigationWatcher(sessionManager);
108+
109+
// Exit early - no extension functionality until we enter allowed group
110+
return;
67111
}
68-
69-
// Then initialize the main functionality
70-
new FacebookPostObserver();
71-
if (allowed) {
72-
new FloatingChatWindow();
112+
113+
log('In allowed group, initializing session and extension');
114+
115+
// STEP 2: Initialize SessionManager
116+
const sessionManager = SessionManager.getInstance({
117+
requireValidSession: true,
118+
enableLogging: __DEV__
119+
});
120+
121+
// STEP 3: Initialize InitializationGate with SessionManager
122+
// This will block until user/session are validated
123+
await initializeGlobalGate(sessionManager);
124+
125+
// STEP 4: Set up navigation watcher now that we have valid session
126+
setupNavigationWatcher(sessionManager);
127+
128+
// STEP 5: Only after valid session, initialize extension functionality
129+
await initializeExtensionFeatures();
130+
131+
log('Content script initialization complete');
132+
133+
} catch (err) {
134+
error('Failed to initialize content script with new architecture', err);
135+
136+
// Fallback: clean up any partial initialization
137+
cleanupExtension();
138+
139+
// Still set up navigation watcher in case user navigates to allowed group later
140+
try {
141+
const fallbackSessionManager = SessionManager.getInstance({
142+
requireValidSession: false,
143+
enableLogging: __DEV__
144+
});
145+
setupNavigationWatcher(fallbackSessionManager);
146+
} catch (fallbackErr) {
147+
error('Failed to set up fallback navigation watcher', fallbackErr);
73148
}
74-
} catch (error) {
75-
console.error('Failed to initialize content script:', error);
76149
}
77150
})();

browser-extension/src/content/observer.ts

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { metricsManager } from './metrics/MetricsManager';
66
import { POST_CONTENT_SELECTOR as POST_SELECTOR } from '@/content/dom/selectors';
77
import { isInAllowedGroupNow } from '@/content/utils/group';
88
import { ChatMessage } from '@/content/types';
9+
import { requireGlobalInitialization, protectedExecute } from '@/shared/InitializationGate';
910
// Chat session metrics state attached to chat window elements
1011
interface ChatMetrics {
1112
sessionId: string;
@@ -108,11 +109,18 @@ export class FacebookPostObserver {
108109

109110
/**
110111
* Ensures a valid persistent user id exists for chat/analytics.
111-
* Falls back to generating and storing a UUID if missing.
112+
* Now uses session-validated user ID from InitializationGate.
112113
*/
113114
private ensureUserId(): string {
114-
// User IDs are backend-generated and persisted in localStorage by MetricsManager
115-
return getUserId();
115+
try {
116+
// Get validated user ID from session
117+
const sessionData = requireGlobalInitialization();
118+
return sessionData.userId;
119+
} catch (err) {
120+
// Fallback to storage method if session not ready (shouldn't happen in normal flow)
121+
logError('Session not ready when getting user ID', err);
122+
return getUserId();
123+
}
116124
}
117125

118126
/**
@@ -216,6 +224,7 @@ export class FacebookPostObserver {
216224
log('⚙️ Observer setup complete');
217225
console.groupEnd();
218226

227+
// Initialize with session validation - this should only be called after session is ready
219228
this.initialize();
220229
}
221230

@@ -573,17 +582,20 @@ export class FacebookPostObserver {
573582
},
574583
});
575584

576-
// Send analysis request to backend via background service
585+
// Send analysis request to backend via background service with session protection
577586
const t0 = performance.now();
578-
const response = await sendAiSlopRequest({
579-
content,
580-
postId,
581-
imageUrls: mediaUrls.images,
582-
videoUrls: mediaUrls.videos,
583-
postUrl: mediaUrls.postUrl,
584-
hasVideos: mediaUrls.hasVideos,
585-
videoResults: videoResults,
586-
});
587+
const response = await protectedExecute(async () => {
588+
requireGlobalInitialization(); // Ensure session is valid
589+
return await sendAiSlopRequest({
590+
content,
591+
postId,
592+
imageUrls: mediaUrls.images,
593+
videoUrls: mediaUrls.videos,
594+
postUrl: mediaUrls.postUrl,
595+
hasVideos: mediaUrls.hasVideos,
596+
videoResults: videoResults,
597+
});
598+
}, 'sendAiSlopRequest');
587599
const t1 = performance.now();
588600

589601
// Track response
@@ -1839,14 +1851,17 @@ export class FacebookPostObserver {
18391851
category: 'chat',
18401852
metadata: { postId },
18411853
});
1842-
// Send message to background script to handle chat API
1843-
const response = await sendChat({
1844-
postId: postId,
1845-
message: message,
1846-
userId: this.ensureUserId(),
1847-
postContent: postContent,
1848-
previousAnalysis: previousAnalysis,
1849-
});
1854+
// Send message to background script to handle chat API with session protection
1855+
const response = await protectedExecute(async () => {
1856+
const sessionData = requireGlobalInitialization(); // Ensure session is valid
1857+
return await sendChat({
1858+
postId: postId,
1859+
message: message,
1860+
userId: sessionData.userId,
1861+
postContent: postContent,
1862+
previousAnalysis: previousAnalysis,
1863+
});
1864+
}, 'sendChat');
18501865

18511866
if ('error' in response) {
18521867
// Keep loader bubble to display error message in catch
@@ -1931,11 +1946,15 @@ export class FacebookPostObserver {
19311946
if (!messagesContainer) return;
19321947

19331948
try {
1934-
const userId = this.ensureUserId();
1935-
log(`[AI-Slop] Loading chat history for post ${postId}, user ${userId}`);
1936-
1937-
// Delegate network call to background for consistent CORS/timeout handling
1938-
const historyData = await fetchChatHistory({ postId, userId });
1949+
// Load chat history with session protection
1950+
const historyData = await protectedExecute(async () => {
1951+
const sessionData = requireGlobalInitialization(); // Ensure session is valid
1952+
log(`[AI-Slop] Loading chat history for post ${postId}, user ${sessionData.userId}`);
1953+
1954+
// Delegate network call to background for consistent CORS/timeout handling
1955+
return await fetchChatHistory({ postId, userId: sessionData.userId });
1956+
}, 'fetchChatHistory');
1957+
19391958
log(`[AI-Slop] Loaded ${historyData.total_messages} previous messages`);
19401959

19411960
metricsManager.trackEvent({

browser-extension/src/content/ui/components/ChatWindow.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Messenger-style floating chat window component
22
import { metricsManager } from '@/content/metrics/MetricsManager';
3+
import { requireGlobalInitialization, protectedExecute } from '@/shared/InitializationGate';
34

45
export class FloatingChatWindow {
56
/** Chat window container */
@@ -19,6 +20,7 @@ export class FloatingChatWindow {
1920
private profileStatus: HTMLParagraphElement | null = null;
2021

2122
constructor() {
23+
// Setup message listener - this component is only created after session validation
2224
this.setupMessageListener();
2325
}
2426

0 commit comments

Comments
 (0)