pinokiod 3.16.4 → 3.17.1

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.
@@ -957,7 +957,6 @@ class Bin {
957
957
  let dependencies
958
958
  if (r.name === "conda") {
959
959
  dependencies = config.bin.conda_requirements
960
- console.log("check dependencies", dependencies)
961
960
  requirements[i].dependencies = dependencies
962
961
  }
963
962
  let installed = await this.check_installed(r, dependencies)
@@ -980,8 +979,6 @@ class Bin {
980
979
  this.install_required = install_required
981
980
  this.requirements_pending = requirements_pending
982
981
 
983
- console.log("check_bin finished 2")
984
-
985
982
  requirements = requirements.filter((r) => {
986
983
  return r.relevant
987
984
  })
package/kernel/bin/py.js CHANGED
@@ -34,7 +34,6 @@ class Py {
34
34
  break;
35
35
  }
36
36
  }
37
- console.log({ exists, exists2, exists3, exists4 })
38
37
  return exists && exists2 && exists3 && exists4
39
38
  }
40
39
  async uninstall(req, ondata) {
package/kernel/peer.js CHANGED
@@ -107,6 +107,7 @@ class PeerDiscovery {
107
107
  })
108
108
  return res.data
109
109
  } catch (e) {
110
+ console.log("_refresh error", { host , e })
110
111
  return null
111
112
  }
112
113
  }
package/kernel/procs.js CHANGED
@@ -31,6 +31,11 @@ class Procs {
31
31
  // this.cache[localAddress] = false
32
32
  // return false;
33
33
  //}
34
+
35
+ // ignore caddy
36
+ if (parseInt(port) === 2019) {
37
+ return false
38
+ }
34
39
  if (this.cache.hasOwnProperty("" + port)) {
35
40
  return this.cache["" + port]
36
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "3.16.4",
3
+ "version": "3.17.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -3416,7 +3416,6 @@ class Server {
3416
3416
  bin: this.kernel.bin.preset("network"),
3417
3417
  })
3418
3418
 
3419
- console.log({ requirements_pending, install_required })
3420
3419
  if (!requirements_pending && install_required) {
3421
3420
  console.log("redirect to /setup/network")
3422
3421
  res.redirect("/setup/network?callback=/network")
Binary file
Binary file
Binary file
@@ -267,6 +267,20 @@ footer .toggle-expand {
267
267
  footer pre {
268
268
  background: none !important;
269
269
  }
270
+ .swal2-popup.full-popup img {
271
+ width: 100%;
272
+ }
273
+ .swal2-popup.full-popup {
274
+ max-width: 800px;
275
+ width: 100%;
276
+ }
277
+ .full-modal {
278
+ font-size: 14px;
279
+ text-align: left;
280
+ }
281
+ .swal2-title.full-title {
282
+ text-align: left;
283
+ }
270
284
  .swal2-popup.min-popup a {
271
285
  outline: none;
272
286
  }
@@ -27,6 +27,14 @@
27
27
  text-decoration: none;
28
28
  color: black;
29
29
  }
30
+ ol {
31
+ padding-inline-start: 15px;
32
+ }
33
+ a.explain {
34
+ color: royalblue;
35
+ text-decoration: underline;
36
+ cursor: pointer;
37
+ }
30
38
  .status {
31
39
  padding: 10px;
32
40
  margin: 10px;
@@ -245,7 +253,7 @@ td:first-child, th:first-child {
245
253
  vertical-align: top;
246
254
  border-bottom: 1px solid rgba(0,0,0,0.05);
247
255
  text-align: left;
248
- padding: 20px 0;
256
+ padding: 20px 5px;
249
257
  font-weight: normal;
250
258
  font-size: 12px;
251
259
  margin-bottom: 10px;
@@ -256,7 +264,7 @@ td:first-child, th:first-child {
256
264
  .container-row td {
257
265
  vertical-align: top;
258
266
  font-size: 14px;
259
- padding: 5px 2px;
267
+ padding: 5px;
260
268
  word-wrap: break-word;
261
269
  }
262
270
 
@@ -379,6 +387,9 @@ input:checked + .slider:before {
379
387
  flex-grow: 1;
380
388
  padding: 10px;
381
389
  }
390
+ table h3 {
391
+ margin: 0;
392
+ }
382
393
 
383
394
 
384
395
  @media only screen and (max-width: 480px) {
@@ -423,7 +434,11 @@ input:checked + .slider:before {
423
434
  <div class='config'>
424
435
  <div class='config-header'>
425
436
  <div class='header-label'><i class='fa-solid fa-wifi'></i> Network</div>
426
- <div class='desc'>Instant HTTPS for every app on your local network.</div>
437
+ <div class='desc'>Instant HTTPS and Peer network for every app on your local network.</div>
438
+ <ol>
439
+ <li><strong>Instant HTTPS</strong> <a class='explain' data-type='https'>Learn more</a></li>
440
+ <li><strong>Local Peer-to-Peer Network</strong> <a class='explain' data-type='peer'>Learn more</a></li>
441
+ </ol>
427
442
  </div>
428
443
  <div class='config-body'>
429
444
  <div class='config-row'>
@@ -465,121 +480,73 @@ input:checked + .slider:before {
465
480
  </div>
466
481
  <% } else { %>
467
482
  <div class='header-label-sub'>
468
- <h1><i class="fa-solid fa-wifi"></i> <%=name%></h1>
483
+ <h1><i class="fa-solid fa-wifi"></i> <%=name%> (peer)</h1>
469
484
  <div class='s'><i class="fa-brands fa-<%=brands[platform]%>"></i> <%=platform%> <%=host%></div>
470
485
  </div>
471
486
  <% } %>
472
487
  <table>
473
- <tr>
474
- <th><h2>Server</h2><div>Running apps</div></th>
475
- <th><h2>Shared</h2><div>Use on any machine on the network</div></th>
476
- <th><h2>Local</h2><div>Use on <%=current_host%></div></th>
477
- </tr>
478
- <% processes.forEach((item) => { %>
479
- <tr>
480
- <td class='title'><%=item.name%></td>
481
- <td>
482
- <% item.external_router.forEach((domain) => { %>
483
- <div>
484
- <a class='ln' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
485
- </div>
486
- <% }) %>
487
- <% if (item.external_ip) { %>
488
- <div>
489
- <a class='ln' target="_blank" href="http://<%=item.external_ip%>">http://<%=item.external_ip%></a>
490
- </div>
491
- <% } %>
492
- </td>
493
- <td>
494
- <% item.internal_router.forEach((domain) => { %>
488
+ <% if (current_host === host) { %>
489
+ <tr>
490
+ <th><h2>App</h2><div>All servers on this machine</div></th>
491
+ <th><h2>Local</h2><div>endpoints accessible from this machine</div></th>
492
+ <th><h2>Network</h2><div>endpoints accessible from any machine on the local network</div></th>
493
+ <th><h2>Peer</h2><div>endpoints accessible from any pinokio peer on the local network <a class='explain' data-type='peer'>How to start a peer</a></div></th>
494
+ </tr>
495
+ <% processes.forEach((item) => { %>
496
+ <tr>
497
+ <td class='title'><%=item.name%></td>
498
+ <td>
499
+ <% item.internal_router.forEach((domain) => { %>
500
+ <div>
501
+ <a class='ln' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
502
+ </div>
503
+ <% }) %>
495
504
  <div>
496
- <a class='ln' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
505
+ <a class='ln' target="_blank" href="http://localhost:<%=item.port%>">http://localhost:<%=item.port%></a>
497
506
  </div>
498
- <% }) %>
499
- <div>
500
- <a class='ln' target="_blank" href="http://localhost:<%=item.port%>">http://localhost:<%=item.port%></a>
501
- </div>
502
- </td>
507
+ </td>
508
+ <td>
509
+ <% if (item.external_ip) { %>
510
+ <div>
511
+ <a class='ln' target="_blank" href="http://<%=item.external_ip%>">http://<%=item.external_ip%></a>
512
+ </div>
513
+ <% } %>
514
+ </td>
515
+ <td>
516
+ <% item.external_router.forEach((domain) => { %>
517
+ <div>
518
+ <a class='ln' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
519
+ </div>
520
+ <% }) %>
521
+ </td>
522
+ </tr>
523
+ <% }) %>
524
+ <% } else { %>
525
+ <tr>
526
+ <th><h2>App</h2><div>All servers on this machine</div></th>
527
+ <th><h2>Endpoints</h2><div>accessible through peer network</div></th>
503
528
  </tr>
504
- <% }) %>
529
+ <% processes.forEach((item) => { %>
530
+ <tr>
531
+ <td class='title'><%=item.name%></td>
532
+ <td>
533
+ <% if (item.external_ip) { %>
534
+ <div>
535
+ <a class='ln' target="_blank" href="http://<%=item.external_ip%>">http://<%=item.external_ip%></a>
536
+ </div>
537
+ <% } %>
538
+ <% item.external_router.forEach((domain) => { %>
539
+ <div>
540
+ <a class='ln' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
541
+ </div>
542
+ <% }) %>
543
+ </td>
544
+ </tr>
545
+ <% }) %>
546
+ <% } %>
505
547
  </table>
506
548
  </div>
507
549
  <% }) %>
508
- <div class='container-row hidden'>
509
- <div class='header-label-sub'><i class="fa-solid fa-microchip"></i> System Programs</div>
510
- <div class='line align-top'>
511
- <h3>
512
- <img class='icon' src="<%=icon%>">
513
- <div class='col'>
514
- <div class='title'>Pinokio</div>
515
- <% if (proxy) { %>
516
- <div class='description'><a href="<%=proxy.proxy%>" target="_blank"><%=proxy.proxy%></a> → <a href="<%=localhost%>" target="_blank"><%=localhost%></a></div>
517
- <a class='wifi-qr badge' data-qr="<%=qr%>" data-url="<%=proxy.proxy%>"><i class="fa-solid fa-qrcode"></i> Scan QR Code</a>
518
- <% } else { %>
519
- <div class='description'>Share Pinokio <a href="<%=localhost%>" target="_blank"><%=localhost%></a> with other devices on the same network</div>
520
- <% } %>
521
- <div class='btn-container'>
522
- <% if (proxy) { %>
523
- <div id='wifi-stop' data-proxy="<%=proxy.proxy%>" class='btn'><div><i class="fa-solid fa-stop"></i> Stop</div></div>
524
- <% } else { %>
525
- <div id='wifi' class='btn'><div><i class="fa-solid fa-play"></i> Start</div></div>
526
- <div id='wifi-starting' class='btn disabled hidden'><div><i class="fa-solid fa-circle-notch fa-spin"></i> Starting</div></div>
527
- <% } %>
528
- </div>
529
- </div>
530
- </h3>
531
- </div>
532
- <% items.forEach((item) => { %>
533
- <div class='line align-top'>
534
- <h3>
535
- <img class='icon' src="<%=item.icon%>">
536
- <div class='col'>
537
- <div class='title'><%=item.name%></div>
538
- <% if (item.proxy) { %>
539
- <div class='description'><a href="<%=item.proxy%>" target="_blank"><%=item.proxy%></a> → <a href="<%=item.target%>" target="_blank"><%=item.target%></a></div>
540
- <a class='wifi-qr badge' data-qr="<%=item.qr%>" data-url="<%=item.proxy%>"><i class="fa-solid fa-qrcode"></i> Scan QR Code</a>
541
- <% } else { %>
542
- <div class='description'>Share <%=item.target%> with other devices on the same network</div>
543
- <% } %>
544
- <div class='btn-container'>
545
- <% if (item.running) { %>
546
- <div data-port="<%=item.port%>" data-name="<%=item.name%>" data-target="<%=item.target%>" class='stop btn'><i class="fa-solid fa-stop"></i> Stop</div>
547
- <% } else { %>
548
- <div data-port="<%=item.port%>" data-name="<%=item.name%>" data-target="<%=item.target%>" class='start btn'><i class="fa-solid fa-play"></i> Start at port <%=item.port%></div>
549
- <% } %>
550
- </div>
551
- </div>
552
- </h3>
553
- </div>
554
- <% }) %>
555
- </div>
556
- <!--
557
- <div class='container-row'>
558
- <div class='header-label-sub'><i class="fa-solid fa-dice-d6"></i> Pinokio</div>
559
- <% apps.forEach((item) => { %>
560
- <a class='line align-top' href="<%=item.link%>">
561
- <h3>
562
- <% if (item.icon) { %>
563
- <img class='icon' src="<%=item.icon%>">
564
- <% } else { %>
565
- <% if (theme === 'dark') { %>
566
- <img class='icon' src="/pinokio-white.png">
567
- <% } else { %>
568
- <img class='icon' src="/pinokio-black.png">
569
- <% } %>
570
- <% } %>
571
- <div class='col'>
572
- <div class='title'><%=item.name%></div>
573
- <div class='description'><%=item.description%></div>
574
- </div>
575
- <div class='spinner'>
576
- <i class="fa-solid fa-chevron-right"></i>
577
- </div>
578
- </h3>
579
- </a>
580
- <% }) %>
581
- </div>
582
- -->
583
550
  </div>
584
551
  <% } %>
585
552
  </main>
@@ -663,6 +630,59 @@ document.querySelector("#active").addEventListener("change", (e)=> {
663
630
  document.querySelector("main").addEventListener("click", async (e) => {
664
631
  let target
665
632
 
633
+ if (e.target.classList.contains("explain")) {
634
+ target = e.target
635
+ } else {
636
+ target = e.target.closest(".explain")
637
+ }
638
+ if (target) {
639
+ e.preventDefault()
640
+ e.stopPropagation()
641
+ let type = target.getAttribute("data-type")
642
+ if (type === "https") {
643
+ await Swal.fire({
644
+ title: "Instant HTTPS",
645
+ customClass: {
646
+ popup: "full-popup",
647
+ title: "full-title"
648
+ },
649
+ showConfirmButton: false,
650
+ html: `<div class='full-modal'>
651
+ <ol>
652
+ <li><strong>Instant:</strong> Pinokio automatically turns ALL your local applications into HTTPS.</li>
653
+ <li><strong>System-wide:</strong> Instant HTTPS automagically works for ALL your local apps and APIs system-wide (not just those installed on pinokio)</li>
654
+ <li><strong>Not just for Pinokio:</strong> This means, even the apis and apps that have nothing to do with Pinokio (such as Ollama, LMStudio, or really anything) become HTTPS-ified.</li>
655
+ <li><strong>Whole New Application Architecture:</strong> Exposing localhost apps over HTTPS unlocks powerful new possibilities. It enables a completely new application architecture where public websites can securely leverage your local APIs as backend services.</li>
656
+ </ol>
657
+ <img src="/https.png"/>
658
+ <br>
659
+ </div>`
660
+ })
661
+ } else if (type === "peer") {
662
+ await Swal.fire({
663
+ title: "Peer to Peer HTTPS Local Network",
664
+ customClass: {
665
+ popup: "full-popup",
666
+ title: "full-title"
667
+ },
668
+ showConfirmButton: false,
669
+ html: `<div class='full-modal'>
670
+ <div>Peers can access each other over HTTPS. It's like a whole internet, but among your local machines.</div>
671
+ <br>
672
+ <img src="/peernet.png"/>
673
+ <br>
674
+ <div>To start a peer, install Pinokio on any other machine on the same network.</div>
675
+ <br>
676
+ <div>Peers automatically discover each other and display accessible apps and endpoints from other machines:</div>
677
+ <br>
678
+ <img src="/peers.png"/>
679
+ <br>
680
+ </div>`
681
+ })
682
+ }
683
+ return
684
+ }
685
+
666
686
  if (e.target.classList.contains("save")) {
667
687
  target = e.target
668
688
  } else {
@@ -0,0 +1,874 @@
1
+ <html>
2
+ <head>
3
+ <title>Pinokio</title>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
6
+ <link href="/xterm.min.css" rel="stylesheet" />
7
+ <link href="/css/fontawesome.min.css" rel="stylesheet">
8
+ <link href="/css/solid.min.css" rel="stylesheet">
9
+ <link href="/css/regular.min.css" rel="stylesheet">
10
+ <link href="/css/brands.min.css" rel="stylesheet">
11
+ <link href="/markdown.css" rel="stylesheet"/>
12
+ <link href="/noty.css" rel="stylesheet"/>
13
+ <link href="/style.css" rel="stylesheet"/>
14
+ <script src="/sweetalert2.js"></script>
15
+ <script src="/commander.js"></script>
16
+ <% if (agent === "electron") { %>
17
+ <link href="/electron.css" rel="stylesheet"/>
18
+ <% } %>
19
+ <style>
20
+ .line2 {
21
+ display: flex;
22
+ align-items: center;
23
+ cursor: pointer;
24
+ background: rgba(0,0,100,0.04);
25
+ }
26
+ .line2 a {
27
+ text-decoration: none;
28
+ color: black;
29
+ }
30
+ .status {
31
+ padding: 10px;
32
+ margin: 10px;
33
+ border-radius: 10px;
34
+ }
35
+ .status.offline {
36
+ background: silver;
37
+ }
38
+ .status.online {
39
+ background: yellowgreen;
40
+ }
41
+ .switch {
42
+ padding: 10px;
43
+ margin: 10px 0;
44
+ }
45
+ .switch[data-online=true] {
46
+ color: yellowgreen;
47
+ }
48
+ .button {
49
+ padding: 10px;
50
+ }
51
+ .on, .off {
52
+ display: flex;
53
+ align-items: center;
54
+ }
55
+ header .runner {
56
+ padding: 10px 0 0;
57
+ }
58
+ .proc-table {
59
+ display: flex;
60
+ }
61
+ .proc-table .title {
62
+ flex-grow: 1;
63
+ }
64
+ .proxy {
65
+ padding: 0 10px;
66
+ text-align: right;
67
+ }
68
+ .proxy a {
69
+ text-decoration: none;
70
+ display: inline-block;
71
+ padding: 5px 10px;
72
+ border-radius: 2px;
73
+ background: black;
74
+ color: white;
75
+ }
76
+ .browser-options-btns {
77
+ display: flex;
78
+ padding-top: 5px;
79
+ }
80
+ .browser-options-row {
81
+ border-top: 1px solid rgba(0,0,0,0.1);
82
+ border-bottom: 1px solid rgba(0,0,0,0.1);
83
+ display: flex;
84
+ padding: 20px 0;
85
+ align-items: flex-start;
86
+ }
87
+ .browser-options-row h3 {
88
+ margin: 0;
89
+ font-size: 14px;
90
+ margin-bottom: 3px;
91
+ }
92
+ .line.align-top h3.table-grid {
93
+ display: grid;
94
+ grid-template-columns: repeat(3, 1fr);
95
+ }
96
+ .browser-options-row .btn.disabled {
97
+ opacity: 0.7;
98
+ }
99
+ .browser-options-row .btn {
100
+ font-weight: bold;
101
+ width: 100px;
102
+ text-align: center;
103
+ display: block;
104
+ margin-right: 10px;
105
+ flex-shrink: 0;
106
+ padding: 5px;
107
+ }
108
+ .badge {
109
+ font-size: 12px;
110
+ padding: 5px 10px;
111
+ background: rgba(0,0,100,0.1);
112
+ border-radius: 5px;
113
+ display: inline-block;
114
+ margin-top: 5px;
115
+ }
116
+ a.badge {
117
+ color: royalblue;
118
+ text-decoration: none;
119
+ cursor: pointer;
120
+ }
121
+ .badge a {
122
+ color: royalblue;
123
+ text-decoration: none;
124
+ }
125
+ body.dark .btn {
126
+ color: white;
127
+ border: 1px solid rgba(255,255,255,0.3);
128
+ }
129
+ body.dark .badge {
130
+ background: rgba(255,255,255,0.1);
131
+ }
132
+ body.dark .browser-options-row {
133
+ border-top: 1px solid rgba(255,255,255,0.05);
134
+ border-bottom: 1px solid rgba(255,255,255,0.05);
135
+ }
136
+ .description a {
137
+ color: royalblue;
138
+ text-decoration: none;
139
+ }
140
+ a.ln[href^=https] {
141
+ color: #56ad00;
142
+ }
143
+ a.ln:hover {
144
+ color: red !important;
145
+ }
146
+ .line.align-top h3 {
147
+ /*
148
+ align-items: flex-start;
149
+ */
150
+ align-items: center;
151
+ }
152
+ .line a:hover {
153
+ color: red;
154
+ }
155
+ .line img {
156
+ width: 40px;
157
+ height: 40px;
158
+ }
159
+ .btn-container {
160
+ margin-top: 10px;
161
+ }
162
+ .header-label {
163
+ padding: 0;
164
+ font-size: 50px;
165
+ letter-spacing: -2px;
166
+ font-weight: lighter;
167
+ /*
168
+ text-align: center;
169
+ */
170
+ text-transform: capitalize;
171
+ }
172
+ .header-label-sub h1 {
173
+ font-size: 25px;
174
+ letter-spacing: -1px;
175
+ font-weight: lighter;
176
+ }
177
+ .header-label-sub {
178
+ font-size: 16px;
179
+ padding: 10px 0;
180
+ border-bottom: 1px solid rgba(0,0,0,0.1);
181
+ }
182
+ .header-label-sub .s {
183
+ padding: 10px 0;
184
+ opacity: 0.7;
185
+ }
186
+ .header-container {
187
+ margin: 10px;
188
+ background: rgba(0, 0, 100, 0.04);
189
+ padding: 20px;
190
+ box-sizing: border-box;
191
+ }
192
+ .container {
193
+ display: grid;
194
+ grid-template-columns: repeat(<%=list.length%>, 1fr);
195
+ }
196
+ .dark .container-row {
197
+ background: rgba(255, 255, 255, 0.04);
198
+ }
199
+ .container-row {
200
+ background: rgba(0, 0, 100, 0.04);
201
+ padding: 15px 30px;
202
+ box-sizing: border-box;
203
+ }
204
+ .container-row.current .header-label-sub {
205
+ color: royalblue;
206
+ }
207
+ /*
208
+ .container-row.current .header-label-sub {
209
+ color: royalblue;
210
+ }
211
+ */
212
+ .desc {
213
+ /*
214
+ text-align:center;
215
+ */
216
+ padding: 10px 0;
217
+ }
218
+ #reset-label {
219
+ color: firebrick;
220
+ }
221
+ .link-label {
222
+ margin-right: 10px;
223
+ text-decoration: underline;
224
+ color: royalblue;
225
+ cursor: pointer;
226
+ }
227
+ .container-row table {
228
+ width: 100%;
229
+ table-layout: fixed;
230
+ }
231
+ table a {
232
+ text-decoration: none;
233
+ color: royalblue;
234
+ }
235
+ td:first-child, th:first-child {
236
+ max-width: 100px;
237
+ }
238
+ .container-row th h2 {
239
+ font-weight: lighter;
240
+ font-size: 18px;
241
+ margin: 0;
242
+ text-transform: uppercase;
243
+ }
244
+ .container-row th {
245
+ vertical-align: top;
246
+ border-bottom: 1px solid rgba(0,0,0,0.05);
247
+ text-align: left;
248
+ padding: 20px 0;
249
+ font-weight: normal;
250
+ font-size: 12px;
251
+ margin-bottom: 10px;
252
+ }
253
+ .container-row td.title {
254
+ overflow-wrap: break-word;
255
+ }
256
+ .container-row td {
257
+ vertical-align: top;
258
+ font-size: 14px;
259
+ padding: 5px 2px;
260
+ word-wrap: break-word;
261
+ }
262
+
263
+ body.dark .config {
264
+ color: white;
265
+ }
266
+ .config {
267
+ /*
268
+ background: rgba(0,0,0,0.9);
269
+ */
270
+ color: black;
271
+ padding: 30px;
272
+ text-align: left;
273
+ }
274
+ .config-header {
275
+ }
276
+ .config-body {
277
+ display: flex;
278
+ align-items: flex-end;
279
+ }
280
+ .config .btn {
281
+ font-size: 16px;
282
+ font-weight: bold;
283
+ width: 100px;
284
+ background: royalblue;
285
+ flex-shrink: 0;
286
+ }
287
+ .config-row {
288
+ padding: 10px 20px 0 0;
289
+ }
290
+ .label {
291
+ padding-bottom: 5px;
292
+ font-weight: bold;
293
+ }
294
+ .config-row input[type=text] {
295
+ padding: 7px;
296
+ }
297
+ .btn.save {
298
+ padding: 8px;
299
+ }
300
+ .switcher {
301
+ position: relative;
302
+ display: inline-block;
303
+ width: 60px;
304
+ height: 34px;
305
+ }
306
+
307
+ .switcher input {
308
+ opacity: 0;
309
+ width: 0;
310
+ height: 0;
311
+ }
312
+
313
+ .slider {
314
+ position: absolute;
315
+ cursor: pointer;
316
+ top: 0;
317
+ left: 0;
318
+ right: 0;
319
+ bottom: 0;
320
+ background-color: #ccc;
321
+ -webkit-transition: .4s;
322
+ transition: .4s;
323
+ }
324
+
325
+ .slider:before {
326
+ position: absolute;
327
+ content: "";
328
+ height: 26px;
329
+ width: 26px;
330
+ left: 4px;
331
+ bottom: 4px;
332
+ background-color: white;
333
+ -webkit-transition: .4s;
334
+ transition: .4s;
335
+ }
336
+
337
+ input:checked + .slider {
338
+ /*
339
+ background-color: #2196F3;
340
+ */
341
+ background-color: royalblue;
342
+ }
343
+
344
+ input:focus + .slider {
345
+ box-shadow: 0 0 1px #2196F3;
346
+ }
347
+
348
+ input:checked + .slider:before {
349
+ -webkit-transform: translateX(26px);
350
+ -ms-transform: translateX(26px);
351
+ transform: translateX(26px);
352
+ }
353
+
354
+ /* Rounded sliders */
355
+ .slider.round {
356
+ border-radius: 34px;
357
+ }
358
+
359
+ .slider.round:before {
360
+ border-radius: 50%;
361
+ }
362
+ .loading {
363
+ text-align: center;
364
+ padding: 50px;
365
+ }
366
+ .advanced a.btn {
367
+ text-align: center;
368
+ }
369
+ .advanced {
370
+ margin: 20px 0;
371
+ }
372
+ .advanced h3 {
373
+ margin: 0 0 5px;
374
+ }
375
+ .advanced .row {
376
+ display: flex;
377
+ }
378
+ .advanced input[type=text] {
379
+ flex-grow: 1;
380
+ padding: 10px;
381
+ }
382
+
383
+
384
+ @media only screen and (max-width: 480px) {
385
+ .btn2 {
386
+ padding: 5px;
387
+ font-size: 11px;
388
+ }
389
+ .nav-btns {
390
+ flex-grow: 1;
391
+ justify-content: center;
392
+ padding: 0;
393
+ }
394
+ }
395
+ </style>
396
+ <script src="/hotkeys.min.js"></script>
397
+ <script src="/sweetalert2.js"></script>
398
+ <script src="/common.js"></script>
399
+ <script src="/opener.js"></script>
400
+ </head>
401
+ <body class='<%=theme%>' data-agent="<%=agent%>">
402
+ <header class='navheader grabbable'>
403
+ <h1>
404
+ <a class='home' href="/"><img class='icon' src="/pinokio-black.png"></a>
405
+ <button class='btn2' id='back'><div><i class="fa-solid fa-chevron-left"></i></div><div>Prev</div></button>
406
+ <button class='btn2' id='forward'><div><i class="fa-solid fa-chevron-right"></i></div><div>Next</div></button>
407
+ <button class='btn2' id='refresh-page'><div><i class="fa-solid fa-rotate-right"></i></div><div>Refresh</div></button>
408
+ <a class='btn2 create-new' id='create-new-folder'><div><i class="fa-solid fa-folder-plus"></i></div><div>Create</div></a>
409
+ <a href="/network" class='btn2'><div><i class="fa-solid fa-wifi"></i></div><div>Network</div></a>
410
+ <a href="/connect" class='btn2'><div><i class="fa-solid fa-circle-user"></i></div><div>Connect</div></a>
411
+ <div class='flexible'></div>
412
+ <div class='nav-btns'>
413
+ <a class='btn2' id='explore' href="/?mode=explore"><div><i class="fa-solid fa-magnifying-glass"></i></div><div>Discover</div></a>
414
+ <a class='btn2' href="<%=portal%>" target="_blank"><div><i class="fa-solid fa-question"></i></div><div>Help</div></a>
415
+ <button class='btn2' id='genlog'><div><i class="fa-solid fa-laptop-code"></i></div><div>Logs</div></button>
416
+ <a id='downloadlogs' download class='hidden btn2' href="/pinokio/logs.zip"><div><i class="fa-solid fa-download"></i></div><div>Download logs</div></a>
417
+ <a class='btn2' href="/?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
418
+ <button id='new-window' title='open a new window' class='btn2' data-agent="<%=agent%>"><div><i class="fa-solid fa-plus"></i></div><div>Window</div></button>
419
+ </div>
420
+ </h1>
421
+ </header>
422
+ <main>
423
+ <div class='config'>
424
+ <div class='config-header'>
425
+ <div class='header-label'><i class='fa-solid fa-wifi'></i> Network</div>
426
+ <div class='desc'>Instant HTTPS for every app on your local network.</div>
427
+ </div>
428
+ <div class='config-body'>
429
+ <div class='config-row'>
430
+ <div id='active-label' class='label'><%=peer_active ? "On" : "Off"%></div>
431
+ <label class="switcher">
432
+ <input id='active' type="checkbox" <%=peer_active ? 'checked' : ''%>>
433
+ <span class="slider"></span>
434
+ </label>
435
+ </div>
436
+ <div class='config-row' id='network_name'>
437
+ <div class='label'>Network Name</div>
438
+ <input id='name' type='text' value="<%=name%>">
439
+ </div>
440
+ <button class='btn save'>Save</button>
441
+ </div>
442
+ <div class='advanced'>
443
+ <div class='row'>
444
+ <div id='advanced-label' class='link-label label'>Advanced</div>
445
+ <div id='reset-label' class='link-label label'>Reset HTTPS Certificates</div>
446
+ </div>
447
+ </div>
448
+ </div>
449
+ <div class='loading hidden'>
450
+ <i class="fa-solid fa-circle-notch fa-spin"></i> Stand by. Do not close this window..
451
+ </div>
452
+ <% if (requirements_pending) { %>
453
+ <div class='loading'>
454
+ <i class="fa-solid fa-circle-notch fa-spin"></i> Loading...
455
+ </div>
456
+ <% } else { %>
457
+ <div class='container'>
458
+ <% let brands = { win32: "windows", darwin: "apple", linux: "Linux" } %>
459
+ <% list.forEach(({ host, name, platform, processes }, index) => { %>
460
+ <div class='container-row <%=current_host === host ? 'current' : ''%>'>
461
+ <% if (current_host === host) { %>
462
+ <div class='header-label-sub'>
463
+ <h1><i class="fa-solid fa-podcast"></i> <%=name%> (this machine)</h1>
464
+ <div class='s'><i class="fa-brands fa-<%=brands[platform]%>"></i> <%=platform%> <%=host%></div>
465
+ </div>
466
+ <% } else { %>
467
+ <div class='header-label-sub'>
468
+ <h1><i class="fa-solid fa-wifi"></i> <%=name%></h1>
469
+ <div class='s'><i class="fa-brands fa-<%=brands[platform]%>"></i> <%=platform%> <%=host%></div>
470
+ </div>
471
+ <% } %>
472
+ <table>
473
+ <tr>
474
+ <th><h2>Server</h2><div>Running apps</div></th>
475
+ <th><h2>Shared</h2><div>Use on any machine on the network</div></th>
476
+ <th><h2>Local</h2><div>Use on <%=current_host%></div></th>
477
+ </tr>
478
+ <% processes.forEach((item) => { %>
479
+ <tr>
480
+ <td class='title'><%=item.name%></td>
481
+ <td>
482
+ <% item.external_router.forEach((domain) => { %>
483
+ <div>
484
+ <a class='ln' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
485
+ </div>
486
+ <% }) %>
487
+ <% if (item.external_ip) { %>
488
+ <div>
489
+ <a class='ln' target="_blank" href="http://<%=item.external_ip%>">http://<%=item.external_ip%></a>
490
+ </div>
491
+ <% } %>
492
+ </td>
493
+ <td>
494
+ <% item.internal_router.forEach((domain) => { %>
495
+ <div>
496
+ <a class='ln' target="_blank" href="https://<%=domain%>">https://<%=domain%></a>
497
+ </div>
498
+ <% }) %>
499
+ <div>
500
+ <a class='ln' target="_blank" href="http://localhost:<%=item.port%>">http://localhost:<%=item.port%></a>
501
+ </div>
502
+ </td>
503
+ </tr>
504
+ <% }) %>
505
+ </table>
506
+ </div>
507
+ <% }) %>
508
+ <div class='container-row hidden'>
509
+ <div class='header-label-sub'><i class="fa-solid fa-microchip"></i> System Programs</div>
510
+ <div class='line align-top'>
511
+ <h3>
512
+ <img class='icon' src="<%=icon%>">
513
+ <div class='col'>
514
+ <div class='title'>Pinokio</div>
515
+ <% if (proxy) { %>
516
+ <div class='description'><a href="<%=proxy.proxy%>" target="_blank"><%=proxy.proxy%></a> → <a href="<%=localhost%>" target="_blank"><%=localhost%></a></div>
517
+ <a class='wifi-qr badge' data-qr="<%=qr%>" data-url="<%=proxy.proxy%>"><i class="fa-solid fa-qrcode"></i> Scan QR Code</a>
518
+ <% } else { %>
519
+ <div class='description'>Share Pinokio <a href="<%=localhost%>" target="_blank"><%=localhost%></a> with other devices on the same network</div>
520
+ <% } %>
521
+ <div class='btn-container'>
522
+ <% if (proxy) { %>
523
+ <div id='wifi-stop' data-proxy="<%=proxy.proxy%>" class='btn'><div><i class="fa-solid fa-stop"></i> Stop</div></div>
524
+ <% } else { %>
525
+ <div id='wifi' class='btn'><div><i class="fa-solid fa-play"></i> Start</div></div>
526
+ <div id='wifi-starting' class='btn disabled hidden'><div><i class="fa-solid fa-circle-notch fa-spin"></i> Starting</div></div>
527
+ <% } %>
528
+ </div>
529
+ </div>
530
+ </h3>
531
+ </div>
532
+ <% items.forEach((item) => { %>
533
+ <div class='line align-top'>
534
+ <h3>
535
+ <img class='icon' src="<%=item.icon%>">
536
+ <div class='col'>
537
+ <div class='title'><%=item.name%></div>
538
+ <% if (item.proxy) { %>
539
+ <div class='description'><a href="<%=item.proxy%>" target="_blank"><%=item.proxy%></a> → <a href="<%=item.target%>" target="_blank"><%=item.target%></a></div>
540
+ <a class='wifi-qr badge' data-qr="<%=item.qr%>" data-url="<%=item.proxy%>"><i class="fa-solid fa-qrcode"></i> Scan QR Code</a>
541
+ <% } else { %>
542
+ <div class='description'>Share <%=item.target%> with other devices on the same network</div>
543
+ <% } %>
544
+ <div class='btn-container'>
545
+ <% if (item.running) { %>
546
+ <div data-port="<%=item.port%>" data-name="<%=item.name%>" data-target="<%=item.target%>" class='stop btn'><i class="fa-solid fa-stop"></i> Stop</div>
547
+ <% } else { %>
548
+ <div data-port="<%=item.port%>" data-name="<%=item.name%>" data-target="<%=item.target%>" class='start btn'><i class="fa-solid fa-play"></i> Start at port <%=item.port%></div>
549
+ <% } %>
550
+ </div>
551
+ </div>
552
+ </h3>
553
+ </div>
554
+ <% }) %>
555
+ </div>
556
+ <!--
557
+ <div class='container-row'>
558
+ <div class='header-label-sub'><i class="fa-solid fa-dice-d6"></i> Pinokio</div>
559
+ <% apps.forEach((item) => { %>
560
+ <a class='line align-top' href="<%=item.link%>">
561
+ <h3>
562
+ <% if (item.icon) { %>
563
+ <img class='icon' src="<%=item.icon%>">
564
+ <% } else { %>
565
+ <% if (theme === 'dark') { %>
566
+ <img class='icon' src="/pinokio-white.png">
567
+ <% } else { %>
568
+ <img class='icon' src="/pinokio-black.png">
569
+ <% } %>
570
+ <% } %>
571
+ <div class='col'>
572
+ <div class='title'><%=item.name%></div>
573
+ <div class='description'><%=item.description%></div>
574
+ </div>
575
+ <div class='spinner'>
576
+ <i class="fa-solid fa-chevron-right"></i>
577
+ </div>
578
+ </h3>
579
+ </a>
580
+ <% }) %>
581
+ </div>
582
+ -->
583
+ </div>
584
+ <% } %>
585
+ </main>
586
+ <script>
587
+ document.querySelector("#reset-label").addEventListener("click", async (e) => {
588
+ e.preventDefault()
589
+ e.stopPropagation()
590
+ let ok = confirm("are you sure you want to re-generate http certificates?")
591
+ if (ok) {
592
+ let r = await fetch("/network/reset", {
593
+ method: "post",
594
+ headers: {
595
+ "Content-Type": "application/json"
596
+ }
597
+ }).then((res) => {
598
+ return res.json()
599
+ })
600
+ if (r.success) {
601
+ //alert("Successfully updated the pinokio home to " + val)
602
+ document.querySelector(".loading").classList.remove("hidden")
603
+ document.querySelector(".save").classList.add("hidden")
604
+ fetch("/restart", {
605
+ method: "post"
606
+ }, () => {
607
+ })
608
+ setInterval(async () => {
609
+ try {
610
+ let res = await fetch("/check_peer").then((res) => {
611
+ return res.json()
612
+ })
613
+ if (res.success) {
614
+ document.querySelector(".loading").classList.add("hidden")
615
+ location.href = location.href
616
+ }
617
+ } catch (e) {
618
+ console.log(e)
619
+ }
620
+ }, 1000)
621
+ } else if (r.error) {
622
+ alert(r.error)
623
+ location.href = location.href
624
+ }
625
+ }
626
+
627
+ })
628
+ document.querySelector("#advanced-label").addEventListener("click", (e) => {
629
+ e.preventDefault()
630
+ e.stopPropagation()
631
+ //document.querySelector("#advanced-input").classList.toggle("hidden")
632
+ Commander({
633
+ callback: "/network",
634
+ path: "~/network",
635
+ icon: "fa-solid fa-circle-down",
636
+ title: "Custom Router",
637
+ placeholder: "enter a custom router git url",
638
+ description: `<div>you can write your own custom routers to handle custom request handling for any port <a target="_blank" href="<%=docs%>">Learn more</a></div>`,
639
+ target: "_top",
640
+ message: (url) => {
641
+ let name = new URL(url).pathname.split("/").slice(-2).join("_")
642
+ return encodeURIComponent(`git clone ${url} ${name}`)
643
+ }
644
+ })
645
+ })
646
+ if (document.querySelector("#active").checked) {
647
+ console.log("checked")
648
+ document.querySelector("#network_name").classList.remove("hidden")
649
+ } else {
650
+ console.log("not checked")
651
+ document.querySelector("#network_name").classList.add("hidden")
652
+ }
653
+ document.querySelector("#active").addEventListener("change", (e)=> {
654
+ console.log(e.target.checked)
655
+ if (e.target.checked) {
656
+ document.querySelector("#network_name").classList.remove("hidden")
657
+ document.querySelector("#active-label").innerHTML = "On"
658
+ } else {
659
+ document.querySelector("#network_name").classList.add("hidden")
660
+ document.querySelector("#active-label").innerHTML = "Off"
661
+ }
662
+ })
663
+ document.querySelector("main").addEventListener("click", async (e) => {
664
+ let target
665
+
666
+ if (e.target.classList.contains("save")) {
667
+ target = e.target
668
+ } else {
669
+ target = e.target.closest(".save")
670
+ }
671
+
672
+ if (target) {
673
+ let active = document.querySelector("#active").checked ? "1": "0"
674
+ let body = {
675
+ PINOKIO_NETWORK_NAME: document.querySelector("#name").value,
676
+ PINOKIO_NETWORK_ACTIVE: active,
677
+ PINOKIO_HTTPS_ACTIVE: active,
678
+ }
679
+ /*
680
+ let body = {
681
+ PINOKIO_NETWORK_NAME: document.querySelector("#name").value,
682
+ PINOKIO_NETWORK_ACTIVE: (document.querySelector("#peer_active").checked ? "1": "0"),
683
+ PINOKIO_HTTPS_ACTIVE: (document.querySelector("#https_active").checked ? "1": "0")
684
+ }
685
+ */
686
+ console.log(body)
687
+ let r = await fetch("/network", {
688
+ method: "post",
689
+ headers: { "Content-Type": "application/json" },
690
+ body: JSON.stringify(body)
691
+ }).then((res) => {
692
+ return res.json()
693
+ })
694
+ if (r.success) {
695
+ //alert("Successfully updated the pinokio home to " + val)
696
+ document.querySelector(".loading").classList.remove("hidden")
697
+ document.querySelector(".save").classList.add("hidden")
698
+ fetch("/restart", {
699
+ method: "post"
700
+ }, () => {
701
+ })
702
+ setInterval(async () => {
703
+ try {
704
+ let res = await fetch("/check_peer").then((res) => {
705
+ return res.json()
706
+ })
707
+ if (res.success) {
708
+ document.querySelector(".loading").classList.add("hidden")
709
+ location.href = location.href
710
+ }
711
+ } catch (e) {
712
+ console.log(e)
713
+ }
714
+ }, 1000)
715
+ } else if (r.error) {
716
+ alert(r.error)
717
+ location.href = location.href
718
+ }
719
+ return
720
+ }
721
+
722
+ if (e.target.classList.contains("stop")) {
723
+ target = e.target
724
+ } else {
725
+ target = e.target.closest(".stop")
726
+ }
727
+ if (target) {
728
+ // stop
729
+ let res = await fetch("/proxy", {
730
+ method: "post",
731
+ headers: {
732
+ "Content-Type": "application/json"
733
+ },
734
+ body: JSON.stringify({
735
+ action: "stop",
736
+ target: target.getAttribute("data-target"),
737
+ name: target.getAttribute("data-name")
738
+ })
739
+ }).then((res) => {
740
+ return res.json()
741
+ })
742
+ location.href = location.href
743
+ return
744
+ }
745
+
746
+ if (e.target.classList.contains("start")) {
747
+ target = e.target
748
+ } else {
749
+ target = e.target.closest(".start")
750
+ }
751
+ if (target) {
752
+ let res = await fetch("/proxy", {
753
+ method: "post",
754
+ headers: {
755
+ "Content-Type": "application/json"
756
+ },
757
+ body: JSON.stringify({
758
+ action: "start",
759
+ target: target.getAttribute("data-target"),
760
+ name: target.getAttribute("data-name"),
761
+ port: target.getAttribute("data-port")
762
+ })
763
+ }).then((res) => {
764
+ return res.json()
765
+ })
766
+ location.href = location.href
767
+ }
768
+
769
+ if (e.target.classList.contains("wifi-qr")) {
770
+ target = e.target
771
+ } else {
772
+ target = e.target.closest(".wifi-qr")
773
+ }
774
+ if (target) {
775
+ let url = target.getAttribute("data-url")
776
+ let qr = target.getAttribute("data-qr")
777
+ console.log({ url, qr })
778
+ await Swal.fire({
779
+ title: "Local Share",
780
+ customClass: {
781
+ popup: "min-popup",
782
+ title: "min-title"
783
+ },
784
+ showConfirmButton: false,
785
+ html: `<div class='min-modal'>
786
+ <div>Access Pinokio from</div>
787
+ <div>any local network device</div>
788
+ <div><img src="${qr}"></div>
789
+ <a href="${url}" target="_blank">${url}</a>
790
+ </div>`
791
+ })
792
+ }
793
+
794
+
795
+ })
796
+ if (document.querySelector("#back")) {
797
+ document.querySelector("#back").addEventListener("click", (e) => {
798
+ history.back()
799
+ })
800
+ }
801
+ if (document.querySelector("#forward")) {
802
+ document.querySelector("#forward").addEventListener("click", (e) => {
803
+ history.forward()
804
+ })
805
+ }
806
+ <% if (proxy) { %>
807
+ if (document.querySelector("#wifi-stop")) {
808
+ document.querySelector("#wifi-stop").addEventListener("click", async (e) => {
809
+ let res = await fetch("/unpublish", {
810
+ method: "post",
811
+ headers: {
812
+ "Content-Type": "application/json"
813
+ },
814
+ body: JSON.stringify({
815
+ type: "local"
816
+ })
817
+ }).then((res) => {
818
+ return res.json()
819
+ })
820
+ console.log(res)
821
+ location.href = location.href
822
+ })
823
+ }
824
+ <% } else { %>
825
+ if (document.querySelector("#wifi")) {
826
+ document.querySelector("#wifi").addEventListener("click", async (e) => {
827
+ document.querySelector("#wifi").classList.add("hidden")
828
+ document.querySelector("#wifi-starting").classList.remove("hidden")
829
+ let res = await fetch("/publish", {
830
+ method: "post",
831
+ headers: {
832
+ "Content-Type": "application/json"
833
+ },
834
+ body: JSON.stringify({
835
+ type: "local"
836
+ })
837
+ }).then((res) => {
838
+ return res.json()
839
+ })
840
+ console.log(res)
841
+ location.href = location.href
842
+ })
843
+ }
844
+ <% } %>
845
+ <% if (requirements_pending) { %>
846
+ setInterval(() => {
847
+ fetch("/requirements_check/network").then((res) => {
848
+ return res.json()
849
+ }).then((res) => {
850
+ console.log(res)
851
+ if (!res.requirements_pending) {
852
+ if (res.install_required) {
853
+ location.href = "/setup/network?callback=/network"
854
+ } else {
855
+ location.href = location.href
856
+ }
857
+ }
858
+ })
859
+ }, 2000)
860
+ <% } %>
861
+ <% if (peer_active) { %>
862
+ setInterval(() => {
863
+ fetch("/peer_check").then((res) => {
864
+ return res.json()
865
+ }).then((res) => {
866
+ if (res.updated) {
867
+ location.href = location.href
868
+ }
869
+ })
870
+ }, 2000)
871
+ <% } %>
872
+ </script>
873
+ </body>
874
+ </html>