pinokiod 3.8.33 → 3.8.34

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/caddy.js CHANGED
@@ -9,8 +9,9 @@ class Caddy {
9
9
  let peers = this.kernel.peer.get()
10
10
  for(let ip of peers) {
11
11
  let account = this.kernel.peer.account(ip)
12
+ let namespaced_url = `pinokio.${account}.localhost`
12
13
  payload.apps.http.servers.main.routes.push({
13
- "match": [{ "host": [`pinokio.${account}.localhost`] }],
14
+ "match": [{ "host": [namespaced_url] }],
14
15
  "handle": [
15
16
  {
16
17
  "handler": "reverse_proxy",
@@ -33,6 +34,14 @@ class Caddy {
33
34
  return payload
34
35
  }
35
36
  home () {
37
+ let internal_ip = "127.0.0.1:42000"
38
+ let namespaced_url = "pinokio.localhost"
39
+
40
+ if (!this.mapping[internal_ip]) {
41
+ this.mapping[internal_ip] = new Set()
42
+ }
43
+ this.mapping[internal_ip].add(namespaced_url)
44
+
36
45
  return {
37
46
  "apps": {
38
47
  "http": {
@@ -61,11 +70,11 @@ class Caddy {
61
70
  ]
62
71
  },
63
72
  {
64
- "match": [{ "host": [`pinokio.localhost`] }],
73
+ "match": [{ "host": [namespaced_url] }],
65
74
  "handle": [
66
75
  {
67
76
  "handler": "reverse_proxy",
68
- "upstreams": [{ "dial": "127.0.0.1:42000" }]
77
+ "upstreams": [{ "dial": internal_ip }]
69
78
  },
70
79
  {
71
80
  "handler": "headers",
@@ -100,55 +109,98 @@ class Caddy {
100
109
  for(let key in local_variables) {
101
110
  let val = local_variables[key]
102
111
  if (val.startsWith("http")) {
103
- val = val.replace(/https?:\/\//, '')
104
112
 
113
+ // val :- https?:(some url)
114
+
115
+
116
+ let internal_ip = val.replace(/https?:\/\//, '')
105
117
 
106
- // replace 127,0.0.1 or localhost with the 192.168 address
107
- val = val.replace(/localhost|127\.0\.0\.1/g, ip)
108
118
  let api_path = path.resolve(this.kernel.peer.homedir(ip), "api")
109
119
  let rel_path = path.relative(api_path, script_path)
110
120
  let name = rel_path.split(path.sep)[0]
111
- console.log({ api_path, rel_path, name, val })
121
+ console.log({ api_path, rel_path, name, internal_ip })
112
122
 
123
+ let chunks = internal_ip.split(":")
124
+ if (chunks.length === 2) {
125
+ // 1. if the URL is a localhost url with port
126
+ let INTERNAL_HOST = chunks[0]
127
+ let INTERNAL_PORT = chunks[1]
113
128
 
114
- // create a proxy proxy
115
- // - get next available port and expose
116
- payload.apps.http.servers.main.routes.push({
117
- "match": [{
118
- "host": [":PORT_PLACEHOLDER_" + val],
119
- }],
120
- "handle": [
121
- {
122
- "handler": "reverse_proxy",
123
- "upstreams": [{ "dial": val }]
124
- },
125
- {
126
- "handler": "headers",
127
- "response": {
128
- "set": {
129
- "Access-Control-Allow-Origin": ["*"],
130
- "Access-Control-Allow-Methods": ["GET, POST, OPTIONS, PUT, DELETE"],
131
- "Access-Control-Allow-Headers": ["*"],
132
- "Vary": ["Origin"]
133
- }
134
- }
135
- }
136
- ]
137
- })
129
+ this.ip_mapping[INTERNAL_PORT] = ip
138
130
 
139
131
 
140
- // proxy proxy => proxy
141
- if (key === "url") {
142
- let url = `${name}.${account}.localhost`.toLowerCase()
143
- this.mapping[val] = url
132
+ // replace 127,0.0.1 or localhost with the 192.168 address
133
+ //val = val.replace(/localhost|127\.0\.0\.1/g, ip)
134
+
135
+
136
+ // create a proxy proxy
137
+ // - get next available port and expose
138
+ payload.apps.http.servers[":" + INTERNAL_PORT] = {
139
+ "listen": [":PORT_PLACEHOLDER_" + INTERNAL_PORT],
140
+ "routes": [{
141
+ "handle": [
142
+ {
143
+ "handler": "reverse_proxy",
144
+ "upstreams": [{ "dial": internal_ip }]
145
+ },
146
+ {
147
+ "handler": "headers",
148
+ "response": {
149
+ "set": {
150
+ "Access-Control-Allow-Origin": ["*"],
151
+ "Access-Control-Allow-Methods": ["GET, POST, OPTIONS, PUT, DELETE"],
152
+ "Access-Control-Allow-Headers": ["*"],
153
+ "Vary": ["Origin"]
154
+ }
155
+ }
156
+ }
157
+ ]
158
+ }]
159
+ }
160
+
161
+ // proxy proxy => proxy
162
+ if (key === "url") {
163
+ let namespaced_url = `${name}.${account}.localhost`.toLowerCase()
164
+ if (!this.mapping[internal_ip]) {
165
+ this.mapping[internal_ip] = new Set()
166
+ }
167
+ this.mapping[internal_ip].add(namespaced_url)
168
+ payload.apps.http.servers.main.routes.push({
169
+ "match": [{
170
+ "host": [namespaced_url]
171
+ }],
172
+ "handle": [
173
+ {
174
+ "handler": "reverse_proxy",
175
+ "upstreams": [{ "dial": ip + ":PORT_PLACEHOLDER_" + INTERNAL_PORT }]
176
+ },
177
+ {
178
+ "handler": "headers",
179
+ "response": {
180
+ "set": {
181
+ "Access-Control-Allow-Origin": ["*"],
182
+ "Access-Control-Allow-Methods": ["GET, POST, OPTIONS, PUT, DELETE"],
183
+ "Access-Control-Allow-Headers": ["*"],
184
+ "Vary": ["Origin"]
185
+ }
186
+ }
187
+ }
188
+ ]
189
+ })
190
+ }
191
+ let namespaced_url = `${name}.${account}.localhost`.toLowerCase()
192
+ if (!this.mapping[internal_ip]) {
193
+ this.mapping[internal_ip] = new Set()
194
+ }
195
+ this.mapping[internal_ip].add(namespaced_url)
144
196
  payload.apps.http.servers.main.routes.push({
145
197
  "match": [{
146
- "host": [url]
198
+ "host": [namespaced_url],
147
199
  }],
148
200
  "handle": [
149
201
  {
150
202
  "handler": "reverse_proxy",
151
- "upstreams": [{ "dial": ":PORT_PLACEHOLDER_" + val }]
203
+ "upstreams": [{ "dial": ip + ":PORT_PLACEHOLDER_" + INTERNAL_PORT }]
152
204
  },
153
205
  {
154
206
  "handler": "headers",
@@ -163,29 +215,12 @@ class Caddy {
163
215
  }
164
216
  ]
165
217
  })
218
+ } else {
219
+
220
+ // 2. if otherwise (anything, like https://google.com)
221
+ // DO NOT create a proxy
166
222
  }
167
- payload.apps.http.servers.main.routes.push({
168
- "match": [{
169
- "host": [`${key}.${name}${account}.localhost`.toLowerCase()],
170
- }],
171
- "handle": [
172
- {
173
- "handler": "reverse_proxy",
174
- "upstreams": [{ "dial": ":PORT_PLACEHOLDER_" + val }]
175
- },
176
- {
177
- "handler": "headers",
178
- "response": {
179
- "set": {
180
- "Access-Control-Allow-Origin": ["*"],
181
- "Access-Control-Allow-Methods": ["GET, POST, OPTIONS, PUT, DELETE"],
182
- "Access-Control-Allow-Headers": ["*"],
183
- "Vary": ["Origin"]
184
- }
185
- }
186
- }
187
- ]
188
- })
223
+
189
224
  }
190
225
  }
191
226
  }
@@ -203,16 +238,45 @@ class Caddy {
203
238
  let val = local_variables[key]
204
239
  if (val.startsWith("http")) {
205
240
  val = val.replace(/https?:\/\//, '')
241
+ let internal_ip = val
206
242
  let api_path = this.kernel.path("api")
207
243
  let rel_path = path.relative(api_path, script_path)
208
244
  let name = rel_path.split(path.sep)[0]
209
245
  console.log({ api_path, rel_path, name, val })
210
- if (key === "url") {
211
- let url = `${name}.localhost`.toLowerCase()
212
- this.mapping[val] = url
246
+ let chunks = internal_ip.split(":")
247
+ if (chunks.length === 2) {
248
+ if (key === "url") {
249
+ let url = `${name}.localhost`.toLowerCase()
250
+ if (!this.mapping[val]) {
251
+ this.mapping[val] = new Set()
252
+ }
253
+ this.mapping[val].add(url)
254
+ payload.apps.http.servers.main.routes.push({
255
+ "match": [{
256
+ "host": [url]
257
+ }],
258
+ "handle": [
259
+ {
260
+ "handler": "reverse_proxy",
261
+ "upstreams": [{ "dial": val }]
262
+ },
263
+ {
264
+ "handler": "headers",
265
+ "response": {
266
+ "set": {
267
+ "Access-Control-Allow-Origin": ["*"],
268
+ "Access-Control-Allow-Methods": ["GET, POST, OPTIONS, PUT, DELETE"],
269
+ "Access-Control-Allow-Headers": ["*"],
270
+ "Vary": ["Origin"]
271
+ }
272
+ }
273
+ }
274
+ ]
275
+ })
276
+ }
213
277
  payload.apps.http.servers.main.routes.push({
214
278
  "match": [{
215
- "host": [url]
279
+ "host": [`${key}.${name}.localhost`.toLowerCase()],
216
280
  }],
217
281
  "handle": [
218
282
  {
@@ -232,29 +296,10 @@ class Caddy {
232
296
  }
233
297
  ]
234
298
  })
299
+ } else {
300
+ // 2. if otherwise (anything, like https://google.com)
301
+ // do NOT create a proxy
235
302
  }
236
- payload.apps.http.servers.main.routes.push({
237
- "match": [{
238
- "host": [`${key}.${name}.localhost`.toLowerCase()],
239
- }],
240
- "handle": [
241
- {
242
- "handler": "reverse_proxy",
243
- "upstreams": [{ "dial": val }]
244
- },
245
- {
246
- "handler": "headers",
247
- "response": {
248
- "set": {
249
- "Access-Control-Allow-Origin": ["*"],
250
- "Access-Control-Allow-Methods": ["GET, POST, OPTIONS, PUT, DELETE"],
251
- "Access-Control-Allow-Headers": ["*"],
252
- "Vary": ["Origin"]
253
- }
254
- }
255
- }
256
- ]
257
- })
258
303
  }
259
304
  }
260
305
  }
@@ -272,29 +317,15 @@ class Caddy {
272
317
  let caddy_mapping = this.kernel.peer.caddy(ip)
273
318
  console.log({ caddy_mapping })
274
319
  for(let internal_ip in caddy_mapping) {
275
- let INTERNAL_PORT = internal_ip.split(":")[1]
276
- let https_domain = caddy_mapping[internal_ip]
277
- // let remote_app_ip = app_ip.replace(/localhost|127\.0\.0\.1/g, ip)
278
- // console.log({ https_domain, app_ip, ip })
279
- // https_domain <port>.localhost
280
- // let re = new RegExp(ip)
281
-
282
- // insert the $account value right before the last part (1234.localhost => 1234.x.localhost)
283
- let chunks = https_domain.split(".")
284
- let namespaced_url
285
- if (chunks.length === 3) {
286
- namespaced_url = https_domain
287
- } else if (chunks.length === 2) {
288
- chunks.splice(chunks.length - 1, 0, account)
289
- namespaced_url = chunks.join(".")
290
- }
291
- this.mapping[internal_ip] = namespaced_url
292
- console.log({ namespaced_url, https_domain, internal_ip })
293
-
294
-
295
320
  // create a proxy proxy
296
321
  // 1. create an exposed port listening on 0.0.0.0
297
322
  // 2. the exposed port points to the internal ip
323
+ let chunks = internal_ip.split(":")
324
+ let INTERNAL_HOST = chunks[0]
325
+ let INTERNAL_PORT = chunks[1]
326
+
327
+ this.ip_mapping[INTERNAL_PORT] = ip
328
+
298
329
  payload.apps.http.servers[":" + INTERNAL_PORT] = {
299
330
  "listen": [":PORT_PLACEHOLDER_" + INTERNAL_PORT],
300
331
  "routes": [{
@@ -317,30 +348,55 @@ class Caddy {
317
348
  ]
318
349
  }]
319
350
  }
351
+ let https_domains = caddy_mapping[internal_ip]
352
+ console.log(">>>>>>>> https_domains", https_domains)
320
353
 
321
- // 1. point proxy URL to the 127.0.0.1:EXPOSED_PORT
322
- payload.apps.http.servers.main.routes.push({
323
- "match": [{
324
- "host": [namespaced_url],
325
- }],
326
- "handle": [
327
- {
328
- "handler": "reverse_proxy",
329
- "upstreams": [{ "dial": ip + ":PORT_PLACEHOLDER_" + INTERNAL_PORT }]
330
- },
331
- {
332
- "handler": "headers",
333
- "response": {
334
- "set": {
335
- "Access-Control-Allow-Origin": ["*"],
336
- "Access-Control-Allow-Methods": ["GET, POST, OPTIONS, PUT, DELETE"],
337
- "Access-Control-Allow-Headers": ["*"],
338
- "Vary": ["Origin"]
354
+ // 1. if https_domains is made up of one item <port>.localhost,
355
+ // add <port>.<account>.localhost
356
+ // 2. if https_domains is made up of two items <port>.localhost and <port>.<account>.localhost, don't do anything
357
+ for(let https_domain of https_domains) {
358
+ // insert the $account value right before the last part (1234.localhost => 1234.x.localhost)
359
+ let chunks = https_domain.split(".")
360
+ let namespaced_url
361
+ if (chunks.length === 3) {
362
+ namespaced_url = https_domain
363
+ } else if (chunks.length === 2) {
364
+ chunks.splice(chunks.length - 1, 0, account)
365
+ namespaced_url = chunks.join(".")
366
+ }
367
+ console.log("##x", { chunks, namespaced_url, https_domain })
368
+ if (!this.mapping[internal_ip]) {
369
+ this.mapping[internal_ip] = new Set()
370
+ }
371
+ console.log("this.mapping", this.mapping)
372
+ console.log("internal_ip", internal_ip)
373
+ console.log("result", this.mapping[internal_ip])
374
+ this.mapping[internal_ip].add(namespaced_url)
375
+ console.log({ namespaced_url, https_domain, internal_ip })
376
+ // 1. point proxy URL to the 127.0.0.1:EXPOSED_PORT
377
+ payload.apps.http.servers.main.routes.push({
378
+ "match": [{
379
+ "host": [namespaced_url],
380
+ }],
381
+ "handle": [
382
+ {
383
+ "handler": "reverse_proxy",
384
+ "upstreams": [{ "dial": ip + ":PORT_PLACEHOLDER_" + INTERNAL_PORT }]
385
+ },
386
+ {
387
+ "handler": "headers",
388
+ "response": {
389
+ "set": {
390
+ "Access-Control-Allow-Origin": ["*"],
391
+ "Access-Control-Allow-Methods": ["GET, POST, OPTIONS, PUT, DELETE"],
392
+ "Access-Control-Allow-Headers": ["*"],
393
+ "Vary": ["Origin"]
394
+ }
339
395
  }
340
396
  }
341
- }
342
- ]
343
- })
397
+ ]
398
+ })
399
+ }
344
400
  }
345
401
  }
346
402
  return payload
@@ -353,7 +409,10 @@ class Caddy {
353
409
  }
354
410
  for(let item of list) {
355
411
  let url = `${item.port}.localhost`
356
- this.mapping[item.ip] = url
412
+ if (!this.mapping[item.ip]) {
413
+ this.mapping[item.ip] = new Set()
414
+ }
415
+ this.mapping[item.ip].add(url)
357
416
  payload.apps.http.servers.main.routes.push({
358
417
  "match": [{
359
418
  "host": [url],
@@ -386,7 +445,6 @@ class Caddy {
386
445
  }
387
446
  }
388
447
  async update() {
389
- this.mapping = {}
390
448
  let payload = await this.payload()
391
449
  console.log("Make request to caddy", JSON.stringify(payload, null, 2))
392
450
  let response = await axios.post('http://127.0.0.1:2019/load', payload, {
@@ -397,6 +455,9 @@ class Caddy {
397
455
  console.log("Caddy Response", { response })
398
456
  }
399
457
  async payload () {
458
+ this.mapping = {}
459
+ this.ip_mapping = {}
460
+ this.port_mapping = {} // 127.0.0.1 => 192.168,..
400
461
 
401
462
  // localhost home
402
463
  let payload = this.home()
@@ -422,9 +483,20 @@ class Caddy {
422
483
  if (count > 0) {
423
484
  let ports = await this.kernel.ports(count)
424
485
  console.log({ count, ports })
425
- payload = Util.fill_object(payload, /PORT_PLACEHOLDER_.+/, ports)
486
+ let response = Util.fill_object(payload, /PORT_PLACEHOLDER_.+/, ports)
487
+ payload = response.result
488
+ let replaced_map = response.replaced_map
489
+ for(let key in replaced_map) {
490
+ let original_port = key.replace("PORT_PLACEHOLDER_", "")
491
+ this.port_mapping[original_port] = replaced_map[key]
492
+ }
426
493
  }
427
494
 
495
+ for(let key in this.mapping) {
496
+ this.mapping[key] = Array.from(this.mapping[key])
497
+ }
498
+
499
+
428
500
  return payload
429
501
  }
430
502
  }
package/kernel/peer.js CHANGED
@@ -14,7 +14,7 @@ class PeerDiscovery {
14
14
  account(ip) {
15
15
  return this.data[ip].account.username
16
16
  }
17
- home(ip) {
17
+ homedir(ip) {
18
18
  return this.data[ip].homedir
19
19
  }
20
20
  caddy(ip) {
@@ -36,7 +36,6 @@ class PeerDiscovery {
36
36
  this.peers.add(ip);
37
37
  let interval = setInterval(() => {
38
38
  axios.get(`http://${ip}:42000/pinokio/mem`).then((res) => {
39
- console.log("axios", res.data)
40
39
  this.data[ip] = res.data
41
40
  if (res.data.caddy) {
42
41
  console.log(`Discovered peer: ${ip}`);
package/kernel/util.js CHANGED
@@ -308,7 +308,9 @@ const update_env = async (filepath, changes) => {
308
308
  await fs.promises.writeFile(filepath, newval)
309
309
  };
310
310
  function fill_object(obj, pattern, list) {
311
+ console.log("FILL_OBJECT")
311
312
  const map = {};
313
+ const replaced_map = {}
312
314
  let index = 0;
313
315
 
314
316
  function recurse(value) {
@@ -317,6 +319,7 @@ function fill_object(obj, pattern, list) {
317
319
  if (!(match in map)) {
318
320
  if (index >= list.length) throw new Error("Not enough items provided");
319
321
  map[match] = list[index++];
322
+ replaced_map[match] = map[match]
320
323
  }
321
324
  return map[match];
322
325
  });
@@ -332,7 +335,8 @@ function fill_object(obj, pattern, list) {
332
335
  return value;
333
336
  }
334
337
 
335
- return recurse(obj);
338
+ let res = recurse(obj);
339
+ return { result: res, replaced_map }
336
340
  }
337
341
 
338
342
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "3.8.33",
3
+ "version": "3.8.34",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -2659,14 +2659,15 @@ class Server {
2659
2659
  })
2660
2660
  }))
2661
2661
  this.app.get("/proxy", ex(async (req, res) => {
2662
+ await this.kernel.caddy.update()
2662
2663
  let list = await this.kernel.processes.refresh()
2663
2664
  list = list.map((item) => {
2664
2665
  return {
2665
- proxy: this.kernel.caddy.mapping[item.ip],
2666
+ proxy: this.kernel.caddy.mapping[item.ip] || [],
2666
2667
  ...item
2667
2668
  }
2668
2669
  })
2669
- await this.kernel.caddy.update()
2670
+ console.log("list", list)
2670
2671
 
2671
2672
  let peers = this.kernel.peer.get()
2672
2673
 
@@ -2728,27 +2729,23 @@ class Server {
2728
2729
  let pinokio = (await this.kernel.loader.load(p)).resolved
2729
2730
  let p2 = path.resolve(apipath, folder, "pinokio_meta.json")
2730
2731
  let pinokio2 = (await this.kernel.loader.load(p2)).resolved
2731
- let cc;
2732
2732
  if (pinokio) {
2733
- cc = {
2734
- name: pinokio.title,
2735
- description: pinokio.description,
2736
- link: `/pinokio/browser/${folder}/browse#n1`,
2737
- icon: pinokio.icon ? `/api/${folder}/${pinokio.icon}?raw=true` : null
2738
- }
2739
- }
2740
- if (pinokio2) {
2741
- if (pinokio2.title) cc.title = pinokio2.title
2742
- if (pinokio2.description) cc.description = pinokio2.description
2743
- if (pinokio2.icon) cc.icon = pinokio2.icon
2744
- }
2745
- if (cc) {
2733
+ let cc = Object.assign({} , pinokio , pinokio2);
2734
+ console.log({ pinokio, pinokio2, cc })
2735
+ cc.link = `/pinokio/browser/${folder}/browse#n1`,
2736
+ cc.icon = cc.icon ? `/api/${folder}/${cc.icon}?raw=true` : null
2737
+ cc.name = cc.title
2738
+ console.log("after", { pinokio, pinokio2, cc })
2746
2739
  apps.push(cc)
2747
2740
  }
2748
2741
  }
2742
+
2743
+ console.log({ port_mapping: this.kernel.caddy.port_mapping, ip_mapping: this.kernel.caddy.ip_mapping })
2749
2744
  res.render("proxy", {
2750
2745
  peers,
2751
2746
  list,
2747
+ port_mapping: this.kernel.caddy.port_mapping,
2748
+ ip_mapping: this.kernel.caddy.ip_mapping,
2752
2749
  agent: this.agent,
2753
2750
  theme: this.theme,
2754
2751
  items: proxies,
@@ -54,6 +54,12 @@
54
54
  header .runner {
55
55
  padding: 10px 0 0;
56
56
  }
57
+ .proc-table {
58
+ display: flex;
59
+ }
60
+ .proc-table .title {
61
+ flex-grow: 1;
62
+ }
57
63
  .proxy {
58
64
  padding: 0 10px;
59
65
  text-align: right;
@@ -147,21 +153,27 @@ body.dark .browser-options-row {
147
153
  margin-top: 10px;
148
154
  }
149
155
  .header-label {
156
+ padding: 0;
150
157
  font-size: 50px;
151
158
  letter-spacing: -1px;
152
- padding: 50px 0 0;
153
159
  text-align: center;
154
160
  text-transform: capitalize;
155
161
  }
156
162
  .header-label-sub {
157
163
  font-size: 25px;
158
164
  letter-spacing: -1px;
159
- padding: 15px;
165
+ padding: 0 10px 10px;
160
166
  font-weight: bold;
161
167
  }
168
+ .header-container {
169
+ margin: 10px;
170
+ background: rgba(0, 0, 100, 0.04);
171
+ padding: 20px;
172
+ box-sizing: border-box;
173
+ }
162
174
  .container {
163
175
  display: grid;
164
- grid-template-columns: repeat(4, 1fr);
176
+ grid-template-columns: repeat(2, 1fr);
165
177
  }
166
178
  .dark .container-row {
167
179
  background: rgba(255, 255, 255, 0.04);
@@ -176,6 +188,25 @@ body.dark .browser-options-row {
176
188
  text-align:center;
177
189
  padding: 10px;
178
190
  }
191
+ .container-row table {
192
+ width: 100%;
193
+ }
194
+ table a {
195
+ text-decoration: none;
196
+ color: royalblue;
197
+ }
198
+ .container-row th {
199
+ text-align: left;
200
+ font-size: 18px;
201
+ padding: 5px;
202
+ text-transform: uppercase;
203
+ }
204
+ .container-row td.title {
205
+ }
206
+ .container-row td {
207
+ font-size: 14px;
208
+ padding: 5px;
209
+ }
179
210
  @media only screen and (max-width: 480px) {
180
211
  .btn2 {
181
212
  padding: 5px;
@@ -207,37 +238,48 @@ body.dark .browser-options-row {
207
238
  </h1>
208
239
  </header>
209
240
  <main>
210
- <div class='header-label'><i class='fa-solid fa-wifi'></i> Local Sharing</div>
241
+ <div class='header-label'><i class='fa-solid fa-wifi'></i> Networking</div>
211
242
  <div class='desc'>Serve Pinokio, installed apps, and even external apps to any device on the same network.</div>
243
+ <div class='header-container'>
244
+ <div class='header-label-sub'><i class="fa-solid fa-podcast"></i> Local Network</div>
245
+ <% peers.forEach((item) => { %>
246
+ <a target="_blank" href="http://<%=item%>" class='line align-top'>
247
+ <h3 class='table-grid'><%=item%></h3>
248
+ </a>
249
+ <% }) %>
250
+ </div>
212
251
  <div class='container'>
213
252
  <div class='container-row'>
214
- <div class='header-label-sub'><i class="fa-solid fa-microchip"></i> Peers</div>
215
- <% peers.forEach((item) => { %>
216
- <a target="_blank" href="http://<%=item%>" class='line align-top'>
217
- <h3 class='table-grid'><%=item%></h3>
218
- </a>
219
- <% }) %>
220
- </div>
221
- <div class='container-row'>
222
- <div class='header-label-sub'><i class="fa-solid fa-microchip"></i> Apps</div>
223
- <div class='line align-top'>
224
- <h3 class='table-grid'>
225
- <div class='col'>Name</div>
226
- <div class='col'>Pid</div>
227
- <div class='col'>Port</div>
228
- </h3>
229
- </div>
230
- <% list.forEach((item) => { %>
231
- <a target="_blank" href="https://<%=item.proxy%>" class='line align-top'>
232
- <h3 class='table-grid'>
233
- <div class='col'><%=item.name%></div>
234
- <div class='col'><%=item.pid%></div>
235
- <div class='col'><%=item.port%></div>
236
- </h3>
237
- </a>
238
- <% }) %>
253
+ <div class='header-label-sub'><i class="fa-solid fa-microchip"></i> System</div>
254
+ <table>
255
+ <tr>
256
+ <th>Server</th>
257
+ <th>https</th>
258
+ <th>http</th>
259
+ </tr>
260
+ <% list.forEach((item) => { %>
261
+ <tr>
262
+ <td class='title'><%=item.name%></td>
263
+ <td>
264
+ <% item.proxy.forEach((ip) => { %>
265
+ <div>
266
+ <a target="_blank" href="https://<%=ip%>">https://<%=ip%></a>
267
+ </div>
268
+ <% }) %>
269
+ </td>
270
+ <td>
271
+ <div>
272
+ <a target="_blank" href="http://localhost:<%=item.port%>">http://localhost:<%=item.port%></a>
273
+ </div>
274
+ <div>
275
+ <a target="_blank" href="http://<%=ip_mapping[item.port]%>:<%=port_mapping[item.port]%>">http://<%=ip_mapping[item.port]%>:<%=port_mapping[item.port]%></a>
276
+ </div>
277
+ </td>
278
+ </tr>
279
+ <% }) %>
280
+ </table>
239
281
  </div>
240
- <div class='container-row'>
282
+ <div class='container-row hidden'>
241
283
  <div class='header-label-sub'><i class="fa-solid fa-microchip"></i> System Programs</div>
242
284
  <div class='line align-top'>
243
285
  <h3>
@@ -286,7 +328,7 @@ body.dark .browser-options-row {
286
328
  <% }) %>
287
329
  </div>
288
330
  <div class='container-row'>
289
- <div class='header-label-sub'><i class="fa-solid fa-dice-d6"></i> Application Programs</div>
331
+ <div class='header-label-sub'><i class="fa-solid fa-dice-d6"></i> Pinokio</div>
290
332
  <% apps.forEach((item) => { %>
291
333
  <a class='line align-top' href="<%=item.link%>">
292
334
  <h3>