pinokiod 1.3.95 → 1.3.97
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/shell.js +2 -1
- package/kernel/util.js +1 -1
- package/package.json +1 -1
- package/pipe/index.js +30 -7
- package/pipe/views/login.ejs +1 -1
- package/server/views/fullscreen_editor.ejs +30 -106
- package/server/views/share_editor.ejs +99 -11
package/kernel/shell.js
CHANGED
package/kernel/util.js
CHANGED
package/package.json
CHANGED
package/pipe/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { createProxyMiddleware } = require('http-proxy-middleware');
|
|
2
|
+
const { yellow, green, blue } = require('kleur');
|
|
2
3
|
|
|
3
4
|
const path = require('path')
|
|
4
5
|
const { createHttpTerminator } = require('http-terminator')
|
|
@@ -16,17 +17,12 @@ class Pipe {
|
|
|
16
17
|
if (req.session.authenticated) {
|
|
17
18
|
next();
|
|
18
19
|
} else {
|
|
19
|
-
console.log("not authenticated. redirect /pionkio/login")
|
|
20
20
|
res.redirect('/pinokio/login');
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
// a pipe server for creating publicly shareable endpoints
|
|
24
24
|
async start(url, scriptPath, passcode, config) {
|
|
25
|
-
|
|
26
|
-
if (!config) config = {}
|
|
27
|
-
if (config.icon) {
|
|
28
|
-
}
|
|
29
|
-
|
|
25
|
+
console.log("pipe.start", { url, scriptPath, passcode, config })
|
|
30
26
|
|
|
31
27
|
let port = await this.kernel.port()
|
|
32
28
|
let app = express();
|
|
@@ -49,7 +45,34 @@ class Pipe {
|
|
|
49
45
|
const { password } = req.body;
|
|
50
46
|
|
|
51
47
|
//if (await bcrypt.compare(password, hashedPassword)) {
|
|
52
|
-
|
|
48
|
+
|
|
49
|
+
// multiple passcode options supported (comma separated)
|
|
50
|
+
let passcode_options = passcode.split(",")
|
|
51
|
+
if (passcode_options.includes(password)) {
|
|
52
|
+
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
|
|
53
|
+
const agent = req.headers['user-agent']
|
|
54
|
+
let event = {
|
|
55
|
+
time: `${new Date().toLocaleString()}`,
|
|
56
|
+
ip,
|
|
57
|
+
agent
|
|
58
|
+
}
|
|
59
|
+
let log = `\r\n[${event.time}] Login from ${event.ip} | ${event.agent}\r\n`
|
|
60
|
+
console.log(log)
|
|
61
|
+
// this.kernel.api.ondata({
|
|
62
|
+
// id: scriptPath,
|
|
63
|
+
// type: "notify",
|
|
64
|
+
// data: {
|
|
65
|
+
// html: `<div><h6>${event.time}</h6><div>IP: ${event.ip}</div>`
|
|
66
|
+
// }
|
|
67
|
+
// })
|
|
68
|
+
this.kernel.api.ondata({
|
|
69
|
+
type: "stream",
|
|
70
|
+
kernel: true,
|
|
71
|
+
id: scriptPath,
|
|
72
|
+
data: {
|
|
73
|
+
raw: yellow(log)
|
|
74
|
+
}
|
|
75
|
+
})
|
|
53
76
|
req.session.authenticated = true;
|
|
54
77
|
res.redirect('/');
|
|
55
78
|
} else {
|
package/pipe/views/login.ejs
CHANGED
|
@@ -67,7 +67,7 @@ h1 {
|
|
|
67
67
|
-->
|
|
68
68
|
<h1>PINOKIO</h1>
|
|
69
69
|
<form method="POST" action="/pinokio/login">
|
|
70
|
-
<input placeholder="enter passcode" type="password" name="password" required>
|
|
70
|
+
<input autofocus placeholder="enter passcode" type="password" name="password" required>
|
|
71
71
|
<button type="submit">Login</button>
|
|
72
72
|
</form>
|
|
73
73
|
<% if (error) { %>
|
|
@@ -104,21 +104,6 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
104
104
|
<% if (error) { %>
|
|
105
105
|
document.querySelector(".requirements .content").innerHTML = '<div class="loading"><i class="fa-solid fa-circle-exclamation"></i> <%=error%></div>'
|
|
106
106
|
<% } %>
|
|
107
|
-
<% if (!install_required) { %>
|
|
108
|
-
/*
|
|
109
|
-
document.querySelector("footer").addEventListener("click", (e) => {
|
|
110
|
-
if (e.target.classList.contains("toggle-expand") || e.target.closest(".toggle-expand")) {
|
|
111
|
-
let el = document.querySelector("footer .toggle-expand i")
|
|
112
|
-
if (el.classList.contains("fa-circle-chevron-up")) {
|
|
113
|
-
el.className = "fa-solid fa-circle-chevron-down"
|
|
114
|
-
} else if (el.classList.contains("fa-circle-chevron-down")) {
|
|
115
|
-
el.className = "fa-solid fa-circle-chevron-up"
|
|
116
|
-
}
|
|
117
|
-
document.querySelector("footer").classList.toggle("expanded")
|
|
118
|
-
}
|
|
119
|
-
})
|
|
120
|
-
*/
|
|
121
|
-
<% } %>
|
|
122
107
|
<% if (requirements_pending) { %>
|
|
123
108
|
await new Promise((resolve, reject) => {
|
|
124
109
|
let interval = setInterval(() => {
|
|
@@ -641,34 +626,29 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
641
626
|
}
|
|
642
627
|
});
|
|
643
628
|
|
|
644
|
-
document.querySelector(".stop")
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
629
|
+
if (document.querySelector(".stop")) {
|
|
630
|
+
document.querySelector(".stop").addEventListener("click", async (e) => {
|
|
631
|
+
await rpc.stop()
|
|
632
|
+
// socket.run({
|
|
633
|
+
// id: location.pathname,
|
|
634
|
+
// method: "kernel.api.stop"
|
|
635
|
+
// }, (stream) => {
|
|
636
|
+
// console.log("#", stream)
|
|
637
|
+
// })
|
|
638
|
+
document.querySelector(".run .play").classList.remove("hidden")
|
|
639
|
+
document.querySelector(".run .starting").classList.add("hidden")
|
|
640
|
+
document.querySelector(".run .stop").classList.add("hidden")
|
|
641
|
+
})
|
|
642
|
+
}
|
|
643
|
+
if (document.querySelector(".play")) {
|
|
644
|
+
document.querySelector(".play").addEventListener("click", async (e) => {
|
|
645
|
+
document.querySelector(".run .play").classList.add("hidden")
|
|
646
|
+
document.querySelector(".run .starting").classList.remove("hidden")
|
|
647
|
+
await rpc.run()
|
|
648
|
+
})
|
|
649
|
+
}
|
|
661
650
|
<% } %>
|
|
662
651
|
|
|
663
|
-
/*
|
|
664
|
-
document.querySelector("#save").addEventListener("click", async (e) => {
|
|
665
|
-
console.log("SAVE")
|
|
666
|
-
await rpc.save()
|
|
667
|
-
n.Noty({
|
|
668
|
-
text: `saved`,
|
|
669
|
-
})
|
|
670
|
-
})
|
|
671
|
-
*/
|
|
672
652
|
<% if (stop) { %>
|
|
673
653
|
await rpc.stop()
|
|
674
654
|
document.querySelector(".run .play").classList.remove("hidden")
|
|
@@ -685,74 +665,18 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
685
665
|
<% } %>
|
|
686
666
|
<% } %>
|
|
687
667
|
<% } %>
|
|
688
|
-
/*
|
|
689
|
-
document.querySelector("#explore").addEventListener("click", async (e) => {
|
|
690
|
-
e.preventDefault()
|
|
691
|
-
e.stopPropagation()
|
|
692
|
-
location.href = "/?mode=explore"
|
|
693
|
-
})
|
|
694
|
-
Reporter()
|
|
695
|
-
*/
|
|
696
668
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
// let url = target.getAttribute("data-url")
|
|
706
|
-
// const token = prompt("Enter an Ngrok auth token to expose your machine to the public Internet https://ngrok.com/")
|
|
707
|
-
// if (token && token.length > 0) {
|
|
708
|
-
// let response = await fetch("/pinokio/tunnel", {
|
|
709
|
-
// method: "POST",
|
|
710
|
-
// headers: {
|
|
711
|
-
// "Content-Type": "application/json"
|
|
712
|
-
// },
|
|
713
|
-
// body: JSON.stringify({ url, action: "start", token })
|
|
714
|
-
// }).then((res) => {
|
|
715
|
-
// return res.json()
|
|
716
|
-
// })
|
|
717
|
-
// await reloadMemory()
|
|
718
|
-
// }
|
|
719
|
-
// } else if (target2) {
|
|
720
|
-
// console.log("target2", target2)
|
|
721
|
-
// e.preventDefault()
|
|
722
|
-
// e.stopPropagation()
|
|
723
|
-
// let url = target2.getAttribute("data-url")
|
|
724
|
-
// let response = await fetch("/pinokio/tunnel", {
|
|
725
|
-
// method: "POST",
|
|
726
|
-
// headers: {
|
|
727
|
-
// "Content-Type": "application/json"
|
|
728
|
-
// },
|
|
729
|
-
// body: JSON.stringify({ url, action: "stop" })
|
|
730
|
-
// }).then((res) => {
|
|
731
|
-
// return res.json()
|
|
732
|
-
// })
|
|
733
|
-
// await reloadMemory()
|
|
734
|
-
// }
|
|
735
|
-
// })
|
|
736
|
-
/*
|
|
737
|
-
document.querySelector("#source").addEventListener("click", (e) => {
|
|
738
|
-
refreshParent({
|
|
739
|
-
id: "<%=filepath%>",
|
|
740
|
-
action: {
|
|
741
|
-
"type": "newtab",
|
|
742
|
-
"url": e.target.getAttribute("data-editor-url")
|
|
669
|
+
if (document.querySelector("#lv")) {
|
|
670
|
+
document.querySelector("#lv").addEventListener("click", (e) => {
|
|
671
|
+
let m = e.target.closest(".memory")
|
|
672
|
+
m.classList.toggle("collapsed")
|
|
673
|
+
if (m.classList.contains("collapsed")) {
|
|
674
|
+
m.querySelector("i").className = "fa-solid fa-sort-down"
|
|
675
|
+
} else {
|
|
676
|
+
m.querySelector("i").className = "fa-solid fa-sort-up"
|
|
743
677
|
}
|
|
744
678
|
})
|
|
745
|
-
}
|
|
746
|
-
*/
|
|
747
|
-
document.querySelector("#lv").addEventListener("click", (e) => {
|
|
748
|
-
let m = e.target.closest(".memory")
|
|
749
|
-
m.classList.toggle("collapsed")
|
|
750
|
-
if (m.classList.contains("collapsed")) {
|
|
751
|
-
m.querySelector("i").className = "fa-solid fa-sort-down"
|
|
752
|
-
} else {
|
|
753
|
-
m.querySelector("i").className = "fa-solid fa-sort-up"
|
|
754
|
-
}
|
|
755
|
-
})
|
|
679
|
+
}
|
|
756
680
|
})
|
|
757
681
|
const reloadMemory = async () => {
|
|
758
682
|
let url = "/pinokio/memory?filepath=<%=encodedFilePath%>"
|
|
@@ -137,7 +137,6 @@ hr {
|
|
|
137
137
|
height: 1px;
|
|
138
138
|
}
|
|
139
139
|
.shared {
|
|
140
|
-
margin-left: 30px;
|
|
141
140
|
}
|
|
142
141
|
.shared a {
|
|
143
142
|
display: block;
|
|
@@ -145,19 +144,90 @@ hr {
|
|
|
145
144
|
}
|
|
146
145
|
.shared img {
|
|
147
146
|
width: 200px;
|
|
147
|
+
border: 1px solid rgba(0,0,0,0.1);
|
|
148
|
+
}
|
|
149
|
+
.shared h2 {
|
|
150
|
+
margin: 0;
|
|
151
|
+
letter-spacing: -1px;
|
|
152
|
+
}
|
|
153
|
+
.passcode ol {
|
|
154
|
+
padding-inline-start: 15px;
|
|
155
|
+
margin: 5px 0 10px;
|
|
156
|
+
}
|
|
157
|
+
body.dark .passcode {
|
|
158
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
159
|
+
}
|
|
160
|
+
.passcode {
|
|
161
|
+
margin-left: 30px;
|
|
162
|
+
padding: 20px !important;
|
|
163
|
+
box-sizing: border-box;
|
|
164
|
+
border: 1px solid rgba(0,0,0,0.1);
|
|
165
|
+
margin-top: 20px;
|
|
166
|
+
margin-bottom: 20px;
|
|
167
|
+
}
|
|
168
|
+
.passcode h2 {
|
|
169
|
+
margin: 0;
|
|
170
|
+
font-size: 20px;
|
|
171
|
+
letter-spacing: -1px;
|
|
172
|
+
}
|
|
173
|
+
.passcode div {
|
|
174
|
+
padding: 0 0 10px;
|
|
175
|
+
}
|
|
176
|
+
.passcode .row {
|
|
177
|
+
display: flex;
|
|
178
|
+
padding: 0;
|
|
179
|
+
}
|
|
180
|
+
body.dark .passcode .row input {
|
|
181
|
+
background: white;
|
|
182
|
+
}
|
|
183
|
+
.passcode .row input {
|
|
184
|
+
border-radius: 0;
|
|
185
|
+
flex-grow: 1;
|
|
186
|
+
padding: 10px;
|
|
187
|
+
box-sizing: border-box;
|
|
188
|
+
border: none;
|
|
189
|
+
background: rgba(0,0,100,0.1);
|
|
190
|
+
}
|
|
191
|
+
body.dark .passcode button {
|
|
192
|
+
background: royalblue;
|
|
193
|
+
}
|
|
194
|
+
.passcode button {
|
|
195
|
+
border-radius: 0;
|
|
196
|
+
}
|
|
197
|
+
body.dark .cloudflare-running {
|
|
198
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
199
|
+
}
|
|
200
|
+
.cloudflare-running {
|
|
201
|
+
margin-left: 30px;
|
|
202
|
+
padding: 20px !important;
|
|
203
|
+
box-sizing: border-box;
|
|
204
|
+
border: 1px solid rgba(0,0,0,0.1);
|
|
148
205
|
}
|
|
149
206
|
|
|
150
207
|
</style>
|
|
151
208
|
<script>
|
|
152
209
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
153
210
|
const n = new N()
|
|
211
|
+
document.querySelector("#reveal").addEventListener("click", async (e) => {
|
|
212
|
+
let el = document.querySelector("#passcode")
|
|
213
|
+
if (el.type === "password") {
|
|
214
|
+
el.type = "text"
|
|
215
|
+
} else {
|
|
216
|
+
el.type = "password"
|
|
217
|
+
}
|
|
218
|
+
})
|
|
154
219
|
document.querySelector("#save").addEventListener("click", async (e) => {
|
|
155
220
|
// save
|
|
156
221
|
const vals = {}
|
|
157
222
|
const els = document.querySelectorAll(".env-item input[type=checkbox]")
|
|
158
223
|
for(let el of els) {
|
|
159
|
-
|
|
224
|
+
let id = el.getAttribute("data-id")
|
|
225
|
+
vals[id] = el.checked
|
|
160
226
|
}
|
|
227
|
+
|
|
228
|
+
const passcode_el = document.querySelector("#passcode")
|
|
229
|
+
vals.PINOKIO_SHARE_PASSCODE = passcode_el.value
|
|
230
|
+
|
|
161
231
|
let res = await fetch("/env", {
|
|
162
232
|
method: "POST",
|
|
163
233
|
headers: {
|
|
@@ -165,7 +235,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
165
235
|
},
|
|
166
236
|
body: JSON.stringify({
|
|
167
237
|
filepath: "api/" + location.pathname.slice(7),
|
|
168
|
-
vals
|
|
238
|
+
vals,
|
|
169
239
|
})
|
|
170
240
|
}).then((res) => {
|
|
171
241
|
return res.json()
|
|
@@ -185,23 +255,41 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
185
255
|
<hr>
|
|
186
256
|
<div class='env-item'>
|
|
187
257
|
<% if (config.PINOKIO_SHARE_CLOUDFLARE.toString() === "true") { %>
|
|
188
|
-
<input type="checkbox" id="PINOKIO_SHARE_CLOUDFLARE" name="cloudflare" checked />
|
|
258
|
+
<input type="checkbox" data-id="PINOKIO_SHARE_CLOUDFLARE" name="cloudflare" checked />
|
|
189
259
|
<% } else { %>
|
|
190
|
-
<input type="checkbox" id="PINOKIO_SHARE_CLOUDFLARE" name="cloudflare" />
|
|
260
|
+
<input type="checkbox" data-id="PINOKIO_SHARE_CLOUDFLARE" name="cloudflare" />
|
|
191
261
|
<% } %>
|
|
192
262
|
<label for="cloudflare"><i class="fa-brands fa-cloudflare"></i> Run a Public Node</label>
|
|
193
263
|
<ul>
|
|
194
264
|
<li>This will let you share your local Pinokio app to the public internet, which anyone with a URL can access.</li>
|
|
195
265
|
<li>Powered by <a href="https://www.cloudflare.com/products/tunnel/" target="_blank" features="browser">Cloudflare Tunnel</a></li>
|
|
196
266
|
<li>You can manually configure this in through the Configure menu (set PINOKIO_SHARE_CLOUDFLARE to true)</li>
|
|
197
|
-
<li>You can add <strong>authentication</strong> by adding a passcode (set PINOKIO_SHARE_PASSCODE in the <strong>Configure</strong> tab)</li>
|
|
198
267
|
</ul>
|
|
199
|
-
|
|
200
|
-
<
|
|
201
|
-
|
|
202
|
-
<
|
|
268
|
+
<div class='passcode'>
|
|
269
|
+
<h2>Passcode (optional)</h2>
|
|
270
|
+
<ol>
|
|
271
|
+
<li><strong>No passcode:</strong> Anyone with the URL can use the app</li>
|
|
272
|
+
<li><strong>Passcode:</strong> Users will need to authenticate with the passcode before using the app.</li>
|
|
273
|
+
<li>To allow multiple passcodes, separate the passode by commas (Example: ab12zd3,D3ad3a,df3d9Fa).</li>
|
|
274
|
+
</ol>
|
|
275
|
+
<div class='row'>
|
|
276
|
+
<input id='passcode' type='password' value="<%=config.PINOKIO_SHARE_PASSCODE || ''%>"/>
|
|
277
|
+
<button class='btn' id='reveal'><i class="fa-solid fa-eye"></i></button>
|
|
203
278
|
</div>
|
|
204
|
-
|
|
279
|
+
</div>
|
|
280
|
+
<% if (cloudflare_links.length > 0) { %>
|
|
281
|
+
<div class='cloudflare-running'>
|
|
282
|
+
<div class='shared'>
|
|
283
|
+
<h2><i class="fa-solid fa-circle-notch fa-spin"></i> Running</h2>
|
|
284
|
+
</div>
|
|
285
|
+
<% cloudflare_links.forEach((link) => { %>
|
|
286
|
+
<div class='shared'>
|
|
287
|
+
<a href="<%=link.url%>" target="_blank" features="browser"><%=link.url%></a>
|
|
288
|
+
<img src="<%=link.qr%>">
|
|
289
|
+
</div>
|
|
290
|
+
<% }) %>
|
|
291
|
+
</div>
|
|
292
|
+
<% } %>
|
|
205
293
|
</div>
|
|
206
294
|
<div class='env-item'>
|
|
207
295
|
<% if (config.PINOKIO_SHARE_LOCAL.toString() === "true") { %>
|