pinokiod 3.230.0 → 3.232.0
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/kernel/bin/cuda.js +83 -76
- package/kernel/bin/setup.js +2 -0
- package/kernel/prototype.js +13 -0
- package/kernel/router/index.js +41 -0
- package/package.json +1 -1
- package/server/index.js +91 -29
- package/server/public/container-tab-link.js +115 -0
- package/server/public/style.css +4 -0
- package/server/public/tab-link-popover.css +118 -0
- package/server/public/tab-link-popover.js +1391 -0
- package/server/views/app.ejs +79 -1626
- package/server/views/container.ejs +3 -0
- package/server/views/index.ejs +6 -0
- package/server/views/net.ejs +449 -8
- package/server/views/network.ejs +5 -3
- package/server/views/partials/dynamic.ejs +1 -1
- package/server/views/partials/menu.ejs +1 -1
- package/server/views/partials/running.ejs +1 -1
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
<link href="/noty.css" rel="stylesheet"/>
|
|
13
13
|
<link href="/style.css" rel="stylesheet"/>
|
|
14
14
|
<link href="/urldropdown.css" rel="stylesheet" />
|
|
15
|
+
<link href="/tab-link-popover.css" rel="stylesheet" />
|
|
15
16
|
<% if (agent === "electron") { %>
|
|
16
17
|
<link href="/electron.css" rel="stylesheet"/>
|
|
17
18
|
<% } %>
|
|
@@ -316,6 +317,8 @@ iframe {
|
|
|
316
317
|
<script src="/opener.js"></script>
|
|
317
318
|
<script src="/nav.js"></script>
|
|
318
319
|
<script src="/report.js"></script>
|
|
320
|
+
<script src="/tab-link-popover.js"></script>
|
|
321
|
+
<script src="/container-tab-link.js"></script>
|
|
319
322
|
</head>
|
|
320
323
|
<body class='<%=theme%>' data-agent="<%=agent%>">
|
|
321
324
|
<!--
|
package/server/views/index.ejs
CHANGED
|
@@ -535,6 +535,9 @@ body.dark aside .current.selected {
|
|
|
535
535
|
<button class='btn browse' data-src="<%=item.review_url%>">
|
|
536
536
|
<i class="fa-regular fa-message"></i> Forum
|
|
537
537
|
</button>
|
|
538
|
+
<button class='btn browse' data-src="<%=item.view_url%>">
|
|
539
|
+
<i class="fa-regular fa-eye"></i> View
|
|
540
|
+
</button>
|
|
538
541
|
<button class='btn open-menu'>
|
|
539
542
|
<i class="fa-solid fa-bars"></i><span> Menu</span>
|
|
540
543
|
</button>
|
|
@@ -626,6 +629,9 @@ body.dark aside .current.selected {
|
|
|
626
629
|
<button class='btn browse' data-src="<%=item.review_url%>">
|
|
627
630
|
<i class="fa-regular fa-message"></i> Forum
|
|
628
631
|
</button>
|
|
632
|
+
<button class='btn browse' data-src="<%=item.view_url%>">
|
|
633
|
+
<i class="fa-regular fa-eye"></i> View
|
|
634
|
+
</button>
|
|
629
635
|
<button class='btn open-menu'>
|
|
630
636
|
<i class="fa-solid fa-bars"></i><span> Menu</span>
|
|
631
637
|
</button>
|
package/server/views/net.ejs
CHANGED
|
@@ -29,6 +29,11 @@
|
|
|
29
29
|
text-decoration: none;
|
|
30
30
|
color: black;
|
|
31
31
|
}
|
|
32
|
+
a.explain {
|
|
33
|
+
color: rgba(127, 91, 243, 0.9);
|
|
34
|
+
text-decoration: underline;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
}
|
|
32
37
|
.status {
|
|
33
38
|
padding: 10px;
|
|
34
39
|
margin: 10px;
|
|
@@ -157,6 +162,76 @@ body.dark .context-menu-wrapper {
|
|
|
157
162
|
text-align: left;
|
|
158
163
|
color: black;
|
|
159
164
|
}
|
|
165
|
+
.get-dns-btn {
|
|
166
|
+
margin-top: 10px;
|
|
167
|
+
display: inline-flex;
|
|
168
|
+
align-items: center;
|
|
169
|
+
gap: 6px;
|
|
170
|
+
font-size: 12px;
|
|
171
|
+
padding: 6px 10px;
|
|
172
|
+
justify-content: center;
|
|
173
|
+
max-width: 200px;
|
|
174
|
+
}
|
|
175
|
+
.dns-modal .header-description {
|
|
176
|
+
display: flex;
|
|
177
|
+
align-items: center;
|
|
178
|
+
gap: 10px;
|
|
179
|
+
margin-bottom: 16px;
|
|
180
|
+
}
|
|
181
|
+
.dns-modal .header-description .bubble {
|
|
182
|
+
background: rgba(0,0,0,0.05);
|
|
183
|
+
padding: 12px;
|
|
184
|
+
border-radius: 8px;
|
|
185
|
+
font-size: 13px;
|
|
186
|
+
line-height: 1.4;
|
|
187
|
+
}
|
|
188
|
+
body.dark .dns-modal .header-description .bubble {
|
|
189
|
+
background: rgba(255,255,255,0.08);
|
|
190
|
+
}
|
|
191
|
+
.dns-modal .address-section {
|
|
192
|
+
display: grid;
|
|
193
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
194
|
+
gap: 10px;
|
|
195
|
+
margin-bottom: 15px;
|
|
196
|
+
}
|
|
197
|
+
.dns-modal .address-section input {
|
|
198
|
+
padding: 10px;
|
|
199
|
+
border-radius: 6px;
|
|
200
|
+
border: 1px solid rgba(0,0,0,0.15);
|
|
201
|
+
font-size: 14px;
|
|
202
|
+
}
|
|
203
|
+
body.dark .dns-modal .address-section input {
|
|
204
|
+
background: rgba(255,255,255,0.05);
|
|
205
|
+
color: white;
|
|
206
|
+
border-color: rgba(255,255,255,0.18);
|
|
207
|
+
}
|
|
208
|
+
.dns-modal .address-result {
|
|
209
|
+
font-weight: bold;
|
|
210
|
+
display: flex;
|
|
211
|
+
align-items: center;
|
|
212
|
+
justify-content: center;
|
|
213
|
+
gap: 10px;
|
|
214
|
+
font-size: 16px;
|
|
215
|
+
padding: 30px 10px;
|
|
216
|
+
background: rgba(0,0,0,0.04);
|
|
217
|
+
border-radius: 8px;
|
|
218
|
+
}
|
|
219
|
+
body.dark .dns-modal .address-result {
|
|
220
|
+
background: rgba(255,255,255,0.08);
|
|
221
|
+
}
|
|
222
|
+
.dns-progress-modal {
|
|
223
|
+
margin-top: 15px;
|
|
224
|
+
}
|
|
225
|
+
.dns-modal-terminal-container {
|
|
226
|
+
width: 100%;
|
|
227
|
+
height: 280px;
|
|
228
|
+
background: #000;
|
|
229
|
+
border-radius: 8px;
|
|
230
|
+
overflow: hidden;
|
|
231
|
+
}
|
|
232
|
+
.dns-modal-terminal-container .xterm .xterm-viewport {
|
|
233
|
+
width: initial !important;
|
|
234
|
+
}
|
|
160
235
|
.blank {
|
|
161
236
|
width: 50px;
|
|
162
237
|
height: 50px;
|
|
@@ -566,7 +641,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
566
641
|
</h1>
|
|
567
642
|
</header>
|
|
568
643
|
<main>
|
|
569
|
-
<div id='terminal' class='hidden'></div>
|
|
570
644
|
<div class='container'>
|
|
571
645
|
<form class='search'>
|
|
572
646
|
<!--
|
|
@@ -592,7 +666,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
592
666
|
<h2><i class="fa-solid fa-wifi"></i> Local network</h2><div>accessible from any machine on the local network</div>
|
|
593
667
|
</div>
|
|
594
668
|
<div class='section'>
|
|
595
|
-
<h2><i class="fa-solid fa-podcast"></i> Peer</h2><div>accessible from any pinokio peer on the local network <a class='explain' data-type='peer'>
|
|
669
|
+
<h2><i class="fa-solid fa-podcast"></i> Peer</h2><div>accessible from any pinokio peer on the local network <a class='explain' data-type='peer'>What is a "Peer"?</a></div>
|
|
596
670
|
</div>
|
|
597
671
|
</div>
|
|
598
672
|
</div>
|
|
@@ -616,10 +690,36 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
616
690
|
<div class='title'><i class="fa-solid fa-circle"></i><span><%=item.title || item.name%></span></div>
|
|
617
691
|
<div class='grid-3'>
|
|
618
692
|
<div class='section'>
|
|
693
|
+
<%
|
|
694
|
+
let default_dns_name = ''
|
|
695
|
+
if (item.internal_router && item.internal_router.length > 0) {
|
|
696
|
+
const firstDomain = item.internal_router[0]
|
|
697
|
+
if (typeof firstDomain === 'string') {
|
|
698
|
+
if (firstDomain.endsWith('.localhost')) {
|
|
699
|
+
default_dns_name = firstDomain.replace(/\.localhost$/, '')
|
|
700
|
+
} else {
|
|
701
|
+
default_dns_name = firstDomain
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (!default_dns_name) {
|
|
706
|
+
default_dns_name = (item.port ? item.port.toString() : (item.name || '')).toString()
|
|
707
|
+
}
|
|
708
|
+
default_dns_name = default_dns_name.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-+|-+$/g, '')
|
|
709
|
+
if (!default_dns_name && item.port) {
|
|
710
|
+
default_dns_name = item.port.toString()
|
|
711
|
+
}
|
|
712
|
+
%>
|
|
619
713
|
<% item.internal_router.forEach((domain) => { %>
|
|
620
714
|
<a class='net' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
|
|
621
715
|
<% }) %>
|
|
622
716
|
<a class='net' target="_blank" href="http://localhost:<%=item.port%>">http://localhost:<%=item.port%></a>
|
|
717
|
+
<% if (allow_dns_creation && item.port) { %>
|
|
718
|
+
<button type='button' class='btn get-dns-btn' data-port="<%=item.port%>" data-default-dns="<%=default_dns_name%>" data-process-title="<%=item.title || item.name%>">
|
|
719
|
+
<i class="fa-solid fa-globe"></i>
|
|
720
|
+
<span>Get .localhost domain</span>
|
|
721
|
+
</button>
|
|
722
|
+
<% } %>
|
|
623
723
|
</div>
|
|
624
724
|
<div class='section'>
|
|
625
725
|
<% if (item.external_ip) { %>
|
|
@@ -799,6 +899,19 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
799
899
|
<% } %>
|
|
800
900
|
</div>
|
|
801
901
|
</div>
|
|
902
|
+
<template id='dns-modal-template'>
|
|
903
|
+
<div class='dns-modal'>
|
|
904
|
+
<div class='address-section'>
|
|
905
|
+
<input type='text' placeholder='domain name' id='dns-modal-name'>
|
|
906
|
+
<input type='number' placeholder='port number' id='dns-modal-port' min='1'>
|
|
907
|
+
</div>
|
|
908
|
+
<div class='address-result' id='dns-modal-result'>
|
|
909
|
+
<div>https://[NAME].localhost</div>
|
|
910
|
+
<i class='fa-solid fa-arrow-right'></i>
|
|
911
|
+
<div>http://localhost:[PORT]</div>
|
|
912
|
+
</div>
|
|
913
|
+
</div>
|
|
914
|
+
</template>
|
|
802
915
|
<aside>
|
|
803
916
|
<div class='btn-tab'>
|
|
804
917
|
<button type='button' class='btn' id='create-launcher-button'><i class="fa-solid fa-plus"></i><div class='caption'>Create</div></button>
|
|
@@ -836,6 +949,324 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
836
949
|
</aside>
|
|
837
950
|
</main>
|
|
838
951
|
<script>
|
|
952
|
+
const dnsCwd = "<%- typeof cwd !== 'undefined' ? JSON.stringify(cwd).slice(1, -1) : '' %>"
|
|
953
|
+
const dnsModalTemplate = document.querySelector('#dns-modal-template')
|
|
954
|
+
const peerName = "<%= peer && peer.name ? peer.name : '' %>".trim().toLowerCase()
|
|
955
|
+
const allowDnsCreation = <%= allow_dns_creation ? 'true' : 'false' %>
|
|
956
|
+
let dnsModalActive = false
|
|
957
|
+
let dnsTerminalInstance = null
|
|
958
|
+
let dnsSocketInstance = null
|
|
959
|
+
let dnsResizeObserver = null
|
|
960
|
+
|
|
961
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
962
|
+
|
|
963
|
+
const normalizeHost = (value) => {
|
|
964
|
+
if (!value) return null
|
|
965
|
+
let str = String(value).trim().toLowerCase()
|
|
966
|
+
if (!str) return null
|
|
967
|
+
str = str.replace(/^https?:\/\//, '')
|
|
968
|
+
const host = str.split('/')[0]
|
|
969
|
+
return host || null
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
const waitForDnsPropagation = async (dnsName, { timeout = 20000, interval = 1000 } = {}) => {
|
|
973
|
+
const base = (dnsName || '').trim().toLowerCase()
|
|
974
|
+
if (!base) return false
|
|
975
|
+
const targets = new Set()
|
|
976
|
+
const pushTarget = (val) => {
|
|
977
|
+
const host = normalizeHost(val)
|
|
978
|
+
if (host) targets.add(host)
|
|
979
|
+
}
|
|
980
|
+
pushTarget(`${base}.localhost`)
|
|
981
|
+
if (peerName) {
|
|
982
|
+
pushTarget(`${base}.${peerName}.localhost`)
|
|
983
|
+
}
|
|
984
|
+
const deadline = Date.now() + timeout
|
|
985
|
+
while (Date.now() < deadline) {
|
|
986
|
+
try {
|
|
987
|
+
const info = await fetch('/pinokio/peer', { headers: { 'cache-control': 'no-cache' } }).then((res) => {
|
|
988
|
+
if (!res.ok) throw new Error('peer info unavailable')
|
|
989
|
+
return res.json()
|
|
990
|
+
})
|
|
991
|
+
const advertisedHosts = new Set()
|
|
992
|
+
const addHost = (value) => {
|
|
993
|
+
const host = normalizeHost(value)
|
|
994
|
+
if (host) advertisedHosts.add(host)
|
|
995
|
+
}
|
|
996
|
+
if (info && Array.isArray(info.router_info)) {
|
|
997
|
+
info.router_info.forEach((proc) => {
|
|
998
|
+
(proc.internal_router || []).forEach(addHost)
|
|
999
|
+
(proc.external_router || []).forEach(addHost)
|
|
1000
|
+
})
|
|
1001
|
+
}
|
|
1002
|
+
if (info && info.router) {
|
|
1003
|
+
Object.values(info.router).forEach((list) => {
|
|
1004
|
+
if (Array.isArray(list)) {
|
|
1005
|
+
list.forEach(addHost)
|
|
1006
|
+
}
|
|
1007
|
+
})
|
|
1008
|
+
}
|
|
1009
|
+
const found = Array.from(targets).some((host) => advertisedHosts.has(host))
|
|
1010
|
+
if (found) {
|
|
1011
|
+
return true
|
|
1012
|
+
}
|
|
1013
|
+
} catch (err) {
|
|
1014
|
+
console.warn('router poll failed', err)
|
|
1015
|
+
}
|
|
1016
|
+
await delay(interval)
|
|
1017
|
+
}
|
|
1018
|
+
return false
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
const renderDnsPreview = (popup, name, port) => {
|
|
1022
|
+
const preview = popup.querySelector('#dns-modal-result')
|
|
1023
|
+
if (!preview) return
|
|
1024
|
+
const safeName = (name && name.length > 0) ? name : '[NAME]'
|
|
1025
|
+
const safePort = (port && port.length > 0) ? port : '[PORT]'
|
|
1026
|
+
preview.innerHTML = ''
|
|
1027
|
+
const left = document.createElement('div')
|
|
1028
|
+
left.textContent = `https://${safeName}.localhost`
|
|
1029
|
+
const icon = document.createElement('i')
|
|
1030
|
+
icon.className = 'fa-solid fa-arrow-right'
|
|
1031
|
+
const right = document.createElement('div')
|
|
1032
|
+
right.textContent = `http://localhost:${safePort}`
|
|
1033
|
+
preview.append(left, icon, right)
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
const promptForDnsMapping = async ({ port, name, title }) => {
|
|
1037
|
+
if (!dnsModalTemplate) {
|
|
1038
|
+
alert('DNS modal unavailable')
|
|
1039
|
+
return null
|
|
1040
|
+
}
|
|
1041
|
+
const html = dnsModalTemplate.innerHTML
|
|
1042
|
+
const result = await Swal.fire({
|
|
1043
|
+
title: 'Get .localhost domain',
|
|
1044
|
+
html,
|
|
1045
|
+
confirmButtonText: 'Create domain',
|
|
1046
|
+
// showCancelButton: true,
|
|
1047
|
+
// focusConfirm: false,
|
|
1048
|
+
// allowOutsideClick: false,
|
|
1049
|
+
customClass: {
|
|
1050
|
+
popup: 'dns-modal-popup'
|
|
1051
|
+
},
|
|
1052
|
+
didOpen: () => {
|
|
1053
|
+
dnsModalActive = true
|
|
1054
|
+
const popup = Swal.getPopup()
|
|
1055
|
+
const nameInput = popup.querySelector('#dns-modal-name')
|
|
1056
|
+
const portInput = popup.querySelector('#dns-modal-port')
|
|
1057
|
+
if (portInput) {
|
|
1058
|
+
portInput.value = port || ''
|
|
1059
|
+
}
|
|
1060
|
+
const updatePreview = () => {
|
|
1061
|
+
renderDnsPreview(popup, nameInput ? nameInput.value.trim() : '', portInput ? portInput.value.trim() : '')
|
|
1062
|
+
}
|
|
1063
|
+
nameInput && nameInput.addEventListener('input', updatePreview)
|
|
1064
|
+
portInput && portInput.addEventListener('input', updatePreview)
|
|
1065
|
+
updatePreview()
|
|
1066
|
+
if (nameInput) {
|
|
1067
|
+
setTimeout(() => nameInput.focus(), 50)
|
|
1068
|
+
}
|
|
1069
|
+
},
|
|
1070
|
+
preConfirm: () => {
|
|
1071
|
+
const popup = Swal.getPopup()
|
|
1072
|
+
const nameInput = popup.querySelector('#dns-modal-name')
|
|
1073
|
+
const portInput = popup.querySelector('#dns-modal-port')
|
|
1074
|
+
const dnsName = nameInput ? nameInput.value.trim() : ''
|
|
1075
|
+
const dnsPort = portInput ? portInput.value.trim() : ''
|
|
1076
|
+
if (!dnsName) {
|
|
1077
|
+
Swal.showValidationMessage('Enter a domain name')
|
|
1078
|
+
return false
|
|
1079
|
+
}
|
|
1080
|
+
if (!dnsPort) {
|
|
1081
|
+
Swal.showValidationMessage('Enter a port number')
|
|
1082
|
+
return false
|
|
1083
|
+
}
|
|
1084
|
+
if (!/^\d+$/.test(dnsPort)) {
|
|
1085
|
+
Swal.showValidationMessage('Port must be numeric')
|
|
1086
|
+
return false
|
|
1087
|
+
}
|
|
1088
|
+
return { dnsName, dnsPort }
|
|
1089
|
+
}
|
|
1090
|
+
})
|
|
1091
|
+
if (!result.isConfirmed) {
|
|
1092
|
+
return null
|
|
1093
|
+
}
|
|
1094
|
+
return result.value
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
const createDnsTerminal = async (container) => {
|
|
1098
|
+
if (!container) {
|
|
1099
|
+
throw new Error('Terminal container missing')
|
|
1100
|
+
}
|
|
1101
|
+
if (typeof Terminal === 'undefined') {
|
|
1102
|
+
throw new Error('Terminal unavailable')
|
|
1103
|
+
}
|
|
1104
|
+
let config = {
|
|
1105
|
+
scrollback: 9999999,
|
|
1106
|
+
fontSize: 12,
|
|
1107
|
+
theme: document.body.classList.contains('dark') ? xtermTheme.FrontEndDelight : xtermTheme.Tomorrow,
|
|
1108
|
+
}
|
|
1109
|
+
try {
|
|
1110
|
+
const res = await fetch('/xterm_config').then((r) => r.json())
|
|
1111
|
+
if (res && res.config) {
|
|
1112
|
+
config = res.config
|
|
1113
|
+
}
|
|
1114
|
+
} catch (err) {
|
|
1115
|
+
console.warn('Failed to load xterm config', err)
|
|
1116
|
+
}
|
|
1117
|
+
const term = new Terminal(config)
|
|
1118
|
+
dnsTerminalInstance = term
|
|
1119
|
+
const fitAddon = new FitAddon.FitAddon()
|
|
1120
|
+
term.loadAddon(fitAddon)
|
|
1121
|
+
if (typeof WebLinksAddon !== 'undefined' && WebLinksAddon.WebLinksAddon) {
|
|
1122
|
+
term.loadAddon(new WebLinksAddon.WebLinksAddon())
|
|
1123
|
+
}
|
|
1124
|
+
container.innerHTML = ''
|
|
1125
|
+
term.open(container)
|
|
1126
|
+
fitAddon.fit()
|
|
1127
|
+
if (window.PinokioTouch && typeof window.PinokioTouch.bindTerminalFocus === 'function') {
|
|
1128
|
+
window.PinokioTouch.bindTerminalFocus(term, container)
|
|
1129
|
+
}
|
|
1130
|
+
const observer = new ResizeObserver(() => {
|
|
1131
|
+
try {
|
|
1132
|
+
fitAddon.fit()
|
|
1133
|
+
} catch (_) {}
|
|
1134
|
+
})
|
|
1135
|
+
observer.observe(container)
|
|
1136
|
+
return { term, observer }
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
const showDnsError = (message) => {
|
|
1140
|
+
Swal.close()
|
|
1141
|
+
Swal.fire('Domain error', message, 'error')
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
const startDnsCreationSocket = (term, dnsName, dnsPort) => {
|
|
1145
|
+
if (dnsSocketInstance && typeof dnsSocketInstance.close === 'function') {
|
|
1146
|
+
try { dnsSocketInstance.close() } catch (_) {}
|
|
1147
|
+
}
|
|
1148
|
+
const socket = new Socket()
|
|
1149
|
+
dnsSocketInstance = socket
|
|
1150
|
+
const write = (text) => {
|
|
1151
|
+
if (text && text !== '\u0007') {
|
|
1152
|
+
term.write(text)
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
const config = {
|
|
1156
|
+
name: dnsName,
|
|
1157
|
+
startType: 'new',
|
|
1158
|
+
projectType: 'dns',
|
|
1159
|
+
dnsPort,
|
|
1160
|
+
}
|
|
1161
|
+
socket.run({
|
|
1162
|
+
id: 'kernel.proto.create',
|
|
1163
|
+
method: 'kernel.proto.create',
|
|
1164
|
+
cwd: dnsCwd,
|
|
1165
|
+
client: {
|
|
1166
|
+
cols: term.cols,
|
|
1167
|
+
rows: term.rows,
|
|
1168
|
+
},
|
|
1169
|
+
params: config,
|
|
1170
|
+
}, (packet) => {
|
|
1171
|
+
if (packet.type === 'stream') {
|
|
1172
|
+
if (packet.data && packet.data.raw) {
|
|
1173
|
+
write(packet.data.raw)
|
|
1174
|
+
} else if (packet.data && packet.data.json) {
|
|
1175
|
+
write(JSON.stringify(packet.data.json).replace(/\n/g, '\r\n'))
|
|
1176
|
+
write('\r\n')
|
|
1177
|
+
} else if (packet.data && packet.data.json2) {
|
|
1178
|
+
write(JSON.stringify(packet.data.json2, null, 2).replace(/\n/g, '\r\n'))
|
|
1179
|
+
write('\r\n')
|
|
1180
|
+
}
|
|
1181
|
+
} else if (packet.type === 'result') {
|
|
1182
|
+
if (packet.data && packet.data.error) {
|
|
1183
|
+
write(`\r\n${packet.data.error}\r\n`)
|
|
1184
|
+
showDnsError(packet.data.error)
|
|
1185
|
+
} else {
|
|
1186
|
+
write('\r\nDomain created. Waiting for router to update...\r\n')
|
|
1187
|
+
const hostname = `${dnsName}.localhost`
|
|
1188
|
+
waitForDnsPropagation(dnsName)
|
|
1189
|
+
.then((ready) => {
|
|
1190
|
+
if (ready) {
|
|
1191
|
+
write(`Router detected ${hostname}. Reloading...\r\n`)
|
|
1192
|
+
} else {
|
|
1193
|
+
write('Router update took longer than expected. Reloading anyway...\r\n')
|
|
1194
|
+
}
|
|
1195
|
+
})
|
|
1196
|
+
.catch((err) => {
|
|
1197
|
+
console.warn('dns propagation wait failed', err)
|
|
1198
|
+
write('Unable to confirm router update. Reloading...\r\n')
|
|
1199
|
+
})
|
|
1200
|
+
.finally(() => {
|
|
1201
|
+
setTimeout(() => {
|
|
1202
|
+
window.location.reload()
|
|
1203
|
+
}, 500)
|
|
1204
|
+
})
|
|
1205
|
+
}
|
|
1206
|
+
} else if (packet.type === 'error') {
|
|
1207
|
+
const message = (packet.data && (packet.data.error || packet.data.message)) || 'Unknown error'
|
|
1208
|
+
write(`\r\n${message}\r\n`)
|
|
1209
|
+
showDnsError(message)
|
|
1210
|
+
}
|
|
1211
|
+
}).then(() => {
|
|
1212
|
+
dnsSocketInstance = null
|
|
1213
|
+
}).catch((err) => {
|
|
1214
|
+
console.error('DNS creation failed', err)
|
|
1215
|
+
showDnsError(err.message || 'Unable to create domain')
|
|
1216
|
+
})
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
const runDnsCreation = ({ dnsName, dnsPort }) => {
|
|
1220
|
+
Swal.fire({
|
|
1221
|
+
title: 'Creating domain',
|
|
1222
|
+
html: `<div class='dns-progress-modal'><div class='dns-modal-terminal-container'></div></div>`,
|
|
1223
|
+
showConfirmButton: false,
|
|
1224
|
+
allowOutsideClick: false,
|
|
1225
|
+
allowEscapeKey: false,
|
|
1226
|
+
didOpen: async () => {
|
|
1227
|
+
try {
|
|
1228
|
+
const popup = Swal.getPopup()
|
|
1229
|
+
const container = popup.querySelector('.dns-modal-terminal-container')
|
|
1230
|
+
const { term, observer } = await createDnsTerminal(container)
|
|
1231
|
+
dnsResizeObserver = observer
|
|
1232
|
+
startDnsCreationSocket(term, dnsName, dnsPort)
|
|
1233
|
+
} catch (err) {
|
|
1234
|
+
console.error('Failed to initialize DNS terminal', err)
|
|
1235
|
+
showDnsError(err.message || 'Unable to start terminal')
|
|
1236
|
+
}
|
|
1237
|
+
},
|
|
1238
|
+
willClose: () => {
|
|
1239
|
+
if (dnsSocketInstance && typeof dnsSocketInstance.close === 'function') {
|
|
1240
|
+
try { dnsSocketInstance.close() } catch (_) {}
|
|
1241
|
+
dnsSocketInstance = null
|
|
1242
|
+
}
|
|
1243
|
+
if (dnsResizeObserver) {
|
|
1244
|
+
try { dnsResizeObserver.disconnect() } catch (_) {}
|
|
1245
|
+
dnsResizeObserver = null
|
|
1246
|
+
}
|
|
1247
|
+
dnsTerminalInstance = null
|
|
1248
|
+
dnsModalActive = false
|
|
1249
|
+
}
|
|
1250
|
+
})
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
document.addEventListener('click', async (event) => {
|
|
1254
|
+
if (!allowDnsCreation) return
|
|
1255
|
+
const trigger = event.target.closest('.get-dns-btn')
|
|
1256
|
+
if (!trigger) {
|
|
1257
|
+
return
|
|
1258
|
+
}
|
|
1259
|
+
event.preventDefault()
|
|
1260
|
+
const port = trigger.getAttribute('data-port') || ''
|
|
1261
|
+
const defaultName = trigger.getAttribute('data-default-dns') || ''
|
|
1262
|
+
const processTitle = trigger.getAttribute('data-process-title') || ''
|
|
1263
|
+
const response = await promptForDnsMapping({ port, name: defaultName, title: processTitle })
|
|
1264
|
+
if (!response) {
|
|
1265
|
+
return
|
|
1266
|
+
}
|
|
1267
|
+
runDnsCreation(response)
|
|
1268
|
+
})
|
|
1269
|
+
|
|
839
1270
|
let list = []
|
|
840
1271
|
document.querySelectorAll(".line.index").forEach((el, index) => {
|
|
841
1272
|
list.push({
|
|
@@ -1219,14 +1650,24 @@ document.addEventListener("click", async (e) => {
|
|
|
1219
1650
|
|
|
1220
1651
|
|
|
1221
1652
|
|
|
1222
|
-
if (document.querySelector(".container")) {
|
|
1223
|
-
document.querySelector(".container").addEventListener("click", async (e) => {
|
|
1224
|
-
e.stopPropagation()
|
|
1225
|
-
})
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1653
|
+
// if (document.querySelector(".container")) {
|
|
1654
|
+
// document.querySelector(".container").addEventListener("click", async (e) => {
|
|
1655
|
+
// e.stopPropagation()
|
|
1656
|
+
// })
|
|
1657
|
+
// }
|
|
1228
1658
|
|
|
1229
1659
|
})
|
|
1660
|
+
let interval = setInterval(() => {
|
|
1661
|
+
if (dnsModalActive) return
|
|
1662
|
+
fetch("/net/<%=selected_name%>/diff").then((res) => {
|
|
1663
|
+
return res.json()
|
|
1664
|
+
}).then((res) => {
|
|
1665
|
+
console.log(res)
|
|
1666
|
+
if (res.diff) {
|
|
1667
|
+
location.href = location.href
|
|
1668
|
+
}
|
|
1669
|
+
})
|
|
1670
|
+
}, 2000)
|
|
1230
1671
|
</script>
|
|
1231
1672
|
</body>
|
|
1232
1673
|
</html>
|
package/server/views/network.ejs
CHANGED
|
@@ -1197,11 +1197,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
1197
1197
|
</aside>
|
|
1198
1198
|
</main>
|
|
1199
1199
|
<script>
|
|
1200
|
+
let interval
|
|
1200
1201
|
document.querySelector("#reset-label").addEventListener("click", async (e) => {
|
|
1201
1202
|
e.preventDefault()
|
|
1202
1203
|
e.stopPropagation()
|
|
1203
1204
|
let ok = confirm("Are you sure you want to reset the network config? (The peer router will be updated to the latest version; 2. A new HTTPS certificate will be generated)")
|
|
1204
1205
|
if (ok) {
|
|
1206
|
+
clearInterval(interval)
|
|
1205
1207
|
let r = await fetch("/network/reset", {
|
|
1206
1208
|
method: "post",
|
|
1207
1209
|
headers: {
|
|
@@ -1366,6 +1368,7 @@ document.querySelector("main").addEventListener("click", async (e) => {
|
|
|
1366
1368
|
}
|
|
1367
1369
|
*/
|
|
1368
1370
|
console.log(body)
|
|
1371
|
+
clearInterval(interval)
|
|
1369
1372
|
let r = await fetch("/network", {
|
|
1370
1373
|
method: "post",
|
|
1371
1374
|
headers: { "Content-Type": "application/json" },
|
|
@@ -1547,8 +1550,8 @@ setInterval(() => {
|
|
|
1547
1550
|
})
|
|
1548
1551
|
}, 2000)
|
|
1549
1552
|
<% } %>
|
|
1550
|
-
<% if (peer_active) { %>
|
|
1551
|
-
setInterval(() => {
|
|
1553
|
+
<% if (peer_active && processes.length === 0) { %>
|
|
1554
|
+
interval = setInterval(() => {
|
|
1552
1555
|
fetch("/peer_check").then((res) => {
|
|
1553
1556
|
return res.json()
|
|
1554
1557
|
}).then((res) => {
|
|
@@ -1558,7 +1561,6 @@ setInterval(() => {
|
|
|
1558
1561
|
})
|
|
1559
1562
|
}, 2000)
|
|
1560
1563
|
<% } %>
|
|
1561
|
-
|
|
1562
1564
|
</script>
|
|
1563
1565
|
</body>
|
|
1564
1566
|
</html>
|