pinokiod 3.170.0 → 3.181.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/kernel/favicon.js +91 -34
- package/kernel/peer.js +73 -0
- package/kernel/util.js +13 -2
- package/package.json +1 -1
- package/server/index.js +249 -26
- package/server/public/common.js +244 -0
- package/server/public/files-app/app.css +73 -1
- package/server/public/files-app/app.js +255 -2
- package/server/public/layout.js +115 -1
- package/server/public/nav.js +227 -64
- package/server/public/style.css +27 -3
- package/server/public/tab-idle-notifier.js +3 -0
- package/server/routes/files.js +96 -0
- package/server/socket.js +71 -4
- package/server/views/app.ejs +603 -53
- package/server/views/connect.ejs +9 -0
- package/server/views/file_browser.ejs +9 -2
- package/server/views/index.ejs +11 -2
- package/server/views/init/index.ejs +9 -2
- package/server/views/layout.ejs +7 -5
- package/server/views/net.ejs +9 -0
- package/server/views/network.ejs +9 -0
- package/server/views/review.ejs +4 -3
- package/server/views/screenshots.ejs +9 -0
- package/server/views/settings.ejs +9 -0
- package/server/views/terminals.ejs +12 -3
- package/server/views/tools.ejs +10 -1
package/server/index.js
CHANGED
|
@@ -230,8 +230,40 @@ class Server {
|
|
|
230
230
|
}
|
|
231
231
|
running_dynamic.push(obj)
|
|
232
232
|
} else {
|
|
233
|
+
const normalizedFilepath = path.normalize(filepath)
|
|
234
|
+
const hasMenuCwd = typeof cwd === 'string'
|
|
235
|
+
const normalizedMenuCwd = hasMenuCwd ? (cwd.length === 0 ? '' : path.normalize(cwd)) : null
|
|
236
|
+
const matchesRunningEntry = (runningKey) => {
|
|
237
|
+
if (typeof runningKey !== 'string' || runningKey.length === 0) {
|
|
238
|
+
return false
|
|
239
|
+
}
|
|
240
|
+
const questionIndex = runningKey.indexOf('?')
|
|
241
|
+
const runningPath = questionIndex >= 0 ? runningKey.slice(0, questionIndex) : runningKey
|
|
242
|
+
if (path.normalize(runningPath) !== normalizedFilepath) {
|
|
243
|
+
return false
|
|
244
|
+
}
|
|
245
|
+
if (!hasMenuCwd) {
|
|
246
|
+
return questionIndex === -1
|
|
247
|
+
}
|
|
248
|
+
if (questionIndex === -1) {
|
|
249
|
+
return normalizedMenuCwd === ''
|
|
250
|
+
}
|
|
251
|
+
const params = querystring.parse(runningKey.slice(questionIndex + 1))
|
|
252
|
+
const rawCwd = typeof params.cwd === 'string' ? params.cwd : null
|
|
253
|
+
if (normalizedMenuCwd === '') {
|
|
254
|
+
return rawCwd !== null && rawCwd.length === 0
|
|
255
|
+
}
|
|
256
|
+
if (!rawCwd || rawCwd.length === 0) {
|
|
257
|
+
return false
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
return path.normalize(rawCwd) === normalizedMenuCwd
|
|
261
|
+
} catch (_) {
|
|
262
|
+
return false
|
|
263
|
+
}
|
|
264
|
+
}
|
|
233
265
|
for(let running_id in this.kernel.api.running) {
|
|
234
|
-
if (running_id
|
|
266
|
+
if (matchesRunningEntry(running_id)) {
|
|
235
267
|
let obj2 = structuredClone(obj)
|
|
236
268
|
obj2.running = true
|
|
237
269
|
obj2.display = "indent"
|
|
@@ -526,9 +558,13 @@ class Server {
|
|
|
526
558
|
}
|
|
527
559
|
|
|
528
560
|
let currentBranch = null
|
|
561
|
+
let isDetached = false
|
|
529
562
|
try {
|
|
530
563
|
currentBranch = await git.currentBranch({ fs, dir, fullname: false })
|
|
531
564
|
} catch (_) {}
|
|
565
|
+
if (!currentBranch) {
|
|
566
|
+
isDetached = true
|
|
567
|
+
}
|
|
532
568
|
|
|
533
569
|
let branches = []
|
|
534
570
|
if (branchList.length > 0) {
|
|
@@ -595,6 +631,7 @@ class Server {
|
|
|
595
631
|
branch: currentBranch,
|
|
596
632
|
branches,
|
|
597
633
|
dir,
|
|
634
|
+
detached: isDetached,
|
|
598
635
|
logError: logError ? String(logError.message || logError) : null
|
|
599
636
|
}
|
|
600
637
|
}
|
|
@@ -812,6 +849,7 @@ class Server {
|
|
|
812
849
|
let run_tab = "/p/" + name
|
|
813
850
|
let dev_tab = "/p/" + name + "/dev"
|
|
814
851
|
let review_tab = "/p/" + name + "/review"
|
|
852
|
+
let files_tab = "/p/" + name + "/files"
|
|
815
853
|
|
|
816
854
|
let editor_tab = `/pinokio/fileview/${encodeURIComponent(name)}`
|
|
817
855
|
let savedTabs = []
|
|
@@ -867,6 +905,7 @@ class Server {
|
|
|
867
905
|
run_tab,
|
|
868
906
|
dev_tab,
|
|
869
907
|
review_tab,
|
|
908
|
+
files_tab,
|
|
870
909
|
// paths,
|
|
871
910
|
theme: this.theme,
|
|
872
911
|
agent: req.agent,
|
|
@@ -1470,10 +1509,15 @@ class Server {
|
|
|
1470
1509
|
drive: path.resolve(this.kernel.homedir, "drive"),
|
|
1471
1510
|
}
|
|
1472
1511
|
}
|
|
1512
|
+
const peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
1513
|
+
let peer_qr = null
|
|
1514
|
+
try { peer_qr = await QRCode.toDataURL(peer_url) } catch (_) {}
|
|
1473
1515
|
let list = this.getPeers()
|
|
1474
1516
|
res.render("settings", {
|
|
1475
1517
|
list,
|
|
1476
1518
|
current_host: this.kernel.peer.host,
|
|
1519
|
+
peer_url,
|
|
1520
|
+
peer_qr,
|
|
1477
1521
|
platform,
|
|
1478
1522
|
version: this.version,
|
|
1479
1523
|
logo: this.logo,
|
|
@@ -2195,6 +2239,10 @@ class Server {
|
|
|
2195
2239
|
qr_cloudflare = await QRCode.toDataURL(this.cloudflare_pub)
|
|
2196
2240
|
}
|
|
2197
2241
|
|
|
2242
|
+
const peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
2243
|
+
let peer_qr = null
|
|
2244
|
+
try { peer_qr = await QRCode.toDataURL(peer_url) } catch (_) {}
|
|
2245
|
+
|
|
2198
2246
|
// custom theme
|
|
2199
2247
|
let exists = await fse.pathExists(this.kernel.path("web"))
|
|
2200
2248
|
if (exists) {
|
|
@@ -2224,6 +2272,8 @@ class Server {
|
|
|
2224
2272
|
res.render("index", {
|
|
2225
2273
|
list,
|
|
2226
2274
|
current_host: this.kernel.peer.host,
|
|
2275
|
+
peer_url,
|
|
2276
|
+
peer_qr,
|
|
2227
2277
|
current_urls,
|
|
2228
2278
|
portal: this.portal,
|
|
2229
2279
|
install: this.install,
|
|
@@ -3885,6 +3935,9 @@ class Server {
|
|
|
3885
3935
|
}))
|
|
3886
3936
|
*/
|
|
3887
3937
|
this.app.get("/tools", ex(async (req, res) => {
|
|
3938
|
+
const peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
3939
|
+
let peer_qr = null
|
|
3940
|
+
try { peer_qr = await QRCode.toDataURL(peer_url) } catch (_) {}
|
|
3888
3941
|
let list = this.getPeers()
|
|
3889
3942
|
let installs = []
|
|
3890
3943
|
for(let key in this.kernel.bin.installed) {
|
|
@@ -3916,6 +3969,8 @@ class Server {
|
|
|
3916
3969
|
}
|
|
3917
3970
|
res.render("tools", {
|
|
3918
3971
|
current_host: this.kernel.peer.host,
|
|
3972
|
+
peer_url,
|
|
3973
|
+
peer_qr,
|
|
3919
3974
|
pending,
|
|
3920
3975
|
installs,
|
|
3921
3976
|
bundles,
|
|
@@ -4000,9 +4055,14 @@ class Server {
|
|
|
4000
4055
|
return (a.name || '').localeCompare(b.name || '')
|
|
4001
4056
|
})
|
|
4002
4057
|
|
|
4058
|
+
const peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
4059
|
+
let peer_qr = null
|
|
4060
|
+
try { peer_qr = await QRCode.toDataURL(peer_url) } catch (_) {}
|
|
4003
4061
|
const list = this.getPeers()
|
|
4004
4062
|
res.render("terminals", {
|
|
4005
4063
|
current_host: this.kernel.peer.host,
|
|
4064
|
+
peer_url,
|
|
4065
|
+
peer_qr,
|
|
4006
4066
|
pluginMenu,
|
|
4007
4067
|
apps,
|
|
4008
4068
|
portal: this.portal,
|
|
@@ -4016,9 +4076,14 @@ class Server {
|
|
|
4016
4076
|
res.redirect(301, "/terminals")
|
|
4017
4077
|
})
|
|
4018
4078
|
this.app.get("/screenshots", ex(async (req, res) => {
|
|
4079
|
+
const peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
4080
|
+
let peer_qr = null
|
|
4081
|
+
try { peer_qr = await QRCode.toDataURL(peer_url) } catch (_) {}
|
|
4019
4082
|
let list = this.getPeers()
|
|
4020
4083
|
res.render("screenshots", {
|
|
4021
4084
|
current_host: this.kernel.peer.host,
|
|
4085
|
+
peer_url,
|
|
4086
|
+
peer_qr,
|
|
4022
4087
|
version: this.version,
|
|
4023
4088
|
portal: this.portal,
|
|
4024
4089
|
logo: this.logo,
|
|
@@ -4105,6 +4170,7 @@ class Server {
|
|
|
4105
4170
|
// if <app_name>.localhost
|
|
4106
4171
|
// otherwise => redirect
|
|
4107
4172
|
|
|
4173
|
+
console.log("Chunks", chunks)
|
|
4108
4174
|
|
|
4109
4175
|
if (chunks.length >= 2) {
|
|
4110
4176
|
|
|
@@ -4151,29 +4217,31 @@ class Server {
|
|
|
4151
4217
|
} else {
|
|
4152
4218
|
nameChunks = chunks
|
|
4153
4219
|
}
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4220
|
+
if (nameChunks) {
|
|
4221
|
+
let name = nameChunks.join(".")
|
|
4222
|
+
let api_path = this.kernel.path("api", name)
|
|
4223
|
+
let exists = await this.exists(api_path)
|
|
4224
|
+
if (exists) {
|
|
4225
|
+
let meta = await this.kernel.api.meta(name)
|
|
4226
|
+
let launcher = await this.kernel.api.launcher(name)
|
|
4227
|
+
let pinokio = launcher.script
|
|
4228
|
+
let launchable = false
|
|
4229
|
+
if (pinokio && pinokio.menu && pinokio.menu.length > 0) {
|
|
4230
|
+
launchable = true
|
|
4231
|
+
}
|
|
4232
|
+
res.render("start", {
|
|
4233
|
+
url,
|
|
4234
|
+
launchable,
|
|
4235
|
+
autolaunch,
|
|
4236
|
+
logo: this.logo,
|
|
4237
|
+
theme: this.theme,
|
|
4238
|
+
agent: req.agent,
|
|
4239
|
+
name: meta.title,
|
|
4240
|
+
image: meta.icon,
|
|
4241
|
+
link: `/p/${name}?autolaunch=${autolaunch ? "1" : "0"}`,
|
|
4242
|
+
})
|
|
4243
|
+
return
|
|
4244
|
+
}
|
|
4177
4245
|
}
|
|
4178
4246
|
}
|
|
4179
4247
|
res.render("start", {
|
|
@@ -4286,9 +4354,14 @@ class Server {
|
|
|
4286
4354
|
drive: path.resolve(this.kernel.homedir, "drive"),
|
|
4287
4355
|
}
|
|
4288
4356
|
}
|
|
4357
|
+
const peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
4358
|
+
let peer_qr = null
|
|
4359
|
+
try { peer_qr = await QRCode.toDataURL(peer_url) } catch (_) {}
|
|
4289
4360
|
let list = this.getPeers()
|
|
4290
4361
|
res.render("settings", {
|
|
4291
4362
|
current_host: this.kernel.peer.host,
|
|
4363
|
+
peer_url,
|
|
4364
|
+
peer_qr,
|
|
4292
4365
|
list,
|
|
4293
4366
|
platform,
|
|
4294
4367
|
version: this.version,
|
|
@@ -4404,6 +4477,9 @@ class Server {
|
|
|
4404
4477
|
return
|
|
4405
4478
|
}
|
|
4406
4479
|
|
|
4480
|
+
const peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
4481
|
+
let peer_qr = null
|
|
4482
|
+
try { peer_qr = await QRCode.toDataURL(peer_url) } catch (_) {}
|
|
4407
4483
|
let list = this.getPeers()
|
|
4408
4484
|
let ai = await this.kernel.proto.ai()
|
|
4409
4485
|
ai = [{
|
|
@@ -4417,6 +4493,8 @@ class Server {
|
|
|
4417
4493
|
list,
|
|
4418
4494
|
ai,
|
|
4419
4495
|
current_host: this.kernel.peer.host,
|
|
4496
|
+
peer_url,
|
|
4497
|
+
peer_qr,
|
|
4420
4498
|
cwd: this.kernel.path("api"),
|
|
4421
4499
|
name: null,
|
|
4422
4500
|
// name: req.params.name,
|
|
@@ -4517,9 +4595,14 @@ class Server {
|
|
|
4517
4595
|
} catch (e) {
|
|
4518
4596
|
}
|
|
4519
4597
|
}
|
|
4598
|
+
const peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
4599
|
+
let peer_qr = null
|
|
4600
|
+
try { peer_qr = await QRCode.toDataURL(peer_url) } catch (_) {}
|
|
4520
4601
|
res.render(`connect`, {
|
|
4521
4602
|
current_urls,
|
|
4522
4603
|
current_host: this.kernel.peer.host,
|
|
4604
|
+
peer_url,
|
|
4605
|
+
peer_qr,
|
|
4523
4606
|
list,
|
|
4524
4607
|
portal: this.portal,
|
|
4525
4608
|
logo: this.logo,
|
|
@@ -4661,6 +4744,13 @@ class Server {
|
|
|
4661
4744
|
this.app.post("/push", ex(async (req, res) => {
|
|
4662
4745
|
try {
|
|
4663
4746
|
const payload = { ...(req.body || {}) }
|
|
4747
|
+
// Normalise audience and device targeting
|
|
4748
|
+
if (typeof payload.audience === 'string') {
|
|
4749
|
+
payload.audience = payload.audience.trim() || undefined
|
|
4750
|
+
}
|
|
4751
|
+
if (typeof payload.device_id === 'string') {
|
|
4752
|
+
payload.device_id = payload.device_id.trim() || undefined
|
|
4753
|
+
}
|
|
4664
4754
|
const resolveAssetPath = (raw) => {
|
|
4665
4755
|
if (typeof raw !== 'string') {
|
|
4666
4756
|
return null
|
|
@@ -4743,6 +4833,19 @@ class Server {
|
|
|
4743
4833
|
}
|
|
4744
4834
|
delete payload.soundUrl
|
|
4745
4835
|
delete payload.soundPath
|
|
4836
|
+
// For device-scoped notifications, suppress host OS notifier for remote origins,
|
|
4837
|
+
// but allow it when the request originates from the local machine
|
|
4838
|
+
if (payload.audience === 'device' && typeof payload.device_id === 'string' && payload.device_id) {
|
|
4839
|
+
try {
|
|
4840
|
+
if (this.socket && typeof this.socket.isLocalDevice === 'function') {
|
|
4841
|
+
payload.host = !!this.socket.isLocalDevice(payload.device_id)
|
|
4842
|
+
} else {
|
|
4843
|
+
payload.host = false
|
|
4844
|
+
}
|
|
4845
|
+
} catch (_) {
|
|
4846
|
+
payload.host = false
|
|
4847
|
+
}
|
|
4848
|
+
}
|
|
4746
4849
|
Util.push(payload)
|
|
4747
4850
|
res.json({ success: true })
|
|
4748
4851
|
} catch (e) {
|
|
@@ -5304,6 +5407,9 @@ class Server {
|
|
|
5304
5407
|
let static_routes = Object.keys(this.kernel.router.rewrite_mapping).map((key) => {
|
|
5305
5408
|
return this.kernel.router.rewrite_mapping[key]
|
|
5306
5409
|
})
|
|
5410
|
+
const peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
5411
|
+
let peer_qr = null
|
|
5412
|
+
try { peer_qr = await QRCode.toDataURL(peer_url) } catch (_) {}
|
|
5307
5413
|
res.render("net", {
|
|
5308
5414
|
static_routes,
|
|
5309
5415
|
selected_name: req.params.name,
|
|
@@ -5322,6 +5428,8 @@ class Server {
|
|
|
5322
5428
|
peer,
|
|
5323
5429
|
protocol,
|
|
5324
5430
|
current_host: this.kernel.peer.host,
|
|
5431
|
+
peer_url,
|
|
5432
|
+
peer_qr,
|
|
5325
5433
|
})
|
|
5326
5434
|
}))
|
|
5327
5435
|
this.app.get("/network", ex(async (req, res) => {
|
|
@@ -5463,6 +5571,9 @@ class Server {
|
|
|
5463
5571
|
let static_routes = Object.keys(this.kernel.router.rewrite_mapping).map((key) => {
|
|
5464
5572
|
return this.kernel.router.rewrite_mapping[key]
|
|
5465
5573
|
})
|
|
5574
|
+
const peer_url = `http://${this.kernel.peer.host}:${DEFAULT_PORT}`
|
|
5575
|
+
let peer_qr = null
|
|
5576
|
+
try { peer_qr = await QRCode.toDataURL(peer_url) } catch (_) {}
|
|
5466
5577
|
res.render("network", {
|
|
5467
5578
|
static_routes,
|
|
5468
5579
|
host,
|
|
@@ -5488,6 +5599,8 @@ class Server {
|
|
|
5488
5599
|
peer_active: this.kernel.peer.active,
|
|
5489
5600
|
port_mapping: this.kernel.router.port_mapping,
|
|
5490
5601
|
// port_mapping: this.kernel.caddy.port_mapping,
|
|
5602
|
+
peer_url,
|
|
5603
|
+
peer_qr,
|
|
5491
5604
|
// ip_mapping: this.kernel.caddy.ip_mapping,
|
|
5492
5605
|
lan: this.kernel.router.local_network_mapping,
|
|
5493
5606
|
agent: req.agent,
|
|
@@ -6246,8 +6359,84 @@ class Server {
|
|
|
6246
6359
|
res.json(response)
|
|
6247
6360
|
}))
|
|
6248
6361
|
this.app.get("/info/git/:ref/*", ex(async (req, res) => {
|
|
6249
|
-
|
|
6250
|
-
|
|
6362
|
+
const repoParam = req.params[0]
|
|
6363
|
+
const ref = req.params.ref || 'HEAD'
|
|
6364
|
+
const summary = await this.getGit(ref, repoParam)
|
|
6365
|
+
|
|
6366
|
+
const repoDir = summary && summary.dir ? summary.dir : this.kernel.path('api', repoParam)
|
|
6367
|
+
|
|
6368
|
+
if (ref === 'HEAD') {
|
|
6369
|
+
try {
|
|
6370
|
+
const { changes: headChanges, git_commit_url } = await this.getRepoHeadStatus(repoParam)
|
|
6371
|
+
summary.changes = headChanges || []
|
|
6372
|
+
summary.git_commit_url = git_commit_url || null
|
|
6373
|
+
} catch (error) {
|
|
6374
|
+
console.error('[git-info] head status error', repoParam, error)
|
|
6375
|
+
summary.changes = []
|
|
6376
|
+
}
|
|
6377
|
+
} else {
|
|
6378
|
+
let changes = []
|
|
6379
|
+
try {
|
|
6380
|
+
const commitOid = await this.kernel.git.resolveCommitOid(repoDir, ref)
|
|
6381
|
+
const parentOid = await this.kernel.git.getParentCommit(repoDir, commitOid)
|
|
6382
|
+
let entries
|
|
6383
|
+
if (parentOid !== commitOid) {
|
|
6384
|
+
entries = await git.walk({
|
|
6385
|
+
fs,
|
|
6386
|
+
dir: repoDir,
|
|
6387
|
+
trees: [git.TREE({ ref: parentOid }), git.TREE({ ref: commitOid })],
|
|
6388
|
+
map: async (filepath, [A, B]) => {
|
|
6389
|
+
if (filepath === '.') return
|
|
6390
|
+
if (!A && B) return { filepath, type: 'added' }
|
|
6391
|
+
if (A && !B) return { filepath, type: 'deleted' }
|
|
6392
|
+
if (A && B) {
|
|
6393
|
+
const Aoid = await A.oid()
|
|
6394
|
+
const Boid = await B.oid()
|
|
6395
|
+
if (Aoid !== Boid) return { filepath, type: 'modified' }
|
|
6396
|
+
}
|
|
6397
|
+
},
|
|
6398
|
+
})
|
|
6399
|
+
} else {
|
|
6400
|
+
entries = await git.walk({
|
|
6401
|
+
fs,
|
|
6402
|
+
dir: repoDir,
|
|
6403
|
+
trees: [git.TREE({ ref: commitOid })],
|
|
6404
|
+
map: async (filepath, [B]) => {
|
|
6405
|
+
if (filepath === '.') return
|
|
6406
|
+
return { filepath, type: 'added' }
|
|
6407
|
+
},
|
|
6408
|
+
})
|
|
6409
|
+
}
|
|
6410
|
+
const diffFiles = (entries || []).filter(Boolean)
|
|
6411
|
+
for (const { filepath, type } of diffFiles) {
|
|
6412
|
+
const fullPath = path.join(repoDir, filepath)
|
|
6413
|
+
const stats = await fs.promises.stat(fullPath).catch(() => null)
|
|
6414
|
+
if (!stats || stats.isDirectory()) {
|
|
6415
|
+
continue
|
|
6416
|
+
}
|
|
6417
|
+
const relpath = path.relative(this.kernel.path('api'), fullPath)
|
|
6418
|
+
changes.push({
|
|
6419
|
+
ref,
|
|
6420
|
+
webpath: "/asset/" + path.relative(this.kernel.homedir, fullPath),
|
|
6421
|
+
file: filepath,
|
|
6422
|
+
path: fullPath,
|
|
6423
|
+
diffpath: `/gitdiff/${ref}/${repoParam}/${filepath}`,
|
|
6424
|
+
status: type,
|
|
6425
|
+
relpath,
|
|
6426
|
+
})
|
|
6427
|
+
}
|
|
6428
|
+
} catch (error) {
|
|
6429
|
+
console.error('[git-info] diff error', repoParam, ref, error)
|
|
6430
|
+
}
|
|
6431
|
+
summary.changes = changes
|
|
6432
|
+
}
|
|
6433
|
+
|
|
6434
|
+
if (!summary.git_commit_url) {
|
|
6435
|
+
summary.git_commit_url = `/run/scripts/git/commit.json?cwd=${repoDir}&callback_target=parent&callback=$location.href`
|
|
6436
|
+
}
|
|
6437
|
+
summary.dir = repoDir
|
|
6438
|
+
|
|
6439
|
+
res.json(summary)
|
|
6251
6440
|
}))
|
|
6252
6441
|
this.app.get("/info/gitstatus/:name", ex(async (req, res) => {
|
|
6253
6442
|
try {
|
|
@@ -6944,6 +7133,35 @@ class Server {
|
|
|
6944
7133
|
let current_peer_info = await this.kernel.peer.current_host()
|
|
6945
7134
|
res.json(current_peer_info)
|
|
6946
7135
|
}))
|
|
7136
|
+
this.app.get("/info/router", ex(async (req, res) => {
|
|
7137
|
+
try {
|
|
7138
|
+
// Lightweight router mapping without favicon or installed scans
|
|
7139
|
+
const https_active = this.kernel.peer.https_active
|
|
7140
|
+
const router_info = await this.kernel.peer.router_info_lite()
|
|
7141
|
+
const rewrite_mapping = this.kernel.router.rewrite_mapping
|
|
7142
|
+
const router = this.kernel.router.published()
|
|
7143
|
+
res.json({ https_active, router_info, rewrite_mapping, router })
|
|
7144
|
+
} catch (err) {
|
|
7145
|
+
res.json({ https_active: false, router_info: [], rewrite_mapping: {}, router: {} })
|
|
7146
|
+
}
|
|
7147
|
+
}))
|
|
7148
|
+
this.app.get("/qr", ex(async (req, res) => {
|
|
7149
|
+
try {
|
|
7150
|
+
const data = typeof req.query.data === 'string' ? req.query.data : ''
|
|
7151
|
+
if (!data) {
|
|
7152
|
+
res.status(400).json({ error: 'Missing data parameter' })
|
|
7153
|
+
return
|
|
7154
|
+
}
|
|
7155
|
+
const scale = Math.max(2, Math.min(10, parseInt(req.query.s || '4', 10) || 4))
|
|
7156
|
+
const margin = Math.max(0, Math.min(4, parseInt(req.query.m || '0', 10) || 0))
|
|
7157
|
+
const buf = await QRCode.toBuffer(data, { type: 'png', scale, margin })
|
|
7158
|
+
res.setHeader('Content-Type', 'image/png')
|
|
7159
|
+
res.setHeader('Cache-Control', 'no-store')
|
|
7160
|
+
res.send(buf)
|
|
7161
|
+
} catch (err) {
|
|
7162
|
+
res.status(500).json({ error: 'Failed to generate QR' })
|
|
7163
|
+
}
|
|
7164
|
+
}))
|
|
6947
7165
|
this.app.get("/info/api", ex(async (req,res) => {
|
|
6948
7166
|
// api related info
|
|
6949
7167
|
let repo = this.kernel.git.find(req.query.git)
|
|
@@ -7052,10 +7270,12 @@ class Server {
|
|
|
7052
7270
|
let run_tab = "/p/" + name
|
|
7053
7271
|
let dev_tab = "/p/" + name + "/dev"
|
|
7054
7272
|
let review_tab = "/p/" + name + "/review"
|
|
7273
|
+
let files_tab = "/p/" + name + "/files"
|
|
7055
7274
|
res.render("review", {
|
|
7056
7275
|
run_tab,
|
|
7057
7276
|
dev_tab,
|
|
7058
7277
|
review_tab,
|
|
7278
|
+
files_tab,
|
|
7059
7279
|
name: req.params.name,
|
|
7060
7280
|
type: "review",
|
|
7061
7281
|
title: name,
|
|
@@ -7070,6 +7290,9 @@ class Server {
|
|
|
7070
7290
|
this.app.get("/p/:name/dev", ex(async (req, res) => {
|
|
7071
7291
|
await this.chrome(req, res, "browse")
|
|
7072
7292
|
}))
|
|
7293
|
+
this.app.get("/p/:name/files", ex(async (req, res) => {
|
|
7294
|
+
await this.chrome(req, res, "files")
|
|
7295
|
+
}))
|
|
7073
7296
|
this.app.get("/p/:name/browse", ex(async (req, res) => {
|
|
7074
7297
|
await this.chrome(req, res, "browse")
|
|
7075
7298
|
}))
|