pinokiod 1.3.96 → 1.3.98

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.
@@ -16,6 +16,7 @@ class C {
16
16
  => cloudflared tunnel --url http://localhost:8080
17
17
  */
18
18
 
19
+ console.log("*tunnel", req.params)
19
20
 
20
21
  let pipe_uri
21
22
  if (req.params.passcode) {
@@ -64,6 +64,7 @@ class Local {
64
64
  */
65
65
 
66
66
  let current_env = await Environment.get2(req.parent.path, kernel)
67
+ console.log({ current_env })
67
68
 
68
69
  // if the key at current_env.PINOKIO_SHARE_VAR (by default it's 'url') is the variable being set, trigger the share logic
69
70
  if (current_env.PINOKIO_SHARE_VAR && current_env.PINOKIO_SHARE_VAR in req.params) {
@@ -293,10 +293,12 @@ const init_folders = async (homedir) => {
293
293
 
294
294
  // Get the actual environment variable at specific path
295
295
  const get2 = async (filepath, kernel) => {
296
+ console.log("Get2")
296
297
  let api_path = Util.api_path(filepath, kernel)
297
298
  let default_env = await get(kernel.homedir)
298
299
  let api_env = await get(api_path)
299
- let current_env = Object.assign(process.env, default_env, api_env)
300
+ console.log({ api_path, default_env, api_env, process_env: process.env })
301
+ let current_env = Object.assign({}, process.env, default_env, api_env)
300
302
  return current_env
301
303
  }
302
304
  const get = async (homedir) => {
package/kernel/shell.js CHANGED
@@ -736,7 +736,8 @@ class Shell {
736
736
  # index: ${this.index}
737
737
  # cmd: ${this.cmd}
738
738
  # timestamp: ${time}
739
- # ${infoYAML.replaceAll("\n", "\n# ")}
739
+
740
+ ${infoYAML}
740
741
 
741
742
  `
742
743
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "1.3.96",
3
+ "version": "1.3.98",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
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
- if (password === passcode) {
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 {
@@ -15,7 +15,7 @@ body {
15
15
  color: white;
16
16
  }
17
17
  main {
18
- max-width: 500px;
18
+ max-width: 300px;
19
19
  width: 100%;
20
20
  text-align: center;
21
21
  text-transform: lowercase;
@@ -35,8 +35,8 @@ main input {
35
35
  /*
36
36
  background: rgba(0,0,0,0.06);
37
37
  */
38
- font-size: 14px;
39
- border-radius: 0;
38
+ font-size: 16px;
39
+ border-radius: 10px 0 0 10px;
40
40
 
41
41
  }
42
42
  main button {
@@ -45,6 +45,8 @@ main button {
45
45
  padding: 10px;
46
46
  box-sizing: border-box;
47
47
  border: none;
48
+ font-size: 16px;
49
+ border-radius: 0 10px 10px 0;
48
50
  }
49
51
  .error {
50
52
  border-left: 3px solid red;
@@ -67,7 +69,7 @@ h1 {
67
69
  -->
68
70
  <h1>PINOKIO</h1>
69
71
  <form method="POST" action="/pinokio/login">
70
- <input placeholder="enter passcode" type="password" name="password" required>
72
+ <input autofocus placeholder="Enter passcode" type="password" name="password" required>
71
73
  <button type="submit">Login</button>
72
74
  </form>
73
75
  <% if (error) { %>
@@ -173,6 +173,7 @@ h1 {
173
173
  font-size: 50px;
174
174
  font-family: georgia;
175
175
  font-style: italic;
176
+ padding-bottom: 20px;
176
177
  }
177
178
  footer {
178
179
  background: none;
@@ -263,6 +264,7 @@ document.addEventListener("DOMContentLoaded", async () => {
263
264
  </header>
264
265
  <% } %>
265
266
  <div id='editor' class='<%=init ? "hidden" : ''%>'>
267
+ <h1>Configure</h1>
266
268
  <% items.forEach((item) => { %>
267
269
  <% if (item.type === "comment") { %>
268
270
  <div class='comment'><%=item.val.replaceAll(/^[ #]+/g, "")%></div>
@@ -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").addEventListener("click", async (e) => {
645
- await rpc.stop()
646
- // socket.run({
647
- // id: location.pathname,
648
- // method: "kernel.api.stop"
649
- // }, (stream) => {
650
- // console.log("#", stream)
651
- // })
652
- document.querySelector(".run .play").classList.remove("hidden")
653
- document.querySelector(".run .starting").classList.add("hidden")
654
- document.querySelector(".run .stop").classList.add("hidden")
655
- })
656
- document.querySelector(".play").addEventListener("click", async (e) => {
657
- document.querySelector(".run .play").classList.add("hidden")
658
- document.querySelector(".run .starting").classList.remove("hidden")
659
- await rpc.run()
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
- // document.querySelector(".memory").addEventListener("click", async (e) => {
700
- // let target = e.target.classList.contains("tunnel") ? e.target : e.target.closest(".tunnel")
701
- // let target2 = e.target.classList.contains("tunnel-stop") ? e.target : e.target.closest(".tunnel-stop")
702
- // if (target) {
703
- // e.preventDefault()
704
- // e.stopPropagation()
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%>"
@@ -74,9 +74,10 @@ td.key {
74
74
 
75
75
  }
76
76
  #editor {
77
+ margin: 0 auto;
77
78
  box-sizing: border-box;
78
79
  max-width: 800px;
79
- padding: 20px;
80
+ padding: 50px;
80
81
  }
81
82
  header .runner {
82
83
  padding: 10px 0 0;
@@ -137,7 +138,6 @@ hr {
137
138
  height: 1px;
138
139
  }
139
140
  .shared {
140
- margin-left: 30px;
141
141
  }
142
142
  .shared a {
143
143
  display: block;
@@ -145,19 +145,96 @@ hr {
145
145
  }
146
146
  .shared img {
147
147
  width: 200px;
148
+ border: 1px solid rgba(0,0,0,0.1);
149
+ }
150
+ .shared h2 {
151
+ margin: 0;
152
+ letter-spacing: -1px;
153
+ }
154
+ .passcode ol {
155
+ padding-inline-start: 15px;
156
+ margin: 5px 0 10px;
157
+ }
158
+ body.dark .passcode {
159
+ border: 1px solid rgba(255,255,255,0.1);
160
+ }
161
+ .passcode {
162
+ margin-left: 30px;
163
+ padding: 20px !important;
164
+ box-sizing: border-box;
165
+ border: 1px solid rgba(0,0,0,0.1);
166
+ margin-top: 20px;
167
+ margin-bottom: 20px;
168
+ }
169
+ .passcode h2 {
170
+ margin: 0;
171
+ font-size: 20px;
172
+ letter-spacing: -1px;
173
+ }
174
+ .passcode div {
175
+ padding: 0 0 10px;
176
+ }
177
+ .passcode .row {
178
+ display: flex;
179
+ padding: 0;
180
+ }
181
+ body.dark .passcode .row input {
182
+ background: white;
183
+ }
184
+ .passcode .row input {
185
+ border-radius: 0;
186
+ flex-grow: 1;
187
+ padding: 10px;
188
+ box-sizing: border-box;
189
+ border: none;
190
+ background: rgba(0,0,100,0.1);
191
+ }
192
+ body.dark .passcode button {
193
+ background: royalblue;
194
+ }
195
+ .passcode button {
196
+ border-radius: 0;
197
+ }
198
+ body.dark .cloudflare-running {
199
+ border: 1px solid rgba(255,255,255,0.1);
200
+ }
201
+ .cloudflare-running {
202
+ margin-left: 30px;
203
+ padding: 20px !important;
204
+ box-sizing: border-box;
205
+ border: 1px solid rgba(0,0,0,0.1);
206
+ }
207
+ h1 {
208
+ font-size: 50px;
209
+ font-family: georgia;
210
+ font-style: italic;
211
+ padding-bottom: 10px;
148
212
  }
149
213
 
150
214
  </style>
151
215
  <script>
152
216
  document.addEventListener("DOMContentLoaded", async () => {
153
217
  const n = new N()
218
+ document.querySelector("#reveal").addEventListener("click", async (e) => {
219
+ let el = document.querySelector("#passcode")
220
+ if (el.type === "password") {
221
+ el.type = "text"
222
+ } else {
223
+ el.type = "password"
224
+ }
225
+ })
154
226
  document.querySelector("#save").addEventListener("click", async (e) => {
155
227
  // save
156
228
  const vals = {}
157
229
  const els = document.querySelectorAll(".env-item input[type=checkbox]")
158
230
  for(let el of els) {
159
- vals[el.id] = el.checked
231
+ let id = el.getAttribute("data-id")
232
+ vals[id] = el.checked
160
233
  }
234
+
235
+ const passcode_el = document.querySelector("#passcode")
236
+ vals.PINOKIO_SHARE_PASSCODE = passcode_el.value
237
+
161
238
  let res = await fetch("/env", {
162
239
  method: "POST",
163
240
  headers: {
@@ -165,7 +242,7 @@ document.addEventListener("DOMContentLoaded", async () => {
165
242
  },
166
243
  body: JSON.stringify({
167
244
  filepath: "api/" + location.pathname.slice(7),
168
- vals
245
+ vals,
169
246
  })
170
247
  }).then((res) => {
171
248
  return res.json()
@@ -185,23 +262,41 @@ document.addEventListener("DOMContentLoaded", async () => {
185
262
  <hr>
186
263
  <div class='env-item'>
187
264
  <% if (config.PINOKIO_SHARE_CLOUDFLARE.toString() === "true") { %>
188
- <input type="checkbox" id="PINOKIO_SHARE_CLOUDFLARE" name="cloudflare" checked />
265
+ <input type="checkbox" data-id="PINOKIO_SHARE_CLOUDFLARE" name="cloudflare" checked />
189
266
  <% } else { %>
190
- <input type="checkbox" id="PINOKIO_SHARE_CLOUDFLARE" name="cloudflare" />
267
+ <input type="checkbox" data-id="PINOKIO_SHARE_CLOUDFLARE" name="cloudflare" />
191
268
  <% } %>
192
269
  <label for="cloudflare"><i class="fa-brands fa-cloudflare"></i> Run a Public Node</label>
193
270
  <ul>
194
271
  <li>This will let you share your local Pinokio app to the public internet, which anyone with a URL can access.</li>
195
272
  <li>Powered by <a href="https://www.cloudflare.com/products/tunnel/" target="_blank" features="browser">Cloudflare Tunnel</a></li>
196
273
  <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
274
  </ul>
199
- <% cloudflare_links.forEach((link) => { %>
200
- <div class='shared'>
201
- <a href="<%=link.url%>" target="_blank" features="browser"><%=link.url%></a>
202
- <img src="<%=link.qr%>">
275
+ <div class='passcode'>
276
+ <h2>Passcode (optional)</h2>
277
+ <ol>
278
+ <li><strong>No passcode:</strong> Anyone with the URL can use the app</li>
279
+ <li><strong>Passcode:</strong> Users will need to authenticate with the passcode before using the app.</li>
280
+ <li>To allow multiple passcodes, separate the passode by commas (Example: ab12zd3,D3ad3a,df3d9Fa).</li>
281
+ </ol>
282
+ <div class='row'>
283
+ <input id='passcode' type='password' value="<%=config.PINOKIO_SHARE_PASSCODE || ''%>"/>
284
+ <button class='btn' id='reveal'><i class="fa-solid fa-eye"></i></button>
203
285
  </div>
204
- <% }) %>
286
+ </div>
287
+ <% if (cloudflare_links.length > 0) { %>
288
+ <div class='cloudflare-running'>
289
+ <div class='shared'>
290
+ <h2><i class="fa-solid fa-circle-notch fa-spin"></i> Running</h2>
291
+ </div>
292
+ <% cloudflare_links.forEach((link) => { %>
293
+ <div class='shared'>
294
+ <a href="<%=link.url%>" target="_blank" features="browser"><%=link.url%></a>
295
+ <img src="<%=link.qr%>">
296
+ </div>
297
+ <% }) %>
298
+ </div>
299
+ <% } %>
205
300
  </div>
206
301
  <div class='env-item'>
207
302
  <% if (config.PINOKIO_SHARE_LOCAL.toString() === "true") { %>