freshroute-server
Version:
Local development server for FreshRoute extension with API mocking and extended functionality
1,110 lines (970 loc) โข 54.2 kB
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FreshRoute Setup - Welcome</title>
<link rel="icon" href="./extension/icons/icon48.png" type="image/png">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
color: #333;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
background: white;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
color: white;
padding: 40px;
text-align: center;
}
.header-icon {
width: 64px;
height: 64px;
margin: 0 auto 20px;
display: block;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
font-weight: 300;
}
.header p {
font-size: 1.2em;
opacity: 0.9;
}
.content {
padding: 40px;
}
.status-card {
background: #f8f9fa;
border-radius: 12px;
padding: 20px;
margin-bottom: 30px;
border-left: 4px solid #4CAF50;
}
.status-card.warning {
border-left-color: #ff9800;
background: #fff3e0;
}
.status-card.error {
border-left-color: #f44336;
background: #ffebee;
}
.chrome-dev-notice {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
color: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 30px;
text-align: center;
}
.chrome-dev-notice h3 {
margin-bottom: 10px;
font-size: 1.3em;
}
.chrome-dev-notice p {
opacity: 0.9;
margin-bottom: 15px;
}
.setup-steps {
display: grid;
gap: 20px;
margin: 30px 0;
}
.step {
background: white;
border: 2px solid #e0e0e0;
border-radius: 12px;
padding: 25px;
transition: all 0.3s ease;
}
.step:hover {
border-color: #4CAF50;
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
}
.step-number {
background: #4CAF50;
color: white;
width: 30px;
height: 30px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-weight: bold;
margin-right: 15px;
}
.step-title {
font-size: 1.3em;
font-weight: 600;
margin-bottom: 10px;
display: flex;
align-items: center;
}
.step-content {
margin-left: 45px;
color: #666;
}
.code-block {
background: #2d3748;
color: #e2e8f0;
padding: 15px;
border-radius: 8px;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 0.9em;
margin: 10px 0;
overflow-x: auto;
position: relative;
}
.copy-btn {
position: absolute;
top: 10px;
right: 10px;
background: #4CAF50;
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 0.8em;
}
.copy-btn:hover {
background: #45a049;
}
.button {
display: inline-block;
background: #4CAF50;
color: white;
padding: 12px 24px;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
margin: 5px;
border: none;
cursor: pointer;
}
.button:hover {
background: #45a049;
transform: translateY(-1px);
}
.button.secondary {
background: #6c757d;
}
.button.secondary:hover {
background: #5a6268;
}
.button.danger {
background: #dc3545;
}
.button.danger:hover {
background: #c82333;
}
.quick-links {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 30px 0;
}
.quick-link {
background: #f8f9fa;
padding: 20px;
border-radius: 12px;
text-align: center;
text-decoration: none;
color: #333;
transition: all 0.3s ease;
cursor: pointer;
}
.quick-link:hover {
background: #e9ecef;
transform: translateY(-2px);
}
.icon {
font-size: 2em;
margin-bottom: 10px;
display: block;
}
.footer {
background: #f8f9fa;
padding: 30px;
text-align: center;
color: #666;
}
.chrome-managed {
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 8px;
padding: 15px;
margin: 15px 0;
}
.chrome-managed h4 {
color: #856404;
margin-bottom: 10px;
}
.tip-box {
background: #e3f2fd;
border-left: 4px solid #2196f3;
padding: 15px;
margin: 15px 0;
border-radius: 4px;
}
.tip-box h4 {
color: #1976d2;
margin-bottom: 8px;
}
.extension-status {
padding: 10px;
border-radius: 8px;
margin: 10px 0;
font-weight: 500;
}
.extension-status.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.extension-status.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.extension-status.warning {
background: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
}
.modal-content {
background-color: white;
margin: 5% auto;
padding: 30px;
border-radius: 12px;
width: 80%;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close:hover {
color: black;
}
.collapsible-content {
display: none;
margin-top: 15px;
padding: 15px;
border-radius: 8px;
}
.instruction-panel {
background: #fff3e0;
border-left: 4px solid #ff9800;
}
.manual-steps-panel {
background: #f8f9fa;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<img src="./extension/icons/icon128.png" alt="FreshRoute" class="header-icon">
<h1>FreshRoute Setup</h1>
<p>API Mocking & URL Rewriting Made Easy</p>
</div>
<div class="content">
<div class="status-card" id="welcomeMessage">
<h3>๐ Welcome to FreshRoute!</h3>
<p>Let's get you set up quickly. We'll automatically detect your extension status and guide you through any needed steps.</p>
<div style="margin-top: 15px;">
<div id="autoCheckStatus" class="extension-status warning">
๐ <strong>Checking for FreshRoute extension...</strong>
<br><small>Please wait while we detect your extension status...</small>
</div>
</div>
</div>
<div class="chrome-dev-notice" id="chromeDevNotice" style="display: none;">
<h3>๐ด Development Chrome Active</h3>
<p>Perfect! You're using Chrome with CORS disabled. Extensions need to be manually loaded in this mode.</p>
</div>
<div class="status-card warning" id="serverStatus">
<h3>โ ๏ธ Server Status: Not Required for Setup</h3>
<p>The FreshRoute server is <strong>not required</strong> for viewing this setup guide.</p>
<p>You only need to start the server when you want to use <strong>API mocking</strong> or <strong>response header overrides</strong>.</p>
<div style="margin-top: 15px;">
<button class="button" onclick="ServerManager.checkStatus()">๐ Check Server Status</button>
<button class="button secondary" onclick="GuideManager.show('server-instructions')">๐ Server Instructions</button>
</div>
</div>
<div class="setup-steps">
<div class="step" id="extensionStep">
<div class="step-title">
<span class="step-number">1</span>
Chrome Extension Setup
</div>
<div class="step-content">
<div id="extensionStatus">
<div class="extension-status warning">
๐ <strong>Checking extension status...</strong>
<br><small>We'll automatically detect your extension and guide you through setup.</small>
</div>
</div>
<!-- Installation instructions (hidden by default) -->
<div id="installationInstructions" style="display: none;">
<div style="margin: 15px 0;">
<button class="button" onclick="GuideManager.toggleSection('chromeExtensionsInstructions')">๐ง How to Open Chrome Extensions</button>
<button class="button secondary" onclick="GuideManager.toggleSection('manualSteps')">๐ Step-by-Step Guide</button>
<button class="button secondary" onclick="Utils.copyExtensionPath()">๐ Copy Extension Path</button>
</div>
<div id="chromeExtensionsInstructions" class="collapsible-content instruction-panel">
<h4>๐ง How to Open Chrome Extensions Page:</h4>
<p style="margin: 10px 0;"><strong>Method 1:</strong> Type in address bar:</p>
<div class="code-block">
<span id="chromeExtensionsUrl">chrome://extensions/</span>
<button class="copy-btn" onclick="Utils.copyText('chromeExtensionsUrl', this)">Copy</button>
</div>
<p style="margin: 10px 0;"><strong>Method 2:</strong> Use Chrome menu โ More tools โ Extensions</p>
</div>
<div id="manualSteps" class="collapsible-content manual-steps-panel">
<h4>๐ Manual Installation Steps:</h4>
<ol style="margin: 10px 0; padding-left: 20px; line-height: 1.8;">
<li>Open Chrome Extensions page (see instructions above)</li>
<li>Enable <strong>"Developer mode"</strong> (toggle in top-right corner)</li>
<li>Click <strong>"Load unpacked"</strong> button</li>
<li>Navigate to the extension folder:
<div class="code-block">
<span id="extensionPath">~/.freshroute/extension</span>
<button class="copy-btn" onclick="Utils.copyText('extensionPath', this)">Copy</button>
</div>
</li>
<li>Select the folder and click <strong>"Select Folder"</strong></li>
<li>The FreshRoute extension should now appear in your extensions list</li>
</ol>
</div>
</div>
<div style="margin: 15px 0;">
<button class="button secondary" onclick="ExtensionManager.checkStatus()">๐ Check Again</button>
<button class="button secondary" onclick="ExtensionManager.testConnection()">๐งช Test Connection</button>
</div>
</div>
</div>
<div class="step">
<div class="step-title">
<span class="step-number">2</span>
Start FreshRoute Server (Optional)
</div>
<div class="step-content">
<p>The server is only needed for <strong>API mocking</strong> and <strong>response header overrides</strong>. URL rewriting works without it!</p>
<div class="tip-box">
<h4>๐ก When do you need the server?</h4>
<ul style="margin: 5px 0; padding-left: 20px;">
<li><strong>API Mocking:</strong> Replace real API responses with custom mock data</li>
<li><strong>Response Headers:</strong> Add custom headers (only headers starting with 'x-' can be added without server)</li>
<li><strong>Dashboard Access:</strong> Create and manage mock rules via web interface</li>
</ul>
</div>
<h4>๐ Starting the Server:</h4>
<div class="code-block">
<span>freshroute start --silent</span>
<button class="copy-btn" onclick="Utils.copyText('startCommand', this)">Copy</button>
</div>
<span id="startCommand" style="display: none;">freshroute start --silent</span>
<p style="margin: 5px 0; font-size: 0.9em; color: #666;">Runs in background, doesn't block your terminal</p>
<div style="margin: 15px 0;">
<button class="button" onclick="ServerManager.checkStatus()">๐ Check Server Status</button>
<button class="button secondary" onclick="GuideManager.show('server-commands')">๐ Server Commands</button>
</div>
</div>
</div>
<div class="step">
<div class="step-title">
<span class="step-number">3</span>
Start Using FreshRoute
</div>
<div class="step-content">
<p>Access the dashboard and extension features to create mock rules and manage API responses.</p>
<div style="margin: 15px 0;">
<button class="button secondary" onclick="GuideManager.show('extension-controls')">๐๏ธ Extension Controls</button>
<button class="button secondary" onclick="GuideManager.show('usage-guide')">๐ Usage Guide</button>
</div>
</div>
</div>
</div>
<div class="quick-links">
<div class="quick-link" onclick="GuideManager.show('network-recorder')">
<span class="icon">๐ก</span>
<h3>Network Recorder</h3>
<p>Capture real requests to create mocks</p>
</div>
<div class="quick-link" onclick="GuideManager.show('extension-guide')">
<span class="icon">โ๏ธ</span>
<h3>Extension Guide</h3>
<p>Learn how to use the extension</p>
</div>
<div class="quick-link" onclick="GuideManager.show('allowed-domains')">
<span class="icon">๐</span>
<h3>Allowed Domains</h3>
<p>View permitted development domains</p>
</div>
<div class="quick-link" onclick="GuideManager.show('troubleshooting')">
<span class="icon">๐ง</span>
<h3>Troubleshooting</h3>
<p>Fix common issues and problems</p>
</div>
</div>
</div>
<div class="footer">
<p><strong>FreshRoute v2.0.0</strong> - API Mocking & URL Rewriting Tool</p>
<p>Server available at <code>http://localhost:3001</code> (when running)</p>
<p style="margin-top: 10px; font-size: 0.9em; opacity: 0.8;">
๐ก <strong>Tip:</strong> Keep this Chrome window open while developing to maintain CORS-disabled mode
</p>
<p style="margin-top: 5px; font-size: 0.9em; opacity: 0.8;">
๐ <strong>Server:</strong> Only needed for API mocking and response header overrides
</p>
</div>
</div>
<!-- Modal for guides and help -->
<div id="helpModal" class="modal">
<div class="modal-content">
<span class="close" onclick="GuideManager.closeModal()">×</span>
<div id="modalContent"></div>
</div>
</div>
<script>
// Utility functions
const Utils = {
copyText: async function(elementId, buttonElement = null) {
const element = document.getElementById(elementId);
const text = element.textContent;
if (!buttonElement) {
const codeBlock = element.closest('.code-block');
if (codeBlock) {
buttonElement = codeBlock.querySelector('.copy-btn');
}
}
try {
if (navigator.clipboard) {
await navigator.clipboard.writeText(text);
} else {
this.fallbackCopy(text);
}
this.showCopyFeedback(buttonElement);
} catch (error) {
this.fallbackCopy(text);
this.showCopyFeedback(buttonElement);
}
},
fallbackCopy: function(text) {
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
},
showCopyFeedback: function(buttonElement) {
if (!buttonElement) return;
const originalText = buttonElement.textContent;
const originalColor = buttonElement.style.backgroundColor;
buttonElement.textContent = 'โ Copied!';
buttonElement.style.backgroundColor = '#4CAF50';
buttonElement.style.color = 'white';
setTimeout(() => {
buttonElement.textContent = originalText;
buttonElement.style.backgroundColor = originalColor;
buttonElement.style.color = '';
}, 2000);
},
copyExtensionPath: function() {
this.copyText('extensionPath', event.target);
},
updateStatus: function(elementId, content, className = '') {
const element = document.getElementById(elementId);
if (element) {
element.innerHTML = content;
if (className) {
element.className = className;
}
}
},
scrollToElement: function(selector, behavior = 'smooth') {
const element = document.querySelector(selector);
if (element) {
element.scrollIntoView({ behavior, block: 'center' });
}
}
};
// Server management
const ServerManager = {
checkStatus: function() {
const statusDiv = document.getElementById('serverStatus');
statusDiv.innerHTML = '<div class="status-card warning"><h3>๐ Checking server status...</h3></div>';
fetch('http://localhost:3001/health')
.then(response => response.json())
.then(data => {
statusDiv.innerHTML = `
<div class="status-card">
<h3>โ
Server Status: Running</h3>
<p>FreshRoute server is running on <strong>http://localhost:3001</strong></p>
<div style="margin-top: 15px;">
<button class="button secondary" onclick="GuideManager.show('server-commands')">๐ Server Commands</button>
</div>
</div>`;
})
.catch(() => {
statusDiv.innerHTML = `
<div class="status-card error">
<h3>โ Server Status: Not Running</h3>
<p>The FreshRoute server is not currently running.</p>
<p>Start it with: <code>freshroute start</code></p>
<div style="margin-top: 15px;">
<button class="button" onclick="GuideManager.show('server-instructions')">๐ How to Start Server</button>
<button class="button secondary" onclick="ServerManager.checkStatus()">๐ Check Again</button>
</div>
</div>`;
});
}
};
// Extension management
const ExtensionManager = {
checkStatus: function() {
const statusDiv = document.getElementById('extensionStatus') || document.getElementById('autoCheckStatus');
Utils.updateStatus(statusDiv.id, '๐ <strong>Checking extension status...</strong><br><small>Scanning for installed extension...</small>', 'extension-status warning');
// Check for extension by looking for injected DOM elements
this.checkForExtensionElements((extensionData) => {
statusDiv.innerHTML = `
โ
<strong>Extension detected and working!</strong>
<br><small>Status: ${extensionData.enabled ? 'Enabled' : 'Disabled'} | Inactivity Timer: ${extensionData.inactivityTimeout}min</small>
<br><small>Floating Toggle: ${extensionData.hasFloatingToggle ? 'Active' : 'Not visible'}</small>`;
statusDiv.className = 'extension-status success';
StepManager.markInstallationComplete();
WelcomeManager.updateStatus('success');
document.getElementById('installationInstructions').style.display = 'none';
}, () => {
statusDiv.innerHTML = `
โ <strong>Extension not found</strong>
<br><small>Please install the Chrome extension to continue.</small>`;
statusDiv.className = 'extension-status error';
StepManager.highlightInstallation();
WelcomeManager.updateStatus('error');
document.getElementById('installationInstructions').style.display = 'block';
});
},
checkForExtensionElements: function(onSuccess, onError, timeout = 5000) {
let attempts = 0;
const maxAttempts = 10;
const checkInterval = 200; // Check every 200ms
const checkForElements = () => {
attempts++;
// Check for floating toggle (most reliable indicator)
const floatingToggle = document.getElementById('freshroute-floating-toggle');
// Check for notification styles (another indicator)
const notificationStyles = document.querySelector('style[data-freshroute-notifications]');
// Check for any FreshRoute-related elements
const freshRouteElements = document.querySelectorAll('[id*="freshroute"], [class*="freshroute"]');
// Check for global FreshRoute objects
const hasGlobalObjects = window.FreshRouteNotifications || window.debugExtensionHeaders;
if (floatingToggle || notificationStyles || freshRouteElements.length > 0 || hasGlobalObjects) {
// Extension is present and active
const extensionData = {
hasFloatingToggle: !!floatingToggle,
hasNotificationStyles: !!notificationStyles,
elementsCount: freshRouteElements.length,
hasGlobalObjects: !!hasGlobalObjects,
enabled: floatingToggle ? !floatingToggle.textContent.includes('OFF') : true,
inactivityTimeout: 30 // Default to 30 minutes
};
// Try to get more details from floating toggle
if (floatingToggle) {
const toggleText = floatingToggle.textContent;
extensionData.enabled = toggleText.includes('ON');
// Extract timer info if present
const timerMatch = toggleText.match(/(\d+)m/);
if (timerMatch) {
extensionData.remainingTime = timerMatch[1];
}
}
onSuccess(extensionData);
return;
}
// Continue checking if we haven't reached max attempts
if (attempts < maxAttempts) {
setTimeout(checkForElements, checkInterval);
} else {
onError();
}
};
// Start checking immediately
checkForElements();
},
testConnection: function() {
const statusDiv = document.getElementById('extensionStatus');
Utils.updateStatus(statusDiv.id, '๐งช <strong>Testing extension functionality...</strong>', 'extension-status warning');
// Test extension by checking multiple indicators
this.checkForExtensionElements((extensionData) => {
// Additional functionality tests
const hasFloatingToggle = extensionData.hasFloatingToggle;
const hasGlobalFunctions = !!window.FreshRouteNotifications;
const canAccessNotifications = hasGlobalFunctions && typeof window.FreshRouteNotifications.count === 'function';
let functionalityScore = 0;
let functionalityDetails = [];
if (hasFloatingToggle) {
functionalityScore += 25;
functionalityDetails.push('โ
Floating toggle active');
}
if (hasGlobalFunctions) {
functionalityScore += 25;
functionalityDetails.push('โ
Global functions available');
}
if (canAccessNotifications) {
functionalityScore += 25;
functionalityDetails.push('โ
Notification system ready');
try {
const notificationCount = window.FreshRouteNotifications.count();
functionalityDetails.push(`โ
Active notifications: ${notificationCount}`);
functionalityScore += 25;
} catch (e) {
functionalityDetails.push('โ ๏ธ Notification count unavailable');
}
}
if (extensionData.elementsCount > 0) {
functionalityDetails.push(`โ
Extension elements found: ${extensionData.elementsCount}`);
}
statusDiv.innerHTML = `
โ
<strong>Extension functionality test: ${functionalityScore}% complete!</strong>
<br><small>Extension is ${extensionData.enabled ? 'enabled' : 'disabled'} and functioning properly</small>
<br><small style="margin-top: 5px; display: block;">${functionalityDetails.join('<br>')}</small>`;
statusDiv.className = 'extension-status success';
}, () => {
statusDiv.innerHTML = `
โ <strong>Extension functionality test failed</strong>
<br><small>No extension elements detected. Please check installation.</small>
<div style="margin-top: 15px;">
<button class="button secondary" onclick="GuideManager.show('troubleshooting')">๐ง Troubleshooting</button>
</div>`;
statusDiv.className = 'extension-status error';
});
},
getAllowedDomains: function(onSuccess, onError, timeout = 3000) {
// Try to get domains from extension global objects first
if (window.FRESHROUTE_ALLOWED_DOMAINS && Array.isArray(window.FRESHROUTE_ALLOWED_DOMAINS)) {
console.log('โ
Using allowed domains from global scope:', window.FRESHROUTE_ALLOWED_DOMAINS);
onSuccess(window.FRESHROUTE_ALLOWED_DOMAINS);
return;
}
// Try legacy global variable
if (window.ALLOWED_DOMAINS && Array.isArray(window.ALLOWED_DOMAINS)) {
console.log('โ
Using allowed domains from legacy global scope:', window.ALLOWED_DOMAINS);
onSuccess(window.ALLOWED_DOMAINS);
return;
}
// Try DomainValidator if available
if (window.DomainValidator && typeof window.DomainValidator.getAllowedDomains === 'function') {
try {
const domains = window.DomainValidator.getAllowedDomains();
console.log('โ
Using allowed domains from DomainValidator:', domains);
onSuccess(domains);
return;
} catch (error) {
console.warn('โ ๏ธ Error using DomainValidator:', error);
}
}
console.log('๐ Falling back to message communication for allowed domains');
// Fallback to message communication
this.communicateWithExtension('FRESHROUTE_GET_ALLOWED_DOMAINS', (data) => {
if (data.allowedDomains && Array.isArray(data.allowedDomains)) {
console.log('โ
Received allowed domains via message:', data.allowedDomains);
onSuccess(data.allowedDomains);
} else {
console.error('โ Invalid response from extension:', data);
onError();
}
}, onError, timeout);
},
communicateWithExtension: function(messageType, onSuccess, onError, timeout = 3000) {
let responseReceived = false;
const messageListener = (event) => {
if (event.data && event.data.type === `${messageType}_RESPONSE`) {
responseReceived = true;
onSuccess(event.data.data || {});
window.removeEventListener('message', messageListener);
}
};
window.addEventListener('message', messageListener);
window.postMessage({
type: messageType,
source: 'setup-page',
timestamp: Date.now()
}, '*');
setTimeout(() => {
if (!responseReceived) {
onError();
window.removeEventListener('message', messageListener);
}
}, timeout);
}
};
// Guide content management
const GuideManager = {
guides: {
'extension-controls': {
title: '๐๏ธ Extension Controls',
content: `
<p>The FreshRoute extension provides several ways to control API mocking:</p>
<h3>๐ Extension Icon</h3>
<p>Click the FreshRoute icon in your browser toolbar to:</p>
<ul>
<li><strong>Enable/Disable</strong> - Toggle API mocking on/off</li>
<li><strong>View Active Rules</strong> - See which rules are currently active</li>
<li><strong>Quick Actions</strong> - Access common features quickly</li>
</ul>
`
},
'usage-guide': {
title: '๐ FreshRoute Usage Guide',
content: `
<h3>๐ Quick Start</h3>
<ol>
<li><strong>Install Extension:</strong> Follow the installation steps above</li>
<li><strong>Create Mock Rules:</strong> Use the Dashboard to define API responses</li>
<li><strong>Browse Websites:</strong> FreshRoute automatically intercepts matching requests</li>
<li><strong>Monitor Activity:</strong> Check the extension popup for active rules</li>
</ol>
`
},
'server-commands': {
title: '๐ FreshRoute Server Commands',
content: `
<h3>๐ Starting & Stopping</h3>
<div class="code-block">
<span>freshroute start --silent</span>
<button class="copy-btn" onclick="Utils.copyText('cmdStart', this)">Copy</button>
</div>
<span id="cmdStart" style="display: none;">freshroute start --silent</span>
<p>Start server in background (recommended)</p>
`
},
'allowed-domains': {
title: '๐ Allowed Development Domains',
content: function() {
return this.generateAllowedDomainsContent();
},
generateAllowedDomainsContent: function() {
// Try to get domains from extension
return new Promise((resolve) => {
ExtensionManager.getAllowedDomains((domains) => {
const domainsList = domains.map(domain =>
`<span style="display: inline-block; background: #e3f2fd; color: #1976d2; padding: 4px 8px; margin: 2px; border-radius: 4px; font-family: monospace; font-size: 0.9em;">${domain}</span>`
).join('');
const content = `
<div style="background: #fff3e0; border-left: 4px solid #ff9800; padding: 15px; margin: 15px 0; border-radius: 4px;">
<h4 style="color: #e65100; margin-bottom: 10px;">๐ Domain Restrictions</h4>
<p>FreshRoute URL rewriting is restricted to development domains only to prevent accidental production redirects.</p>
</div>
<h3>โ
Currently Allowed Domains (${domains.length} total):</h3>
<div style="margin: 15px 0; line-height: 1.8;">
${domainsList}
</div>
<div style="background: #e8f5e8; padding: 15px; margin: 15px 0; border-radius: 8px;">
<h4 style="color: #2e7d32; margin-bottom: 10px;">๐ Domain Matching Rules:</h4>
<ul style="margin: 0; padding-left: 20px;">
<li><strong>Exact match:</strong> "localhost" only matches "localhost"</li>
<li><strong>Subdomain support:</strong> "example.com" allows "dev.example.com", "staging.example.com", etc.</li>
<li><strong>Protocol flexible:</strong> Both HTTP and HTTPS are supported</li>
</ul>
</div>
<div style="background: #f3e5f5; padding: 15px; margin: 15px 0; border-radius: 8px;">
<h4 style="color: #7b1fa2; margin-bottom: 10px;">โ๏ธ Adding New Domains:</h4>
<p>To add new development domains, contact the plugin development team to include your domains in the allowed list.</p>
<p><strong>Example:</strong></p>
<div style="background: #2d3748; color: #e2e8f0; padding: 10px; border-radius: 4px; font-family: monospace; margin: 10px 0;">
'dev.mycompany.com',<br>
'staging.mycompany.com',<br>
'test.mycompany.com'
</div>
</div>
`;
resolve(content);
}, () => {
// Fallback if extension not available
resolve(`
<div style="background: #ffebee; border-left: 4px solid #f44336; padding: 15px; margin: 15px 0; border-radius: 4px;">
<h4 style="color: #c62828; margin-bottom: 10px;">โ Extension Not Available</h4>
<p>Could not fetch allowed domains from the extension. Please ensure the FreshRoute extension is installed and enabled.</p>
</div>
<div style="margin: 15px 0;">
<button class="button secondary" onclick="ExtensionManager.checkStatus()">๐ Check Extension</button>
<button class="button secondary" onclick="GuideManager.show('allowed-domains')">๐ Retry</button>
</div>
`);
});
});
}
},
'troubleshooting': {
title: '๐ง Troubleshooting Guide',
content: `
<h3>โ Extension Not Working</h3>
<p><strong>Solutions:</strong></p>
<ul>
<li>Check if extension is enabled in <code>chrome://extensions/</code></li>
<li>Refresh the page after enabling extension</li>
<li>Check browser console for errors (F12)</li>
</ul>
`
}
},
show: function(guideKey) {
const guide = this.guides[guideKey];
if (guide) {
if (typeof guide.content === 'function') {
// Check if it's an async function (returns a Promise)
const result = guide.content();
if (result && typeof result.then === 'function') {
// Handle async content
this.showModal(`<h2>${guide.title}</h2><div style="padding: 20px; text-align: center;">๐ Loading content...</div><div style="margin-top: 20px;"><button class="button" onclick="GuideManager.closeModal()">Close</button></div>`);
result.then(content => {
this.showModal(`<h2>${guide.title}</h2>${content}<div style="margin-top: 20px;"><button class="button" onclick="GuideManager.closeModal()">Close</button></div>`);
}).catch(error => {
console.error('Error loading guide content:', error);
this.showModal(`<h2>${guide.title}</h2><div style="background: #ffebee; padding: 15px; border-radius: 4px;">โ Error loading content. Please try again.</div><div style="margin-top: 20px;"><button class="button" onclick="GuideManager.closeModal()">Close</button></div>`);
});
} else {
// Sync function
this.showModal(`<h2>${guide.title}</h2>${result}<div style="margin-top: 20px;"><button class="button" onclick="GuideManager.closeModal()">Close</button></div>`);
}
} else {
// Static content
this.showModal(`<h2>${guide.title}</h2>${guide.content}<div style="margin-top: 20px;"><button class="button" onclick="GuideManager.closeModal()">Close</button></div>`);
}
}
},
showModal: function(content) {
document.getElementById('modalContent').innerHTML = content;
document.getElementById('helpModal').style.display = 'block';
},
closeModal: function() {
document.getElementById('helpModal').style.display = 'none';
},
toggleSection: function(sectionId) {
const section = document.getElementById(sectionId);
if (section) {
section.style.display = section.style.display === 'none' ? 'block' : 'none';
}
},
startInstallationGuide: function() {
StepManager.highlightInstallation();
document.getElementById('installationInstructions').style.display = 'block';
this.toggleSection('chromeExtensionsInstructions');
this.toggleSection('manualSteps');
},
startAutoDetection: function() {
let attempts = 0;
const maxAttempts = 20;
const statusDiv = document.getElementById('extensionStatus') || document.getElementById('autoCheckStatus');
const checkInterval = setInterval(() => {
attempts++;
statusDiv.innerHTML = `๐ <strong>Waiting for extension installation... (${attempts}/${maxAttempts})</strong><br><small>Installing the extension? We'll detect it automatically!</small>`;
window.postMessage({
type: 'FRESHROUTE_STATUS_REQUEST',
source: 'setup-page',
timestamp: Date.now()
}, '*');
if (attempts >= maxAttempts) {
clearInterval(checkInterval);
statusDiv.innerHTML = `โฐ <strong>Still waiting for extension...</strong><br><small>Take your time! Click "Check Again" when you're done installing.</small>`;
}
}, 6000);
},
showNextSteps: function() {
this.showModal(`
<h2>๐ Awesome! Extension is Ready!</h2>
<p>Your FreshRoute extension is installed and working perfectly. Here's what you can do now:</p>
<div style="background: #e8f5e8; padding: 20px; border-radius: 8px; margin: 15px 0;">
<h3>๐ Quick Start Options:</h3>
<ol style="margin: 10px 0; padding-left: 20px; line-height: 1.8;">
<li><strong>URL Rewriting:</strong> Works immediately! No server needed.</li>
<li><strong>Basic Headers:</strong> Add 'x-' prefixed headers without server.</li>
<li><strong>API Mocking:</strong> Start server for full mock capabilities.</li>
</ol>
</div>
<div style="margin-top: 20px;">
<button class="button" onclick="GuideManager.closeModal()">๐ I'm Ready to Go!</button>
</div>
`);
}
};
// Step management
const StepManager = {
markInstallationComplete: function() {
const installStep = document.querySelector('.step');
if (installStep) {
installStep.style.opacity = '0.6';
installStep.style.background = '#f8f9fa';
const stepNumber = installStep.querySelector('.step-number');
if (stepNumber) {
stepNumber.innerHTML = 'โ';
stepNumber.style.background = '#28a745';
}
}
this.showUsageSteps();
},
highlightInstallation: function() {
const installStep = document.querySelector('.step');
if (installStep) {
installStep.style.opacity = '1';
installStep.style.background = 'white';
installStep.style.border = '2px solid #ff9800';
Utils.scrollToElement('.step');
}
this.hideUsageSteps();
},
hideUsageSteps: function() {
const steps = document.querySelectorAll('.step');
steps.forEach((step, index) => {
if (index > 0) {
step.style.opacity = '0.4';
step.style.background = '#f8f9fa';
}
});
},
showUsageSteps: function() {
const steps = document.querySelectorAll('.step');
steps.forEach((step, index) => {
if (index > 0) {
step.style.opacity = '1';
step.style.background = 'white';