@livedesk/client 0.1.10 → 0.1.12
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/bin/livedesk-client.js +105 -7
- package/package.json +1 -1
package/bin/livedesk-client.js
CHANGED
|
@@ -271,16 +271,86 @@ async function createSupabaseClient() {
|
|
|
271
271
|
|
|
272
272
|
function openBrowser(url) {
|
|
273
273
|
if (os.platform() === 'win32') {
|
|
274
|
-
spawn('
|
|
274
|
+
spawn('rundll32.exe', ['url.dll,FileProtocolHandler', url], { detached: true, stdio: 'ignore', windowsHide: true }).unref();
|
|
275
275
|
return;
|
|
276
276
|
}
|
|
277
277
|
const command = os.platform() === 'darwin' ? 'open' : 'xdg-open';
|
|
278
278
|
spawn(command, [url], { detached: true, stdio: 'ignore' }).unref();
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
+
function escapeHtml(value) {
|
|
282
|
+
return String(value ?? '')
|
|
283
|
+
.replaceAll('&', '&')
|
|
284
|
+
.replaceAll('<', '<')
|
|
285
|
+
.replaceAll('>', '>')
|
|
286
|
+
.replaceAll('"', '"')
|
|
287
|
+
.replaceAll("'", ''');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function renderOAuthCallbackPage({ title, message, tone = 'neutral' }) {
|
|
291
|
+
const accent = tone === 'error' ? '#991b1b' : tone === 'waiting' ? '#374151' : '#111827';
|
|
292
|
+
return `<!doctype html>
|
|
293
|
+
<html lang="en">
|
|
294
|
+
<head>
|
|
295
|
+
<meta charset="utf-8">
|
|
296
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
297
|
+
<title>${escapeHtml(title)}</title>
|
|
298
|
+
<style>
|
|
299
|
+
:root { color-scheme: light; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
|
300
|
+
* { box-sizing: border-box; }
|
|
301
|
+
html, body { width: 100%; min-height: 100%; margin: 0; }
|
|
302
|
+
body {
|
|
303
|
+
display: grid;
|
|
304
|
+
place-items: center;
|
|
305
|
+
padding: 24px;
|
|
306
|
+
background:
|
|
307
|
+
radial-gradient(circle at 50% 0%, rgba(148, 163, 184, 0.22), transparent 34%),
|
|
308
|
+
linear-gradient(180deg, #f8fafc, #eef2f7);
|
|
309
|
+
color: #111827;
|
|
310
|
+
}
|
|
311
|
+
main {
|
|
312
|
+
width: min(440px, 100%);
|
|
313
|
+
display: grid;
|
|
314
|
+
gap: 14px;
|
|
315
|
+
padding: 28px;
|
|
316
|
+
border: 1px solid rgba(148, 163, 184, 0.34);
|
|
317
|
+
border-radius: 12px;
|
|
318
|
+
background: rgba(255, 255, 255, 0.9);
|
|
319
|
+
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.14);
|
|
320
|
+
text-align: center;
|
|
321
|
+
}
|
|
322
|
+
.mark {
|
|
323
|
+
width: 42px;
|
|
324
|
+
height: 42px;
|
|
325
|
+
display: grid;
|
|
326
|
+
place-items: center;
|
|
327
|
+
justify-self: center;
|
|
328
|
+
border-radius: 10px;
|
|
329
|
+
background: ${accent};
|
|
330
|
+
color: #ffffff;
|
|
331
|
+
font-weight: 950;
|
|
332
|
+
}
|
|
333
|
+
h1 { margin: 0; font-size: 28px; line-height: 1.05; letter-spacing: 0; }
|
|
334
|
+
p { margin: 0; color: #64748b; font-size: 15px; line-height: 1.5; font-weight: 650; }
|
|
335
|
+
small { color: #94a3b8; font-size: 12px; font-weight: 750; }
|
|
336
|
+
</style>
|
|
337
|
+
</head>
|
|
338
|
+
<body>
|
|
339
|
+
<main>
|
|
340
|
+
<div class="mark">LD</div>
|
|
341
|
+
<h1>${escapeHtml(title)}</h1>
|
|
342
|
+
<p>${escapeHtml(message)}</p>
|
|
343
|
+
<small>LiveDesk Client</small>
|
|
344
|
+
</main>
|
|
345
|
+
</body>
|
|
346
|
+
</html>`;
|
|
347
|
+
}
|
|
348
|
+
|
|
281
349
|
async function startOAuthCallbackServer(options = {}) {
|
|
282
350
|
const host = String(process.env.LIVEDESK_CLIENT_AUTH_HOST || DEFAULT_AUTH_CALLBACK_HOST).trim() || DEFAULT_AUTH_CALLBACK_HOST;
|
|
283
351
|
const port = normalizePort(options.authPort) || DEFAULT_AUTH_CALLBACK_PORT;
|
|
352
|
+
let listeningPort = port;
|
|
353
|
+
let completed = false;
|
|
284
354
|
let settleCode;
|
|
285
355
|
let rejectCode;
|
|
286
356
|
const waitForCode = new Promise((resolve, reject) => {
|
|
@@ -288,23 +358,49 @@ async function startOAuthCallbackServer(options = {}) {
|
|
|
288
358
|
rejectCode = reject;
|
|
289
359
|
});
|
|
290
360
|
const server = createServer((req, res) => {
|
|
291
|
-
const requestUrl = new URL(req.url || '/', `http
|
|
361
|
+
const requestUrl = new URL(req.url || '/', `http://${host}:${listeningPort}`);
|
|
362
|
+
if (requestUrl.pathname === '/favicon.ico') {
|
|
363
|
+
res.writeHead(204);
|
|
364
|
+
res.end();
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (completed) {
|
|
368
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
369
|
+
res.end(renderOAuthCallbackPage({
|
|
370
|
+
title: 'LiveDesk sign-in complete',
|
|
371
|
+
message: 'You can close this tab and return to the terminal.'
|
|
372
|
+
}));
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
292
375
|
const code = requestUrl.searchParams.get('code');
|
|
293
376
|
const error = requestUrl.searchParams.get('error_description') || requestUrl.searchParams.get('error');
|
|
294
377
|
if (error) {
|
|
295
378
|
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
296
|
-
res.end(
|
|
379
|
+
res.end(renderOAuthCallbackPage({
|
|
380
|
+
title: 'LiveDesk sign-in failed',
|
|
381
|
+
message: error,
|
|
382
|
+
tone: 'error'
|
|
383
|
+
}));
|
|
384
|
+
completed = true;
|
|
297
385
|
rejectCode(new Error(error));
|
|
298
386
|
server.close();
|
|
299
387
|
return;
|
|
300
388
|
}
|
|
301
389
|
if (!code) {
|
|
302
|
-
res.writeHead(404, { 'Content-Type': 'text/
|
|
303
|
-
res.end(
|
|
390
|
+
res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
391
|
+
res.end(renderOAuthCallbackPage({
|
|
392
|
+
title: 'Waiting for LiveDesk sign-in',
|
|
393
|
+
message: 'This local callback is waiting for an auth code from Google.',
|
|
394
|
+
tone: 'waiting'
|
|
395
|
+
}));
|
|
304
396
|
return;
|
|
305
397
|
}
|
|
306
398
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
307
|
-
res.end(
|
|
399
|
+
res.end(renderOAuthCallbackPage({
|
|
400
|
+
title: 'LiveDesk sign-in complete',
|
|
401
|
+
message: 'You can close this tab and return to the terminal.'
|
|
402
|
+
}));
|
|
403
|
+
completed = true;
|
|
308
404
|
settleCode(code);
|
|
309
405
|
server.close();
|
|
310
406
|
});
|
|
@@ -313,6 +409,8 @@ async function startOAuthCallbackServer(options = {}) {
|
|
|
313
409
|
server.once('error', reject);
|
|
314
410
|
server.listen(port, host, resolve);
|
|
315
411
|
});
|
|
412
|
+
const address = server.address();
|
|
413
|
+
listeningPort = typeof address === 'object' && address ? address.port : port;
|
|
316
414
|
} catch (err) {
|
|
317
415
|
if (err?.code === 'EADDRINUSE') {
|
|
318
416
|
throw new Error(`LiveDesk Google sign-in callback port ${port} is already in use. Close the app using it or run with --auth-port <port> and add that callback URL in Supabase Auth redirect URLs.`);
|
|
@@ -321,7 +419,7 @@ async function startOAuthCallbackServer(options = {}) {
|
|
|
321
419
|
}
|
|
322
420
|
return {
|
|
323
421
|
get redirectTo() {
|
|
324
|
-
return `http://${host}:${
|
|
422
|
+
return `http://${host}:${listeningPort}/callback`;
|
|
325
423
|
},
|
|
326
424
|
waitForCode
|
|
327
425
|
};
|