pinokiod 7.1.60 → 7.1.61
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/package.json +1 -1
- package/server/index.js +5 -1
- package/server/views/connect/index.ejs +99 -47
- package/server/views/setup.ejs +104 -29
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -15492,6 +15492,7 @@ class Server {
|
|
|
15492
15492
|
if (this.onrestart) {
|
|
15493
15493
|
console.log("onrestart exists")
|
|
15494
15494
|
this.onrestart()
|
|
15495
|
+
res.json({ success: true })
|
|
15495
15496
|
} else {
|
|
15496
15497
|
await this.start({ debug: this.debug, browser: this.browser })
|
|
15497
15498
|
res.json({ success: true })
|
|
@@ -15499,7 +15500,10 @@ class Server {
|
|
|
15499
15500
|
}))
|
|
15500
15501
|
this.app.post("/restart", ex(async (req, res) => {
|
|
15501
15502
|
console.log("post /restart")
|
|
15502
|
-
this.start({ debug: this.debug, browser: this.browser })
|
|
15503
|
+
this.start({ debug: this.debug, browser: this.browser }).catch((error) => {
|
|
15504
|
+
console.error("[Pinokiod] restart failed", error && error.stack ? error.stack : error)
|
|
15505
|
+
})
|
|
15506
|
+
res.json({ success: true })
|
|
15503
15507
|
}))
|
|
15504
15508
|
this.app.post("/network", ex(async (req, res) => {
|
|
15505
15509
|
if (this.kernel.homedir) {
|
|
@@ -44,9 +44,16 @@
|
|
|
44
44
|
color: var(--connect-text);
|
|
45
45
|
margin: 0;
|
|
46
46
|
min-height: 100vh;
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-direction: column;
|
|
47
49
|
color-scheme: light;
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
body[data-page="connect"][data-agent="electron"] {
|
|
53
|
+
padding-top: 26px;
|
|
54
|
+
box-sizing: border-box;
|
|
55
|
+
}
|
|
56
|
+
|
|
50
57
|
@media (prefers-color-scheme: dark) {
|
|
51
58
|
body[data-page="connect"] {
|
|
52
59
|
--connect-body-bg: #010617;
|
|
@@ -101,6 +108,12 @@
|
|
|
101
108
|
color-scheme: dark;
|
|
102
109
|
}
|
|
103
110
|
|
|
111
|
+
body[data-page="connect"] main.connect-page-shell {
|
|
112
|
+
flex: 1 1 auto;
|
|
113
|
+
padding: 24px 20px 48px;
|
|
114
|
+
box-sizing: border-box;
|
|
115
|
+
}
|
|
116
|
+
|
|
104
117
|
body[data-page="connect"] .container {
|
|
105
118
|
background: var(--connect-card-bg);
|
|
106
119
|
max-width: 600px;
|
|
@@ -261,7 +274,7 @@
|
|
|
261
274
|
body[data-page="connect"] header.head {
|
|
262
275
|
max-width: 600px;
|
|
263
276
|
margin: 0 auto;
|
|
264
|
-
padding: 50px;
|
|
277
|
+
padding: 16px 50px 36px;
|
|
265
278
|
text-align: center;
|
|
266
279
|
}
|
|
267
280
|
|
|
@@ -297,47 +310,23 @@
|
|
|
297
310
|
<script src="/nav.js"></script>
|
|
298
311
|
</head>
|
|
299
312
|
<body class='<%=theme%>' data-agent="<%=agent%>" data-page="connect">
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
<
|
|
304
|
-
<div><i class="fa-solid fa-expand"></i></div>
|
|
305
|
-
</button>
|
|
306
|
-
<button class="btn2" id="back" data-tippy-content="back"><div><i class="fa-solid fa-chevron-left"></i></div></button>
|
|
307
|
-
<button class="btn2" id="forward" data-tippy-content="forward"><div><i class="fa-solid fa-chevron-right"></i></div></button>
|
|
308
|
-
<button class="btn2" id="refresh-page" data-tippy-content="refresh"><div><i class="fa-solid fa-rotate-right"></i></div></button>
|
|
309
|
-
<button class="btn2" id="screenshot" data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
|
|
310
|
-
<button class="btn2" id="inspector" data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
|
|
311
|
-
<div class="flexible"></div>
|
|
312
|
-
<a class="btn2" href="/columns" data-tippy-content="split into 2 columns">
|
|
313
|
-
<div><i class="fa-solid fa-table-columns"></i></div>
|
|
314
|
-
</a>
|
|
315
|
-
<a class="btn2" href="/rows" data-tippy-content="split into 2 rows">
|
|
316
|
-
<div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
|
|
317
|
-
</a>
|
|
318
|
-
<button class="btn2" id="new-window" data-tippy-content="open a new window" title="open a new window" data-agent="web">
|
|
319
|
-
<div><i class="fa-solid fa-plus"></i></div>
|
|
320
|
-
</button>
|
|
321
|
-
<button class="btn2 hidden" id="close-window" data-tippy-content="close this section">
|
|
322
|
-
<div><i class="fa-solid fa-xmark"></i></div>
|
|
323
|
-
</button>
|
|
324
|
-
</h1>
|
|
325
|
-
</header>
|
|
326
|
-
<header class='head'>
|
|
327
|
-
<div class='logos'>
|
|
328
|
-
<img class='logo' src="/pinokio-black.png"/>
|
|
329
|
-
<div>+</div>
|
|
330
|
-
<% if (name === "huggingface") { %>
|
|
331
|
-
<div class='logo'>🤗</div>
|
|
332
|
-
<% } else if (name === "x") { %>
|
|
333
|
-
<i class="logo fa-brands fa-square-x-twitter"></i>
|
|
334
|
-
<% } else if (name === "pinokio") { %>
|
|
313
|
+
<%- include('../partials/app_navheader', { agent }) %>
|
|
314
|
+
<main class="connect-page-shell">
|
|
315
|
+
<header class='head'>
|
|
316
|
+
<div class='logos'>
|
|
335
317
|
<img class='logo' src="/pinokio-black.png"/>
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
318
|
+
<div>+</div>
|
|
319
|
+
<% if (name === "huggingface") { %>
|
|
320
|
+
<div class='logo'>🤗</div>
|
|
321
|
+
<% } else if (name === "x") { %>
|
|
322
|
+
<i class="logo fa-brands fa-square-x-twitter"></i>
|
|
323
|
+
<% } else if (name === "pinokio") { %>
|
|
324
|
+
<img class='logo' src="/pinokio-black.png"/>
|
|
325
|
+
<% } %>
|
|
326
|
+
</div>
|
|
327
|
+
<h1><%=name%> Connect</h1>
|
|
328
|
+
</header>
|
|
329
|
+
<div class="container">
|
|
341
330
|
<div id="status" class="status hidden">
|
|
342
331
|
</div>
|
|
343
332
|
|
|
@@ -359,7 +348,8 @@
|
|
|
359
348
|
</div>
|
|
360
349
|
</div>
|
|
361
350
|
|
|
362
|
-
|
|
351
|
+
</div>
|
|
352
|
+
</main>
|
|
363
353
|
|
|
364
354
|
<div id="connect-loader" class="loader-overlay hidden">
|
|
365
355
|
<div class="loader-spinner"></div>
|
|
@@ -369,6 +359,7 @@
|
|
|
369
359
|
|
|
370
360
|
<script>
|
|
371
361
|
const CONNECT_NAME = "<%=name%>"
|
|
362
|
+
const CONNECT_AGENT = document.body.getAttribute('data-agent') || 'web'
|
|
372
363
|
const isSecureConnectContext = <%= protocol === "https" ? 'true' : 'false' %>
|
|
373
364
|
const statusElement = document.getElementById('status')
|
|
374
365
|
const loaderElement = document.getElementById('connect-loader')
|
|
@@ -565,6 +556,10 @@
|
|
|
565
556
|
const PROFILE_URL = "<%=config.PROFILE_URL%>"
|
|
566
557
|
const SCOPE = "<%=config.SCOPE%>"
|
|
567
558
|
const name = "<%=name%>"
|
|
559
|
+
const SECURE_CONNECT_POLL_INTERVAL = 2000
|
|
560
|
+
const SECURE_CONNECT_TIMEOUT = 120000
|
|
561
|
+
let secureConnectPollTimer = null
|
|
562
|
+
let secureConnectTimeoutHandle = null
|
|
568
563
|
|
|
569
564
|
function generateRandomString(length) {
|
|
570
565
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
@@ -599,8 +594,6 @@
|
|
|
599
594
|
// OAuth functions
|
|
600
595
|
async function login() {
|
|
601
596
|
try {
|
|
602
|
-
showLoader('Connecting...');
|
|
603
|
-
setStatus(`Redirecting to ${name}...`, 'warning');
|
|
604
597
|
// Clear existing data
|
|
605
598
|
localStorage.removeItem('oauth_state');
|
|
606
599
|
localStorage.removeItem('code_verifier');
|
|
@@ -626,8 +619,22 @@
|
|
|
626
619
|
});
|
|
627
620
|
|
|
628
621
|
const authUrl = OAUTH_URL + '?' + params.toString();
|
|
629
|
-
|
|
630
|
-
|
|
622
|
+
|
|
623
|
+
if (CONNECT_AGENT === 'electron') {
|
|
624
|
+
startSecureConnectPolling();
|
|
625
|
+
setStatus(`Continue the ${name} login in your browser.`, 'warning');
|
|
626
|
+
try {
|
|
627
|
+
window.open(authUrl, '_blank', 'browser');
|
|
628
|
+
} catch (openError) {
|
|
629
|
+
hideLoader();
|
|
630
|
+
setStatus(`Could not open the browser. Please open ${authUrl} manually.`, 'error');
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
showLoader('Connecting...');
|
|
637
|
+
setStatus(`Redirecting to ${name}...`, 'warning');
|
|
631
638
|
window.location.href = authUrl;
|
|
632
639
|
|
|
633
640
|
} catch (error) {
|
|
@@ -637,6 +644,48 @@
|
|
|
637
644
|
}
|
|
638
645
|
}
|
|
639
646
|
|
|
647
|
+
function stopSecureConnectPolling(message = '', type = 'warning') {
|
|
648
|
+
if (secureConnectPollTimer) {
|
|
649
|
+
clearInterval(secureConnectPollTimer);
|
|
650
|
+
secureConnectPollTimer = null;
|
|
651
|
+
}
|
|
652
|
+
if (secureConnectTimeoutHandle) {
|
|
653
|
+
clearTimeout(secureConnectTimeoutHandle);
|
|
654
|
+
secureConnectTimeoutHandle = null;
|
|
655
|
+
}
|
|
656
|
+
if (message) {
|
|
657
|
+
hideLoader();
|
|
658
|
+
setStatus(message, type);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
async function pollSecureConnectStatus() {
|
|
663
|
+
const token = await ensureValidToken();
|
|
664
|
+
if (!token) {
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
stopSecureConnectPolling();
|
|
668
|
+
localStorage.removeItem('oauth_state');
|
|
669
|
+
localStorage.removeItem('code_verifier');
|
|
670
|
+
await fetchUserInfo(token);
|
|
671
|
+
setStatus('Connected.', 'success');
|
|
672
|
+
return true;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function startSecureConnectPolling() {
|
|
676
|
+
stopSecureConnectPolling();
|
|
677
|
+
showLoader('Waiting for authentication in the browser...', {
|
|
678
|
+
cancellable: true,
|
|
679
|
+
onCancel: () => stopSecureConnectPolling('Connection cancelled.', 'warning')
|
|
680
|
+
});
|
|
681
|
+
setStatus(`Waiting for ${name} authentication to finish...`, 'warning');
|
|
682
|
+
pollSecureConnectStatus();
|
|
683
|
+
secureConnectPollTimer = setInterval(pollSecureConnectStatus, SECURE_CONNECT_POLL_INTERVAL);
|
|
684
|
+
secureConnectTimeoutHandle = setTimeout(() => {
|
|
685
|
+
stopSecureConnectPolling('Timed out waiting for the browser login to finish. Please try again.', 'error');
|
|
686
|
+
}, SECURE_CONNECT_TIMEOUT);
|
|
687
|
+
}
|
|
688
|
+
|
|
640
689
|
async function handleOAuthCallback(code) {
|
|
641
690
|
showLoader('Finishing connection...');
|
|
642
691
|
try {
|
|
@@ -670,6 +719,7 @@
|
|
|
670
719
|
console.log('Token response:', response)
|
|
671
720
|
// Store tokens
|
|
672
721
|
if (response.access_token) {
|
|
722
|
+
stopSecureConnectPolling();
|
|
673
723
|
// Cleanup
|
|
674
724
|
localStorage.removeItem('oauth_state');
|
|
675
725
|
localStorage.removeItem('code_verifier');
|
|
@@ -721,11 +771,13 @@
|
|
|
721
771
|
await handleOAuthCallback(code);
|
|
722
772
|
return;
|
|
723
773
|
}
|
|
724
|
-
|
|
774
|
+
|
|
725
775
|
// Check existing session with automatic refresh
|
|
726
776
|
const token = await ensureValidToken();
|
|
727
777
|
if (token) {
|
|
728
778
|
await fetchUserInfo(token);
|
|
779
|
+
} else if (CONNECT_AGENT === 'electron' && localStorage.getItem('oauth_state')) {
|
|
780
|
+
startSecureConnectPolling();
|
|
729
781
|
} else {
|
|
730
782
|
setStatus('Not authenticated', 'warning');
|
|
731
783
|
}
|
package/server/views/setup.ejs
CHANGED
|
@@ -240,38 +240,113 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
240
240
|
location.href = location.href
|
|
241
241
|
<% } %>
|
|
242
242
|
<% if (!requirements_pending && !install_required && wait === "caddy") { %>
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
const waitRoot = document.querySelector(".requirements .content")
|
|
244
|
+
const waitLabel = document.querySelector(".requirements .content .loading span")
|
|
245
|
+
const WAIT_TIMEOUT_MS = 120000
|
|
246
|
+
const setWaitLabel = (message) => {
|
|
247
|
+
if (waitLabel) {
|
|
248
|
+
waitLabel.textContent = message
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const showWaitError = (message) => {
|
|
252
|
+
if (waitRoot) {
|
|
253
|
+
waitRoot.innerHTML = `
|
|
254
|
+
<div class='loading'>
|
|
255
|
+
<i class="fa-solid fa-circle-exclamation"></i>
|
|
256
|
+
<span>${message}</span>
|
|
257
|
+
</div>
|
|
258
|
+
<div class='item'>
|
|
259
|
+
<a class='btn' href='${location.href}'>Retry</a>
|
|
260
|
+
</div>
|
|
261
|
+
`
|
|
262
|
+
} else {
|
|
263
|
+
alert(message)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
245
266
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
267
|
+
try {
|
|
268
|
+
setWaitLabel("Enabling secure localhost access...")
|
|
269
|
+
const networkResponse = await fetch("/network", {
|
|
270
|
+
method: "post",
|
|
271
|
+
headers: { "Content-Type": "application/json" },
|
|
272
|
+
body: JSON.stringify({
|
|
273
|
+
PINOKIO_NETWORK_ACTIVE: "1",
|
|
274
|
+
PINOKIO_HTTPS_ACTIVE: "1",
|
|
275
|
+
})
|
|
276
|
+
}).then((res) => {
|
|
277
|
+
return res.json()
|
|
252
278
|
})
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
await new Promise((resolve, reject) => {
|
|
262
|
-
let interval = setInterval(() => {
|
|
263
|
-
fetch("/check_router_up").then((res) => {
|
|
264
|
-
return res.json()
|
|
265
|
-
}).then((res) => {
|
|
266
|
-
console.log(res)
|
|
267
|
-
if (res.success) {
|
|
268
|
-
clearInterval(interval)
|
|
269
|
-
resolve()
|
|
270
|
-
}
|
|
279
|
+
if (networkResponse && networkResponse.error) {
|
|
280
|
+
throw new Error(networkResponse.error)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
setWaitLabel("Restarting Pinokio...")
|
|
284
|
+
try {
|
|
285
|
+
await fetch("/restart", {
|
|
286
|
+
method: "post"
|
|
271
287
|
})
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
|
|
288
|
+
} catch (error) {
|
|
289
|
+
console.warn("restart request ended early", error)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const startedAt = Date.now()
|
|
293
|
+
let lastKnownError = null
|
|
294
|
+
let lastKnownPhase = null
|
|
295
|
+
|
|
296
|
+
await new Promise((resolve, reject) => {
|
|
297
|
+
const interval = setInterval(async () => {
|
|
298
|
+
try {
|
|
299
|
+
const [routerStatus, startupStatus] = await Promise.all([
|
|
300
|
+
fetch("/check_router_up").then((res) => res.json()),
|
|
301
|
+
fetch("/pinokio/startup_status").then((res) => res.json()).catch(() => null)
|
|
302
|
+
])
|
|
303
|
+
|
|
304
|
+
if (routerStatus && routerStatus.success) {
|
|
305
|
+
clearInterval(interval)
|
|
306
|
+
resolve()
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (routerStatus && routerStatus.error) {
|
|
311
|
+
lastKnownError = routerStatus.error
|
|
312
|
+
}
|
|
313
|
+
if (startupStatus && startupStatus.error) {
|
|
314
|
+
lastKnownError = startupStatus.error
|
|
315
|
+
}
|
|
316
|
+
if (startupStatus && startupStatus.phase) {
|
|
317
|
+
lastKnownPhase = startupStatus.phase
|
|
318
|
+
const phaseMessage = startupStatus.phase === "ready"
|
|
319
|
+
? "Secure router is almost ready..."
|
|
320
|
+
: `Restarting Pinokio... (${startupStatus.phase.replace(/_/g, " ")})`
|
|
321
|
+
setWaitLabel(phaseMessage)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (startupStatus && startupStatus.phase === "error") {
|
|
325
|
+
clearInterval(interval)
|
|
326
|
+
reject(new Error(lastKnownError || "Pinokio failed to restart cleanly"))
|
|
327
|
+
return
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if ((Date.now() - startedAt) >= WAIT_TIMEOUT_MS) {
|
|
331
|
+
clearInterval(interval)
|
|
332
|
+
const detail = lastKnownError || lastKnownPhase || "secure localhost router did not come up"
|
|
333
|
+
reject(new Error(`Timed out waiting for restart: ${detail}`))
|
|
334
|
+
}
|
|
335
|
+
} catch (error) {
|
|
336
|
+
lastKnownError = error && error.message ? error.message : String(error)
|
|
337
|
+
if ((Date.now() - startedAt) >= WAIT_TIMEOUT_MS) {
|
|
338
|
+
clearInterval(interval)
|
|
339
|
+
reject(new Error(`Timed out waiting for restart: ${lastKnownError}`))
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}, 1000)
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
location.href = "<%=current%>"
|
|
346
|
+
} catch (error) {
|
|
347
|
+
console.error("secure router setup failed", error)
|
|
348
|
+
showWaitError(error && error.message ? error.message : "Failed to restart Pinokio")
|
|
349
|
+
}
|
|
275
350
|
<% } %>
|
|
276
351
|
if (document.querySelector("#del-bin")) {
|
|
277
352
|
document.querySelector("#del-bin").addEventListener("click", async (e) => {
|