mirror of
https://github.com/guoriyue/AutoMouser.git
synced 2026-06-03 21:02:31 +08:00
205 lines
7.8 KiB
JavaScript
205 lines
7.8 KiB
JavaScript
// Handle track results - send to backend API and redirect to results page
|
|
import { checkAuth, getAuthToken } from './auth.js';
|
|
import { captureSessionData, detectAuthMethod, captureComprehensiveAuthData } from '../util/session_utils.js';
|
|
import { API_CONFIG, getAuthHeaders } from './config.js';
|
|
|
|
/**
|
|
* Send track data to backend and redirect to results page
|
|
* @param {Array} trackingLog - Array of recorded actions
|
|
* @param {number} popupWindowId - ID of the processing popup window
|
|
* @param {boolean} actionOnlyMode - Whether to skip backend submission
|
|
* @param {Object} initialState - Initial webpage state (window, scroll, viewport)
|
|
*/
|
|
export async function sendTrackToBackend(trackingLog, popupWindowId, actionOnlyMode = false, initialState = null) {
|
|
// Additional failsafe check for actionOnlyMode
|
|
if (actionOnlyMode) {
|
|
console.log('FAILSAFE: actionOnlyMode is active in sendTrackToBackend - aborting API call');
|
|
return { success: false, error: 'Action only mode is active' };
|
|
}
|
|
|
|
// Check authentication first
|
|
const authStatus = await checkAuth();
|
|
|
|
if (!authStatus.isAuthenticated) {
|
|
// This shouldn't happen as auth is checked before recording starts
|
|
console.error('Not authenticated when trying to save track');
|
|
|
|
// Close processing popup
|
|
if (popupWindowId) {
|
|
chrome.windows.remove(popupWindowId).catch(() => {});
|
|
}
|
|
|
|
// Show error notification
|
|
chrome.notifications.create({
|
|
type: 'basic',
|
|
iconUrl: chrome.runtime.getURL('icons/icon-48.png'),
|
|
title: 'Authentication Error',
|
|
message: 'Please log in and try recording again.',
|
|
priority: 2
|
|
});
|
|
|
|
return { success: false, error: 'Not authenticated' };
|
|
}
|
|
|
|
console.log('Sending trackingLog to /track/save:', trackingLog);
|
|
console.log('Number of actions:', trackingLog.length);
|
|
try {
|
|
// Get current tab info
|
|
const [activeTab] = await chrome.tabs.query({active: true, currentWindow: true});
|
|
|
|
// Get auth token
|
|
const token = await getAuthToken();
|
|
|
|
// Capture session data for the recorded site
|
|
const sessionData = await captureSessionData();
|
|
const authMethods = detectAuthMethod(sessionData);
|
|
|
|
// Capture comprehensive auth data from all domains visited during recording
|
|
const comprehensiveAuthData = await captureComprehensiveAuthData(trackingLog);
|
|
|
|
|
|
// Prepare request body matching server expectations
|
|
const requestBody = {
|
|
url: activeTab?.url || '',
|
|
title: activeTab?.title || '',
|
|
actions: trackingLog,
|
|
// Optional metadata can be included if server supports it
|
|
metadata: {
|
|
timestamp: Date.now(),
|
|
extensionVersion: chrome.runtime.getManifest().version,
|
|
actionsCount: trackingLog.length
|
|
},
|
|
sessionData: {
|
|
// Only send non-sensitive auth indicators
|
|
hasAuthentication: authMethods.length > 0,
|
|
authMethods: authMethods.map(m => ({ type: m.type, name: m.name || m.key })),
|
|
authAnalysis: {
|
|
hasLoginForm: false,
|
|
hasLogoutButton: false,
|
|
loginActions: [],
|
|
cookies: [],
|
|
localStorage: [],
|
|
sessionStorage: []
|
|
},
|
|
// Don't send actual cookie/token values for security
|
|
requiresAuth: sessionData.cookies.some(c =>
|
|
c.name.toLowerCase().includes('session') ||
|
|
c.name.toLowerCase().includes('auth')
|
|
),
|
|
},
|
|
// Add comprehensive auth data for Playwright automation
|
|
playwrightAuth: {
|
|
domainData: comprehensiveAuthData,
|
|
visitedDomains: [...new Set(trackingLog.map(action => {
|
|
try {
|
|
return action.url ? new URL(action.url).hostname : null;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}).filter(Boolean))]
|
|
}
|
|
};
|
|
|
|
console.log('Request body:', JSON.stringify(requestBody, null, 2));
|
|
console.log('Request headers:', getAuthHeaders(token));
|
|
|
|
// Send tracking data to backend
|
|
// Note: Chrome extensions are not subject to CORS when making requests from background scripts
|
|
const response = await fetch(API_CONFIG.RESULTS.endpoint, {
|
|
method: 'POST',
|
|
headers: getAuthHeaders(token),
|
|
body: JSON.stringify(requestBody)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text().catch(() => 'Unknown error');
|
|
const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
error.status = response.status;
|
|
error.name = 'HTTP_ERROR';
|
|
error.responseText = errorText;
|
|
throw error;
|
|
}
|
|
|
|
const result = await response.json();
|
|
|
|
// Close the popup window if it exists
|
|
if (popupWindowId) {
|
|
chrome.windows.remove(popupWindowId).catch(() => {
|
|
// Window might already be closed
|
|
});
|
|
}
|
|
|
|
// Open the results page on InverseUI using track_id
|
|
const trackUrl = `${API_CONFIG.RESULTS.viewUrl}${result.track_id}`;
|
|
chrome.tabs.create({
|
|
url: trackUrl
|
|
});
|
|
|
|
return { success: true, trackId: result.track_id, trackUrl: trackUrl };
|
|
|
|
} catch (error) {
|
|
console.error('Failed to send to InverseUI:', error);
|
|
|
|
// Handle error - fallback to local download
|
|
return handleUploadError(trackingLog, popupWindowId, error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle upload errors with fallback to local download
|
|
* @param {Array} trackingLog - Array of recorded actions
|
|
* @param {number} popupWindowId - ID of the processing popup window
|
|
* @param {Error} error - The error that occurred
|
|
*/
|
|
async function handleUploadError(trackingLog, popupWindowId, error) {
|
|
// Close popup if it exists
|
|
if (popupWindowId) {
|
|
chrome.windows.remove(popupWindowId).catch(() => {
|
|
// Window might already be closed
|
|
});
|
|
}
|
|
|
|
// Save recording locally as backup
|
|
const recordingId = `track_${Date.now()}`;
|
|
const jsonData = JSON.stringify(trackingLog, null, 2);
|
|
const dataUrl = 'data:application/json;charset=utf-8,' + encodeURIComponent(jsonData);
|
|
|
|
chrome.downloads.download({
|
|
url: dataUrl,
|
|
filename: `inverseui_${recordingId}.json`,
|
|
saveAs: true
|
|
});
|
|
|
|
// Determine error details
|
|
const errorCode = error.name || 'UNKNOWN_ERROR';
|
|
const errorMessage = error.message || 'An unknown error occurred';
|
|
const httpStatus = error.status || '';
|
|
|
|
// Build error page URL with details
|
|
const errorUrl = new URL(`${API_CONFIG.RESULTS.viewUrl.replace('/track/', '/error')}`);
|
|
errorUrl.searchParams.set('code', errorCode);
|
|
errorUrl.searchParams.set('message', errorMessage);
|
|
errorUrl.searchParams.set('recording_id', recordingId);
|
|
errorUrl.searchParams.set('actions_count', trackingLog.length.toString());
|
|
errorUrl.searchParams.set('timestamp', Date.now().toString());
|
|
|
|
if (httpStatus) {
|
|
errorUrl.searchParams.set('status', httpStatus.toString());
|
|
}
|
|
|
|
// Open error page on InverseUI website
|
|
chrome.tabs.create({
|
|
url: errorUrl.toString()
|
|
});
|
|
|
|
// Also show notification
|
|
chrome.notifications.create({
|
|
type: 'basic',
|
|
iconUrl: chrome.runtime.getURL('icons/icon-48.png'),
|
|
title: 'Upload Failed',
|
|
message: 'Failed to send to InverseUI. Opening error details...',
|
|
priority: 2
|
|
});
|
|
|
|
return { success: false, error: error.message };
|
|
} |