+32
packages/extension/src/lib/api-client.ts
+32
packages/extension/src/lib/api-client.ts
···
67
67
export function getExtensionVersion(): string {
68
68
return chrome.runtime.getManifest().version;
69
69
}
70
+
71
+
/**
72
+
* Check if ATlast server is running
73
+
* Returns true if server is reachable, false otherwise
74
+
*/
75
+
export async function checkServerHealth(): Promise<boolean> {
76
+
try {
77
+
// Try to fetch the root URL with a short timeout
78
+
const controller = new AbortController();
79
+
const timeoutId = setTimeout(() => controller.abort(), 3000);
80
+
81
+
const response = await fetch(ATLAST_API_URL, {
82
+
method: 'HEAD',
83
+
signal: controller.signal
84
+
});
85
+
86
+
clearTimeout(timeoutId);
87
+
88
+
// Any response (even 404) means server is running
89
+
return true;
90
+
} catch (error) {
91
+
console.error('[API Client] Server health check failed:', error);
92
+
return false;
93
+
}
94
+
}
95
+
96
+
/**
97
+
* Get the API URL (for display purposes)
98
+
*/
99
+
export function getApiUrl(): string {
100
+
return ATLAST_API_URL;
101
+
}
+16
packages/extension/src/popup/popup.css
+16
packages/extension/src/popup/popup.css
···
4
4
box-sizing: border-box;
5
5
}
6
6
7
+
code {
8
+
background: rgba(0, 0, 0, 0.1);
9
+
padding: 4px 8px;
10
+
border-radius: 4px;
11
+
font-family: 'Courier New', monospace;
12
+
font-size: 11px;
13
+
display: inline-block;
14
+
margin: 8px 0;
15
+
}
16
+
17
+
@media (prefers-color-scheme: dark) {
18
+
code {
19
+
background: rgba(255, 255, 255, 0.1);
20
+
}
21
+
}
22
+
7
23
body {
8
24
width: 350px;
9
25
min-height: 400px;
+12
packages/extension/src/popup/popup.html
+12
packages/extension/src/popup/popup.html
···
64
64
<p id="error-message" class="error-message"></p>
65
65
<button id="btn-retry" class="btn-secondary">Try Again</button>
66
66
</div>
67
+
68
+
<!-- Server offline state -->
69
+
<div id="state-offline" class="state hidden">
70
+
<div class="icon">🔌</div>
71
+
<p class="message">ATlast server not running</p>
72
+
<p class="error-message">
73
+
Start the dev server:<br>
74
+
<code>npx netlify-cli dev --filter @atlast/web</code>
75
+
</p>
76
+
<p class="hint" id="server-url"></p>
77
+
<button id="btn-check-server" class="btn-primary">Check Again</button>
78
+
</div>
67
79
</main>
68
80
69
81
<footer>
+52
-2
packages/extension/src/popup/popup.ts
+52
-2
packages/extension/src/popup/popup.ts
···
14
14
scraping: document.getElementById('state-scraping')!,
15
15
complete: document.getElementById('state-complete')!,
16
16
uploading: document.getElementById('state-uploading')!,
17
-
error: document.getElementById('state-error')!
17
+
error: document.getElementById('state-error')!,
18
+
offline: document.getElementById('state-offline')!
18
19
};
19
20
20
21
const elements = {
···
23
24
finalCount: document.getElementById('final-count')!,
24
25
statusMessage: document.getElementById('status-message')!,
25
26
errorMessage: document.getElementById('error-message')!,
27
+
serverUrl: document.getElementById('server-url')!,
26
28
progressFill: document.getElementById('progress-fill')! as HTMLElement,
27
29
btnStart: document.getElementById('btn-start')! as HTMLButtonElement,
28
30
btnUpload: document.getElementById('btn-upload')! as HTMLButtonElement,
29
-
btnRetry: document.getElementById('btn-retry')! as HTMLButtonElement
31
+
btnRetry: document.getElementById('btn-retry')! as HTMLButtonElement,
32
+
btnCheckServer: document.getElementById('btn-check-server')! as HTMLButtonElement
30
33
};
31
34
32
35
/**
···
193
196
}
194
197
195
198
/**
199
+
* Check server health and show offline state if needed
200
+
*/
201
+
async function checkServer(): Promise<boolean> {
202
+
console.log('[Popup] 🏥 Checking server health...');
203
+
204
+
// Import health check function
205
+
const { checkServerHealth, getApiUrl } = await import('../lib/api-client.js');
206
+
207
+
const isOnline = await checkServerHealth();
208
+
209
+
if (!isOnline) {
210
+
console.log('[Popup] ❌ Server is offline');
211
+
showState('offline');
212
+
elements.serverUrl.textContent = `Trying to reach: ${getApiUrl()}`;
213
+
return false;
214
+
}
215
+
216
+
console.log('[Popup] ✅ Server is online');
217
+
return true;
218
+
}
219
+
220
+
/**
196
221
* Initialize popup
197
222
*/
198
223
async function init(): Promise<void> {
199
224
console.log('[Popup] 🚀 Initializing popup...');
225
+
226
+
// Check server health first (only in dev mode)
227
+
const { getApiUrl } = await import('../lib/api-client.js');
228
+
const isDev = getApiUrl().includes('127.0.0.1') || getApiUrl().includes('localhost');
229
+
230
+
if (isDev) {
231
+
const serverOnline = await checkServer();
232
+
if (!serverOnline) {
233
+
// Set up retry button
234
+
elements.btnCheckServer.addEventListener('click', async () => {
235
+
elements.btnCheckServer.disabled = true;
236
+
elements.btnCheckServer.textContent = 'Checking...';
237
+
238
+
const online = await checkServer();
239
+
if (online) {
240
+
// Server is back online, re-initialize
241
+
init();
242
+
} else {
243
+
elements.btnCheckServer.disabled = false;
244
+
elements.btnCheckServer.textContent = 'Check Again';
245
+
}
246
+
});
247
+
return;
248
+
}
249
+
}
200
250
201
251
// Get current state
202
252
console.log('[Popup] 📡 Requesting state from background...');