pinokiod 1.3.90 → 1.3.92
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/api/cloudflare/index.js +31 -3
- package/kernel/api/index.js +5 -1
- package/kernel/api/local/index.js +3 -0
- package/kernel/environment.js +42 -25
- package/kernel/index.js +28 -1
- package/kernel/sysinfo.js +1 -2
- package/package.json +4 -2
- package/pipe/index.js +102 -0
- package/pipe/views/login.ejs +78 -0
- package/server/index.js +106 -69
- package/server/public/install.js +2 -2
- package/server/views/browser-experimental.ejs +1 -1
- package/server/views/env_editor.ejs +130 -35
- package/server/views/pre.ejs +109 -0
- package/server/views/settings.ejs +1 -1
- package/server/views/share_editor.ejs +2 -1
|
@@ -1,18 +1,37 @@
|
|
|
1
1
|
const { yellow, green, blue } = require('kleur');
|
|
2
2
|
const QRCode = require('qrcode');
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const Util = require('../../util')
|
|
3
5
|
class C {
|
|
4
6
|
async tunnel (req, ondata, kernel) {
|
|
5
7
|
/*
|
|
6
8
|
{
|
|
7
9
|
"method": "cloundflare.tunnel",
|
|
8
10
|
"params": {
|
|
9
|
-
"uri": "{{local.url}}"
|
|
11
|
+
"uri": "{{local.url}}",
|
|
12
|
+
"passcode": (optional)
|
|
10
13
|
}
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
=> cloudflared tunnel --url http://localhost:8080
|
|
14
17
|
*/
|
|
15
|
-
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
let pipe_uri
|
|
21
|
+
if (req.params.passcode) {
|
|
22
|
+
// 1. start a pipe server => needed for authentication
|
|
23
|
+
const api_path = Util.api_path(req.parent.path, kernel)
|
|
24
|
+
const pinokio_path = path.resolve(api_path, "pinokio.js")
|
|
25
|
+
const config = (await kernel.loader.load(pinokio_path)).resolved
|
|
26
|
+
pipe_uri = await kernel.pipe.start(req.params.uri, req.parent.path, req.params.passcode, config)
|
|
27
|
+
} else {
|
|
28
|
+
// 2. if no passcode required, no need for a pipe server just use the original
|
|
29
|
+
pipe_uri = req.params.uri
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
// 2. run cloudflare tunnel on the proxy
|
|
34
|
+
const message = `cloudflared tunnel --url ${pipe_uri}`
|
|
16
35
|
let params = {
|
|
17
36
|
id: message,
|
|
18
37
|
message
|
|
@@ -65,15 +84,24 @@ class C {
|
|
|
65
84
|
if (!kernel.memory.local[req.parent.path].$share.cloudflare) {
|
|
66
85
|
kernel.memory.local[req.parent.path].$share.cloudflare = {}
|
|
67
86
|
}
|
|
68
|
-
kernel.memory.local[req.parent.path].$share.cloudflare[
|
|
87
|
+
kernel.memory.local[req.parent.path].$share.cloudflare[pipe_uri] = cloudflare_url
|
|
69
88
|
|
|
70
89
|
return { uri: cloudflare_url }
|
|
71
90
|
}
|
|
72
91
|
async stop (req, ondata, kernel) {
|
|
92
|
+
// stop the cloudflare shell
|
|
73
93
|
const message = `cloudflared tunnel --url ${req.params.uri}`
|
|
74
94
|
await kernel.shell.kill({
|
|
75
95
|
id: message
|
|
76
96
|
})
|
|
97
|
+
|
|
98
|
+
// stop pipe server
|
|
99
|
+
await kernel.pipe.stop(req.params.uri, req.parent.path)
|
|
100
|
+
|
|
101
|
+
// delete the local variable
|
|
102
|
+
delete kernel.memory.local[req.parent.path].$share.cloudflare[req.params.uri]
|
|
103
|
+
|
|
77
104
|
}
|
|
105
|
+
|
|
78
106
|
}
|
|
79
107
|
module.exports = C
|
package/kernel/api/index.js
CHANGED
|
@@ -128,6 +128,10 @@ class Api {
|
|
|
128
128
|
// stop all proxies
|
|
129
129
|
this.stopProxy({ script: requestPath })
|
|
130
130
|
|
|
131
|
+
// stop all cloudflare tunnels
|
|
132
|
+
|
|
133
|
+
await this.kernel.stopCloudflare({ path: requestPath })
|
|
134
|
+
|
|
131
135
|
// if there are any pending waiters, delete them
|
|
132
136
|
|
|
133
137
|
if (this.waiter[requestPath]) {
|
|
@@ -538,7 +542,7 @@ class Api {
|
|
|
538
542
|
// get fully resolved env
|
|
539
543
|
let env = await Environment.get2(request.path, this.kernel)
|
|
540
544
|
// set template
|
|
541
|
-
this.kernel.template.update(env)
|
|
545
|
+
this.kernel.template.update({ env })
|
|
542
546
|
|
|
543
547
|
// render until `{{ }}` pattern does not exist
|
|
544
548
|
// 1. render once
|
|
@@ -74,6 +74,9 @@ class Local {
|
|
|
74
74
|
if (val === "true" || val === "1") {
|
|
75
75
|
const c = new Cloudflare()
|
|
76
76
|
req.params.uri = req.params[KEY]
|
|
77
|
+
if ("PINOKIO_SHARE_PASSCODE" in current_env) {
|
|
78
|
+
req.params.passcode = current_env.PINOKIO_SHARE_PASSCODE
|
|
79
|
+
}
|
|
77
80
|
await c.tunnel(req, ondata, kernel)
|
|
78
81
|
}
|
|
79
82
|
}
|
package/kernel/environment.js
CHANGED
|
@@ -102,6 +102,48 @@ const ENVS = [{
|
|
|
102
102
|
"#",
|
|
103
103
|
"##########################################################################",
|
|
104
104
|
],
|
|
105
|
+
}, {
|
|
106
|
+
type: ["system", "app"],
|
|
107
|
+
key: "PINOKIO_SHARE_CLOUDFLARE",
|
|
108
|
+
val: "false",
|
|
109
|
+
comment: [
|
|
110
|
+
"##########################################################################",
|
|
111
|
+
"#",
|
|
112
|
+
"# PINOKIO_SHARE_CLOUDFLARE",
|
|
113
|
+
"# Set this variable to share the app publicly via cloudflare tunnel.",
|
|
114
|
+
"#",
|
|
115
|
+
"##########################################################################",
|
|
116
|
+
]
|
|
117
|
+
}, {
|
|
118
|
+
type: ["app"],
|
|
119
|
+
key: "PINOKIO_SHARE_PASSCODE",
|
|
120
|
+
val: "",
|
|
121
|
+
comment: [
|
|
122
|
+
"##########################################################################",
|
|
123
|
+
"#",
|
|
124
|
+
"# PINOKIO_SHARE_PASSCODE",
|
|
125
|
+
"#",
|
|
126
|
+
"# By default, your publicly shared app will be 100% open to anyone",
|
|
127
|
+
"# with the link via Cloudflare.",
|
|
128
|
+
"#",
|
|
129
|
+
"# You can add authorization by protecting it with a passcode.",
|
|
130
|
+
"# Set this value, and any access to the app will require a pass code input",
|
|
131
|
+
"#",
|
|
132
|
+
"##########################################################################",
|
|
133
|
+
],
|
|
134
|
+
}, {
|
|
135
|
+
type: ["system", "app"],
|
|
136
|
+
key: "PINOKIO_SCRIPT_DEFAULT",
|
|
137
|
+
val: "true",
|
|
138
|
+
comment: [
|
|
139
|
+
"##########################################################################",
|
|
140
|
+
"#",
|
|
141
|
+
"# PINOKIO_SCRIPT_DEFAULT",
|
|
142
|
+
"# If this variable is false, 'default': true menu items in pinokio.js",
|
|
143
|
+
"# will NOT automatically run",
|
|
144
|
+
"#",
|
|
145
|
+
"##########################################################################",
|
|
146
|
+
]
|
|
105
147
|
}, {
|
|
106
148
|
type: ["system"],
|
|
107
149
|
key: "PINOKIO_DRIVE",
|
|
@@ -194,31 +236,6 @@ const ENVS = [{
|
|
|
194
236
|
"#",
|
|
195
237
|
"##########################################################################",
|
|
196
238
|
],
|
|
197
|
-
}, {
|
|
198
|
-
type: ["system", "app"],
|
|
199
|
-
key: "PINOKIO_SHARE_CLOUDFLARE",
|
|
200
|
-
val: "false",
|
|
201
|
-
comment: [
|
|
202
|
-
"##########################################################################",
|
|
203
|
-
"#",
|
|
204
|
-
"# PINOKIO_SHARE_CLOUDFLARE",
|
|
205
|
-
"# Set this variable to share the app publicly via cloudflare tunnel.",
|
|
206
|
-
"#",
|
|
207
|
-
"##########################################################################",
|
|
208
|
-
]
|
|
209
|
-
}, {
|
|
210
|
-
type: ["system", "app"],
|
|
211
|
-
key: "PINOKIO_SCRIPT_DEFAULT",
|
|
212
|
-
val: "true",
|
|
213
|
-
comment: [
|
|
214
|
-
"##########################################################################",
|
|
215
|
-
"#",
|
|
216
|
-
"# PINOKIO_SCRIPT_DEFAULT",
|
|
217
|
-
"# If this variable is false, 'default': true menu items in pinokio.js",
|
|
218
|
-
"# will NOT automatically run",
|
|
219
|
-
"#",
|
|
220
|
-
"##########################################################################",
|
|
221
|
-
]
|
|
222
239
|
}];
|
|
223
240
|
//const ENV = (homedir) => {
|
|
224
241
|
// const lines = ENVS.map((e) => {
|
package/kernel/index.js
CHANGED
|
@@ -22,6 +22,8 @@ const Environment = require("./environment")
|
|
|
22
22
|
const Util = require('./util')
|
|
23
23
|
const Config = require("./pinokio.json")
|
|
24
24
|
const Info = require('./info')
|
|
25
|
+
const Pipe = require("../pipe")
|
|
26
|
+
const Cloudflare = require('./api/cloudflare')
|
|
25
27
|
const VARS = {
|
|
26
28
|
pip: {
|
|
27
29
|
install: {
|
|
@@ -45,11 +47,33 @@ class Kernel {
|
|
|
45
47
|
this.exposed = {}
|
|
46
48
|
this.envs = {}
|
|
47
49
|
this.info = new Info(this)
|
|
50
|
+
this.pipe = new Pipe(this)
|
|
51
|
+
this.cloudflare = new Cloudflare()
|
|
48
52
|
}
|
|
49
53
|
/*
|
|
50
54
|
kernel.env() => return the system environment
|
|
51
55
|
kernel.env("api/stablediffusion") => returns the full environment object
|
|
52
56
|
*/
|
|
57
|
+
async stopCloudflare(option) {
|
|
58
|
+
if (option.uri) {
|
|
59
|
+
} else if (option.path) {
|
|
60
|
+
let scriptPath = option.path
|
|
61
|
+
if (this.memory.local[scriptPath] && this.memory.local[scriptPath].$share) {
|
|
62
|
+
let cf = this.memory.local[scriptPath].$share.cloudflare
|
|
63
|
+
let uris = Object.keys(cf)
|
|
64
|
+
for(let uri of uris) {
|
|
65
|
+
await this.cloudflare.stop({
|
|
66
|
+
parent: {
|
|
67
|
+
path: scriptPath
|
|
68
|
+
},
|
|
69
|
+
params: { uri }
|
|
70
|
+
}, (e) => {
|
|
71
|
+
process.stdout.write(e.raw)
|
|
72
|
+
}, this)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
53
77
|
async env(...args) {
|
|
54
78
|
let folderpath
|
|
55
79
|
if (args) {
|
|
@@ -114,7 +138,10 @@ class Kernel {
|
|
|
114
138
|
* let available_port = await kernel.port()
|
|
115
139
|
*
|
|
116
140
|
**********************************************/
|
|
117
|
-
return portfinder.getPortPromise(
|
|
141
|
+
return portfinder.getPortPromise({
|
|
142
|
+
port: 42000
|
|
143
|
+
})
|
|
144
|
+
//return portfinder.getPortPromise()
|
|
118
145
|
}
|
|
119
146
|
path(...args) {
|
|
120
147
|
return path.resolve(this.homedir, ...args)
|
package/kernel/sysinfo.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pinokiod",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.92",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"7zip-min-win-asar-support": "^1.4.4",
|
|
13
13
|
"async-retry": "^1.3.3",
|
|
14
14
|
"axios": "^1.4.0",
|
|
15
|
+
"bcrypt": "^5.1.1",
|
|
15
16
|
"clear-module": "^4.1.2",
|
|
16
17
|
"compressing": "^1.10.0",
|
|
17
18
|
"cookie-parser": "^1.4.6",
|
|
@@ -33,9 +34,10 @@
|
|
|
33
34
|
"glob-gitignore": "^1.0.14",
|
|
34
35
|
"handlebars": "^4.7.7",
|
|
35
36
|
"http-proxy": "^1.18.1",
|
|
37
|
+
"http-proxy-middleware": "^3.0.0",
|
|
36
38
|
"http-terminator": "^3.2.0",
|
|
37
39
|
"isomorphic-git": "^1.23.0",
|
|
38
|
-
"jimini": "^0.0.
|
|
40
|
+
"jimini": "^0.0.6",
|
|
39
41
|
"jsdom": "^24.0.0",
|
|
40
42
|
"jsonpath-plus": "^7.2.0",
|
|
41
43
|
"key-store": "^1.2.0",
|
package/pipe/index.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const { createProxyMiddleware } = require('http-proxy-middleware');
|
|
2
|
+
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const { createHttpTerminator } = require('http-terminator')
|
|
5
|
+
const express = require('express')
|
|
6
|
+
const session = require('express-session');
|
|
7
|
+
const bcrypt = require('bcrypt');
|
|
8
|
+
const cors = require('cors');
|
|
9
|
+
class Pipe {
|
|
10
|
+
constructor(kernel) {
|
|
11
|
+
this.kernel = kernel
|
|
12
|
+
this.terminators = {}
|
|
13
|
+
this.procs = {}
|
|
14
|
+
}
|
|
15
|
+
authenticate (req, res, next) {
|
|
16
|
+
if (req.session.authenticated) {
|
|
17
|
+
next();
|
|
18
|
+
} else {
|
|
19
|
+
console.log("not authenticated. redirect /pionkio/login")
|
|
20
|
+
res.redirect('/pinokio/login');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// a pipe server for creating publicly shareable endpoints
|
|
24
|
+
async start(url, scriptPath, passcode, config) {
|
|
25
|
+
|
|
26
|
+
if (!config) config = {}
|
|
27
|
+
if (config.icon) {
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
let port = await this.kernel.port()
|
|
32
|
+
let app = express();
|
|
33
|
+
app.use(cors({ origin: '*' }));
|
|
34
|
+
app.use(express.urlencoded({ extended: true }));
|
|
35
|
+
app.set('view engine', 'ejs');
|
|
36
|
+
app.set("views", path.resolve(__dirname, "views"))
|
|
37
|
+
// app.use(express.static(path.resolve(__dirname, 'public')));
|
|
38
|
+
app.use(session({
|
|
39
|
+
secret: 'oikonip',
|
|
40
|
+
resave: false,
|
|
41
|
+
saveUninitialized: true
|
|
42
|
+
}));
|
|
43
|
+
app.get('/pinokio/login', (req, res) => {
|
|
44
|
+
res.render("login", { error: null, ...config })
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
app.post('/pinokio/login', async (req, res) => {
|
|
48
|
+
|
|
49
|
+
const { password } = req.body;
|
|
50
|
+
|
|
51
|
+
//if (await bcrypt.compare(password, hashedPassword)) {
|
|
52
|
+
if (password === passcode) {
|
|
53
|
+
req.session.authenticated = true;
|
|
54
|
+
res.redirect('/');
|
|
55
|
+
} else {
|
|
56
|
+
res.status(401).render("login", { error: "Invalid passcode", ...config })
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
app.get('/pinokio/logout', (req, res) => {
|
|
61
|
+
req.session.authenticated = false;
|
|
62
|
+
res.redirect('/login');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
const proxy = createProxyMiddleware({
|
|
67
|
+
pathFilter: '/',
|
|
68
|
+
target: url,
|
|
69
|
+
ws: true
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
app.use('/', this.authenticate, proxy)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
await new Promise((resolve, reject) => {
|
|
76
|
+
console.log("starting pipe server at port", port)
|
|
77
|
+
let server = app.listen(port, () => {
|
|
78
|
+
console.log(`Pipe server listening on port ${port}`)
|
|
79
|
+
resolve()
|
|
80
|
+
});
|
|
81
|
+
let terminator = createHttpTerminator({ server });
|
|
82
|
+
this.terminators[url] = terminator
|
|
83
|
+
})
|
|
84
|
+
let pipe_uri = `http://localhost:${port}`
|
|
85
|
+
|
|
86
|
+
// original url => pipe url
|
|
87
|
+
if (!this.procs[scriptPath]) {
|
|
88
|
+
this.procs[scriptPath] = {}
|
|
89
|
+
}
|
|
90
|
+
this.procs[scriptPath][url] = pipe_uri
|
|
91
|
+
return pipe_uri
|
|
92
|
+
}
|
|
93
|
+
async stop(url, scriptPath) {
|
|
94
|
+
console.log("stop pipe", { scriptPath, url, terminator: this.terminators[url] })
|
|
95
|
+
if (this.terminators[url]) {
|
|
96
|
+
await this.terminators[url].terminate()
|
|
97
|
+
delete this.terminators[url]
|
|
98
|
+
delete this.procs[scriptPath][url]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
module.exports = Pipe
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
<style>
|
|
7
|
+
body {
|
|
8
|
+
display: flex;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
align-items: center;
|
|
11
|
+
height: 100%;
|
|
12
|
+
font-family: sans-serif;
|
|
13
|
+
font-size: 14px;
|
|
14
|
+
background: rgb(27, 28, 29);
|
|
15
|
+
color: white;
|
|
16
|
+
}
|
|
17
|
+
main {
|
|
18
|
+
max-width: 500px;
|
|
19
|
+
width: 100%;
|
|
20
|
+
text-align: center;
|
|
21
|
+
text-transform: lowercase;
|
|
22
|
+
}
|
|
23
|
+
main img {
|
|
24
|
+
width: 80px;
|
|
25
|
+
margin-bottom: 20px;
|
|
26
|
+
}
|
|
27
|
+
main form {
|
|
28
|
+
display: flex;
|
|
29
|
+
}
|
|
30
|
+
main input {
|
|
31
|
+
flex-grow: 1;
|
|
32
|
+
padding: 10px;
|
|
33
|
+
box-sizing: border-box;
|
|
34
|
+
border: none;
|
|
35
|
+
/*
|
|
36
|
+
background: rgba(0,0,0,0.06);
|
|
37
|
+
*/
|
|
38
|
+
font-size: 14px;
|
|
39
|
+
border-radius: 0;
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
main button {
|
|
43
|
+
color: white;
|
|
44
|
+
background: royalblue;
|
|
45
|
+
padding: 10px;
|
|
46
|
+
box-sizing: border-box;
|
|
47
|
+
border: none;
|
|
48
|
+
}
|
|
49
|
+
.error {
|
|
50
|
+
border-left: 3px solid red;
|
|
51
|
+
color: red;
|
|
52
|
+
text-align: left;
|
|
53
|
+
padding: 5px 10px;
|
|
54
|
+
box-sizing: border-box;
|
|
55
|
+
font-size: 14px;
|
|
56
|
+
}
|
|
57
|
+
h1 {
|
|
58
|
+
letter-spacing: -1px;
|
|
59
|
+
margin-bottom: 20px;
|
|
60
|
+
}
|
|
61
|
+
</style>
|
|
62
|
+
</head>
|
|
63
|
+
<body>
|
|
64
|
+
<main>
|
|
65
|
+
<!--
|
|
66
|
+
<img src="/pinokio-black.png"/>
|
|
67
|
+
-->
|
|
68
|
+
<h1>PINOKIO</h1>
|
|
69
|
+
<form method="POST" action="/pinokio/login">
|
|
70
|
+
<input placeholder="enter passcode" type="password" name="password" required>
|
|
71
|
+
<button type="submit">Login</button>
|
|
72
|
+
</form>
|
|
73
|
+
<% if (error) { %>
|
|
74
|
+
<div class='error'><%=error%></div>
|
|
75
|
+
<% } %>
|
|
76
|
+
<main>
|
|
77
|
+
</body>
|
|
78
|
+
</html>
|
package/server/index.js
CHANGED
|
@@ -172,6 +172,28 @@ class Server {
|
|
|
172
172
|
}
|
|
173
173
|
})
|
|
174
174
|
}
|
|
175
|
+
async init_env(env_dir_path) {
|
|
176
|
+
let current = this.kernel.path(env_dir_path, "ENVIRONMENT")
|
|
177
|
+
// if environment.json doesn't exist,
|
|
178
|
+
let exists = await this.exists(current)
|
|
179
|
+
if (exists) {
|
|
180
|
+
// if ENVIRONMENT already exists, don't do anything
|
|
181
|
+
} else {
|
|
182
|
+
// if ENVIRONMENT doesn't exist, need to create one
|
|
183
|
+
// 1. if _ENVIRONMENT exists, create ENVIRONMENT by appending _ENVIRONMENT to ENVIRONMENT
|
|
184
|
+
// 2. if _ENVIRONMENT doesn't exist, just write ENVIRONMENT
|
|
185
|
+
// if _ENVIRONMENT exists,
|
|
186
|
+
let _environment = this.kernel.path(env_dir_path, "_ENVIRONMENT")
|
|
187
|
+
let _exists = await this.exists(_environment)
|
|
188
|
+
let content = await Environment.ENV("app")
|
|
189
|
+
if (_exists) {
|
|
190
|
+
let _environmentStr = await fs.promises.readFile(_environment, "utf8")
|
|
191
|
+
await fs.promises.writeFile(current, _environmentStr + "\n\n\n" + content)
|
|
192
|
+
} else {
|
|
193
|
+
await fs.promises.writeFile(current, content)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
175
197
|
async chrome(req, res, type) {
|
|
176
198
|
let name = req.params.name
|
|
177
199
|
let app_path = this.kernel.path("api", name, "pinokio.js")
|
|
@@ -230,26 +252,7 @@ class Server {
|
|
|
230
252
|
}
|
|
231
253
|
}
|
|
232
254
|
|
|
233
|
-
|
|
234
|
-
// if environment.json doesn't exist,
|
|
235
|
-
let exists = await this.exists(current)
|
|
236
|
-
if (exists) {
|
|
237
|
-
// if ENVIRONMENT already exists, don't do anything
|
|
238
|
-
} else {
|
|
239
|
-
// if ENVIRONMENT doesn't exist, need to create one
|
|
240
|
-
// 1. if _ENVIRONMENT exists, create ENVIRONMENT by appending _ENVIRONMENT to ENVIRONMENT
|
|
241
|
-
// 2. if _ENVIRONMENT doesn't exist, just write ENVIRONMENT
|
|
242
|
-
// if _ENVIRONMENT exists,
|
|
243
|
-
let _environment = this.kernel.path("api", name, "_ENVIRONMENT")
|
|
244
|
-
let _exists = await this.exists(_environment)
|
|
245
|
-
let content = await Environment.ENV("app")
|
|
246
|
-
if (_exists) {
|
|
247
|
-
let _environmentStr = await fs.promises.readFile(_environment, "utf8")
|
|
248
|
-
await fs.promises.writeFile(current, content + "\n\n" + _environmentStr)
|
|
249
|
-
} else {
|
|
250
|
-
await fs.promises.writeFile(current, content)
|
|
251
|
-
}
|
|
252
|
-
}
|
|
255
|
+
await this.init_env("api/" + name)
|
|
253
256
|
|
|
254
257
|
let mode = "run"
|
|
255
258
|
if (req.query && req.query.mode) {
|
|
@@ -1872,9 +1875,15 @@ class Server {
|
|
|
1872
1875
|
// stop proxies
|
|
1873
1876
|
for(let scriptPath in this.kernel.api.proxies) {
|
|
1874
1877
|
try {
|
|
1878
|
+
// Turn off local sharing
|
|
1875
1879
|
await this.kernel.api.stopProxy({
|
|
1876
1880
|
script: scriptPath
|
|
1877
1881
|
})
|
|
1882
|
+
|
|
1883
|
+
// Turn off cloudflare sharing
|
|
1884
|
+
await this.kernel.stopCloudflare({
|
|
1885
|
+
path: scriptPath
|
|
1886
|
+
})
|
|
1878
1887
|
} catch (e) {
|
|
1879
1888
|
}
|
|
1880
1889
|
}
|
|
@@ -2165,18 +2174,56 @@ class Server {
|
|
|
2165
2174
|
}
|
|
2166
2175
|
})
|
|
2167
2176
|
this.app.post("/env", async (req, res) => {
|
|
2168
|
-
|
|
2169
|
-
let fullpath = path.resolve(this.kernel.homedir, req.body.filepath)
|
|
2177
|
+
let fullpath = path.resolve(this.kernel.homedir, req.body.filepath, "ENVIRONMENT")
|
|
2170
2178
|
let updated = req.body.vals
|
|
2171
2179
|
await Util.update_env(fullpath, updated)
|
|
2172
2180
|
res.json({})
|
|
2173
2181
|
})
|
|
2182
|
+
this.app.get("/env", async (req, res) => {
|
|
2183
|
+
let env_path = path.resolve(this.kernel.homedir)
|
|
2184
|
+
await this.init_env(env_path)
|
|
2185
|
+
|
|
2186
|
+
let filepath = path.resolve(this.kernel.homedir, "ENVIRONMENT")
|
|
2187
|
+
let editorpath = "/edit/ENVIRONMENT"
|
|
2188
|
+
|
|
2189
|
+
const items = await Util.parse_env_detail(filepath)
|
|
2190
|
+
|
|
2191
|
+
|
|
2192
|
+
res.render("env_editor", {
|
|
2193
|
+
config: null,
|
|
2194
|
+
name: null,
|
|
2195
|
+
init: null,
|
|
2196
|
+
editorpath,
|
|
2197
|
+
items,
|
|
2198
|
+
theme: this.theme,
|
|
2199
|
+
filepath,
|
|
2200
|
+
agent: this.agent,
|
|
2201
|
+
})
|
|
2202
|
+
})
|
|
2174
2203
|
this.app.get("/env/*", async (req, res) => {
|
|
2204
|
+
|
|
2205
|
+
let env_path = req.params[0]
|
|
2206
|
+
await this.init_env(env_path)
|
|
2207
|
+
|
|
2175
2208
|
let pathComponents = req.params[0].split("/")
|
|
2176
|
-
let filepath = path.resolve(this.kernel.homedir, req.params[0])
|
|
2177
|
-
let editorpath = "/edit/" + req.params[0]
|
|
2209
|
+
let filepath = path.resolve(this.kernel.homedir, req.params[0], "ENVIRONMENT")
|
|
2210
|
+
let editorpath = "/edit/" + req.params[0] + "/ENVIRONMENT"
|
|
2178
2211
|
const items = await Util.parse_env_detail(filepath)
|
|
2212
|
+
let p = path.resolve(this.kernel.homedir, env_path, "pinokio.js")
|
|
2213
|
+
let config = (await this.kernel.loader.load(p)).resolved
|
|
2214
|
+
if (config.icon) {
|
|
2215
|
+
config.icon = `/${env_path}/${config.icon}?raw=true`
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
let name
|
|
2219
|
+
if (env_path.startsWith("api")) {
|
|
2220
|
+
name = env_path.split("/")[1]
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2179
2223
|
res.render("env_editor", {
|
|
2224
|
+
config,
|
|
2225
|
+
name,
|
|
2226
|
+
init: req.query ? req.query.init : null,
|
|
2180
2227
|
editorpath,
|
|
2181
2228
|
items,
|
|
2182
2229
|
theme: this.theme,
|
|
@@ -2184,12 +2231,45 @@ class Server {
|
|
|
2184
2231
|
agent: this.agent,
|
|
2185
2232
|
})
|
|
2186
2233
|
})
|
|
2234
|
+
this.app.get("/pre/api/:name", async (req, res) => {
|
|
2235
|
+
let p = path.resolve(this.kernel.homedir, req.params.name, "pinokio.js")
|
|
2236
|
+
let config = (await this.kernel.loader.load(p)).resolved
|
|
2237
|
+
if (config && config.pre) {
|
|
2238
|
+
config.pre.forEach((item) => {
|
|
2239
|
+
item.icon = `/api/${req.params.name}/${item.icon}?raw=true`
|
|
2240
|
+
})
|
|
2241
|
+
res.render("pre", {
|
|
2242
|
+
name: req.params.name,
|
|
2243
|
+
theme: this.theme,
|
|
2244
|
+
agent: this.agent,
|
|
2245
|
+
name: req.params.name,
|
|
2246
|
+
items: config.pre
|
|
2247
|
+
})
|
|
2248
|
+
} else {
|
|
2249
|
+
res.redirect("/env/" + req.params.name + "?init=true")
|
|
2250
|
+
}
|
|
2251
|
+
})
|
|
2252
|
+
this.app.get("/initialize/:name", async (req, res) => {
|
|
2253
|
+
let p = path.resolve(this.kernel.homedir, "api", req.params.name, "pinokio.js")
|
|
2254
|
+
let config = (await this.kernel.loader.load(p)).resolved
|
|
2255
|
+
if (config) {
|
|
2256
|
+
// if pinokio.js exists
|
|
2257
|
+
if (config.pre && Array.isArray(config.pre)) {
|
|
2258
|
+
// if pre exists, redirect to /pre/:name
|
|
2259
|
+
res.redirect(`/pre/api/${req.params.name}`)
|
|
2260
|
+
} else {
|
|
2261
|
+
// if pre doesn't exist, redirect to /env/:name
|
|
2262
|
+
res.redirect(`/env/api/${req.params.name}?init=true`)
|
|
2263
|
+
}
|
|
2264
|
+
} else {
|
|
2265
|
+
// if pinokio.js doesn't exist, send to /browser/:name
|
|
2266
|
+
res.redirect(`/pinokio/browser/${req.params.name}`)
|
|
2267
|
+
}
|
|
2268
|
+
})
|
|
2187
2269
|
this.app.get("/share/:name", async (req, res) => {
|
|
2188
2270
|
let filepath = path.resolve(this.kernel.homedir, "api", req.params.name, "ENVIRONMENT")
|
|
2189
2271
|
//let filepath = path.resolve(this.kernel.homedir, req.params[0])
|
|
2190
|
-
console.log("filepath", filepath)
|
|
2191
2272
|
const config = await Util.parse_env(filepath)
|
|
2192
|
-
console.log("config", config)
|
|
2193
2273
|
const keys = [
|
|
2194
2274
|
"PINOKIO_SHARE_CLOUDFLARE",
|
|
2195
2275
|
"PINOKIO_SHARE_LOCAL",
|
|
@@ -2206,10 +2286,8 @@ class Server {
|
|
|
2206
2286
|
})
|
|
2207
2287
|
let cloudflare_links = []
|
|
2208
2288
|
let local_links = []
|
|
2209
|
-
console.log({ app_path, scripts })
|
|
2210
2289
|
for(let script in this.kernel.memory.local) {
|
|
2211
2290
|
let mem = this.kernel.memory.local[script]
|
|
2212
|
-
console.log({ script, mem })
|
|
2213
2291
|
if (mem.$share) {
|
|
2214
2292
|
if (mem.$share.cloudflare) {
|
|
2215
2293
|
for(let key in mem.$share.cloudflare) {
|
|
@@ -2802,47 +2880,6 @@ class Server {
|
|
|
2802
2880
|
});
|
|
2803
2881
|
})
|
|
2804
2882
|
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
// this.expose_app = express();
|
|
2808
|
-
// this.expose_app.use(cors({
|
|
2809
|
-
// origin: '*'
|
|
2810
|
-
// }));
|
|
2811
|
-
// this.expose_app.use('/', proxy(async (req) => {
|
|
2812
|
-
// console.log("req.url", req.url)
|
|
2813
|
-
// let tokens = req.url.split("/")
|
|
2814
|
-
// console.log({ tokens })
|
|
2815
|
-
// if (tokens.length >=2 ) {
|
|
2816
|
-
// let app = tokens[1]
|
|
2817
|
-
// console.log({ app })
|
|
2818
|
-
// console.log("resolved = ", this.kernel.exposed[app])
|
|
2819
|
-
// return this.kernel.exposed[app]
|
|
2820
|
-
// }
|
|
2821
|
-
// }, {
|
|
2822
|
-
//// proxyReqPathResolver: (req) => {
|
|
2823
|
-
//// console.log("proxyReqPathResolver", req.url)
|
|
2824
|
-
//// let chunks = req.url.split("/")
|
|
2825
|
-
//// let transformed
|
|
2826
|
-
//// if (chunks.length > 3) {
|
|
2827
|
-
//// transformed = "/" + chunks.slice(3).join("/")
|
|
2828
|
-
//// } else {
|
|
2829
|
-
//// transformed = "/"
|
|
2830
|
-
//// }
|
|
2831
|
-
//// console.log({ chunks, transformed })
|
|
2832
|
-
//// return transformed
|
|
2833
|
-
//// }
|
|
2834
|
-
// }))
|
|
2835
|
-
// this.expose_port = await portfinder.getPortPromise({ port: this.port })
|
|
2836
|
-
// console.log("EXPOSE_PORT", this.expose_port)
|
|
2837
|
-
// await new Promise((resolve, reject) => {
|
|
2838
|
-
// this.expose_server = this.expose_app.listen(this.expose_port, () => {
|
|
2839
|
-
// console.log(`Server listening on port ${this.expose_port}`)
|
|
2840
|
-
// resolve()
|
|
2841
|
-
// });
|
|
2842
|
-
// this.exposeTerminator = createHttpTerminator({
|
|
2843
|
-
// server: this.expose_server
|
|
2844
|
-
// });
|
|
2845
|
-
// })
|
|
2846
2883
|
}
|
|
2847
2884
|
}
|
|
2848
2885
|
module.exports = Server
|
package/server/public/install.js
CHANGED
|
@@ -104,8 +104,8 @@ const install = async (name, url, term, socket, options) => {
|
|
|
104
104
|
})
|
|
105
105
|
} else {
|
|
106
106
|
// ask the backend to create install.json and start.json if gradio
|
|
107
|
-
//location.href =
|
|
108
|
-
location.href = `/
|
|
107
|
+
//location.href = `/pinokio/browser/${name}`
|
|
108
|
+
location.href = `/initialize/${name}`
|
|
109
109
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -561,7 +561,7 @@ body.dark .loader-container {
|
|
|
561
561
|
<a target="/share/<%=name%>" href="/share/<%=name%>", class="btn header-item frame-link" data-index="1" data-mode="refresh">
|
|
562
562
|
<div class='tab'><i class="fa-solid fa-wifi"></i> Share</div>
|
|
563
563
|
</a>
|
|
564
|
-
<a target="/env/api/<%=name
|
|
564
|
+
<a target="/env/api/<%=name%>" href="/env/api/<%=name%>", class="btn header-item frame-link" data-index="1" data-mode="refresh">
|
|
565
565
|
<div class='tab'><i class="fa-solid fa-gear"></i> Configure</div>
|
|
566
566
|
</a>
|
|
567
567
|
</div>
|
|
@@ -73,13 +73,25 @@ td.key {
|
|
|
73
73
|
.button:hover {
|
|
74
74
|
|
|
75
75
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
<% if (init) { %>
|
|
77
|
+
main {
|
|
78
|
+
width: 100%;
|
|
79
|
+
box-sizing: border-box;
|
|
80
|
+
max-width: 600px;
|
|
81
|
+
padding: 20px;
|
|
82
|
+
margin: 0 auto;
|
|
83
|
+
}
|
|
84
|
+
<% } else { %>
|
|
85
|
+
#editor {
|
|
86
|
+
width: 100%;
|
|
87
|
+
box-sizing: border-box;
|
|
88
|
+
max-width: 600px;
|
|
89
|
+
padding: 20px;
|
|
90
|
+
margin: 0 auto;
|
|
91
|
+
}
|
|
92
|
+
<% } %>
|
|
93
|
+
|
|
81
94
|
header .runner {
|
|
82
|
-
padding: 10px 0 0;
|
|
83
95
|
}
|
|
84
96
|
.disabled {
|
|
85
97
|
opacity: 0.7;
|
|
@@ -119,6 +131,59 @@ body.dark .comment {
|
|
|
119
131
|
#save {
|
|
120
132
|
background: royalblue;
|
|
121
133
|
}
|
|
134
|
+
a#customize {
|
|
135
|
+
text-decoration: underline;
|
|
136
|
+
cursor: pointer;
|
|
137
|
+
}
|
|
138
|
+
.e {
|
|
139
|
+
padding: 10px 0;
|
|
140
|
+
}
|
|
141
|
+
#editor {
|
|
142
|
+
padding: 20px 0;
|
|
143
|
+
}
|
|
144
|
+
.center {
|
|
145
|
+
display: flex;
|
|
146
|
+
align-items: center;
|
|
147
|
+
justify-content: center;
|
|
148
|
+
}
|
|
149
|
+
body {
|
|
150
|
+
display: block;
|
|
151
|
+
}
|
|
152
|
+
.card {
|
|
153
|
+
display: flex;
|
|
154
|
+
padding: 10px 0;
|
|
155
|
+
box-sizing: border-box;
|
|
156
|
+
}
|
|
157
|
+
.card img {
|
|
158
|
+
width: 100px;
|
|
159
|
+
border-radius: 10px;
|
|
160
|
+
}
|
|
161
|
+
.card .description {
|
|
162
|
+
opacity: 0.8;
|
|
163
|
+
}
|
|
164
|
+
.card .title {
|
|
165
|
+
font-weight: bold;
|
|
166
|
+
font-size: 30px;
|
|
167
|
+
letter-spacing: -1px;
|
|
168
|
+
}
|
|
169
|
+
.card .col {
|
|
170
|
+
padding-left: 10px;
|
|
171
|
+
}
|
|
172
|
+
h1 {
|
|
173
|
+
font-size: 50px;
|
|
174
|
+
font-family: georgia;
|
|
175
|
+
font-style: italic;
|
|
176
|
+
}
|
|
177
|
+
footer {
|
|
178
|
+
background: none;
|
|
179
|
+
padding: 0;
|
|
180
|
+
text-align: right;
|
|
181
|
+
display: block;
|
|
182
|
+
margin-bottom: 20px;
|
|
183
|
+
}
|
|
184
|
+
footer .btn {
|
|
185
|
+
margin-left: 10px;
|
|
186
|
+
}
|
|
122
187
|
</style>
|
|
123
188
|
<script>
|
|
124
189
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
@@ -145,40 +210,70 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
145
210
|
return res.json()
|
|
146
211
|
})
|
|
147
212
|
console.log("res" ,res)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
213
|
+
<% if (init && name) { %>
|
|
214
|
+
location.href = "/pinokio/browser/<%=name%>"
|
|
215
|
+
<% } else { %>
|
|
216
|
+
n.Noty({
|
|
217
|
+
text: `Updated!`
|
|
218
|
+
})
|
|
219
|
+
<% } %>
|
|
151
220
|
})
|
|
221
|
+
<% if (init) { %>
|
|
222
|
+
document.querySelector("#customize").addEventListener("click", (e) => {
|
|
223
|
+
e.preventDefault()
|
|
224
|
+
e.stopPropagation()
|
|
225
|
+
document.querySelector("#editor").classList.toggle("hidden")
|
|
226
|
+
})
|
|
227
|
+
<% } %>
|
|
152
228
|
})
|
|
153
229
|
</script>
|
|
154
230
|
</head>
|
|
155
|
-
<body class='
|
|
156
|
-
<
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
</header>
|
|
169
|
-
<main>
|
|
170
|
-
<div id='editor'>
|
|
171
|
-
<% items.forEach((item) => { %>
|
|
172
|
-
<% if (item.type === "comment") { %>
|
|
173
|
-
<div class='comment'><%=item.val.replaceAll(/^[ #]+/g, "")%></div>
|
|
174
|
-
<% } else { %>
|
|
175
|
-
<div class='env-item'>
|
|
176
|
-
<label for="<%=item.key%>"><%=item.key%></label>
|
|
177
|
-
<input id="<%=item.key%>" type="text" value="<%=item.val%>">
|
|
231
|
+
<body class='<%=theme%> <%=init ? 'center' : ''%>'>
|
|
232
|
+
<main>
|
|
233
|
+
<% if (init) { %>
|
|
234
|
+
<div>
|
|
235
|
+
<h1>Install</h1>
|
|
236
|
+
<% if (config) { %>
|
|
237
|
+
<div class='card'>
|
|
238
|
+
<% if (config.icon) { %>
|
|
239
|
+
<img src="<%=config.icon%>">
|
|
240
|
+
<% } %>
|
|
241
|
+
<div class='col'>
|
|
242
|
+
<div class='title'><%=config.title%></div>
|
|
243
|
+
<div class='description'><%=config.description%></div>
|
|
178
244
|
</div>
|
|
179
|
-
|
|
180
|
-
<% }
|
|
245
|
+
</div>
|
|
246
|
+
<% } %>
|
|
247
|
+
<footer>
|
|
248
|
+
<button id='customize' class='btn'><i class="fa-solid fa-gear"></i> Customize</button>
|
|
249
|
+
<div id='save' class='btn'>
|
|
250
|
+
<span class='save'><i class="fa-solid fa-check"></i> Install</span>
|
|
251
|
+
</div>
|
|
252
|
+
</footer>
|
|
181
253
|
</div>
|
|
182
|
-
|
|
254
|
+
<% } else { %>
|
|
255
|
+
<header class='navheader grabbable'>
|
|
256
|
+
<div class='runner'>
|
|
257
|
+
<div id='save' class='btn'>
|
|
258
|
+
<span class='save'><i class="fa-solid fa-check"></i> Save</span>
|
|
259
|
+
</div>
|
|
260
|
+
<a class='btn' href="<%=editorpath%>"><i class="fa-solid fa-pen-to-square"></i> Edit file</a>
|
|
261
|
+
<button class='btn' id='open-fs' data-filepath="<%=filepath%>"><i class="fa-solid fa-eye"></i> Open in File Explorer</button>
|
|
262
|
+
</div>
|
|
263
|
+
</header>
|
|
264
|
+
<% } %>
|
|
265
|
+
<div id='editor' class='<%=init ? "hidden" : ''%>'>
|
|
266
|
+
<% items.forEach((item) => { %>
|
|
267
|
+
<% if (item.type === "comment") { %>
|
|
268
|
+
<div class='comment'><%=item.val.replaceAll(/^[ #]+/g, "")%></div>
|
|
269
|
+
<% } else { %>
|
|
270
|
+
<div class='env-item'>
|
|
271
|
+
<label for="<%=item.key%>"><%=item.key%></label>
|
|
272
|
+
<input id="<%=item.key%>" type="text" value="<%=item.val%>">
|
|
273
|
+
</div>
|
|
274
|
+
<% } %>
|
|
275
|
+
<% }) %>
|
|
276
|
+
</div>
|
|
277
|
+
</main>
|
|
183
278
|
</body>
|
|
184
279
|
</html>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<html class='columns'>
|
|
2
|
+
<head>
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
|
4
|
+
<script src="/hotkeys.min.js"></script>
|
|
5
|
+
<script src="/ace/ace.js"></script>
|
|
6
|
+
<script src="/noty.js"></script>
|
|
7
|
+
<script src="/notyq.js"></script>
|
|
8
|
+
<script src="/xterm.js"></script>
|
|
9
|
+
<script src="/xterm-addon-fit.js"></script>
|
|
10
|
+
<script src="/xterm-addon-web-links.js"></script>
|
|
11
|
+
<script src="/xterm-theme.js"></script>
|
|
12
|
+
<script src="/xterm-addon-search.js"></script>
|
|
13
|
+
<script src="/xterm-addon-search-bar.js"></script>
|
|
14
|
+
<script src="/sweetalert2.js"></script>
|
|
15
|
+
<script src="/Socket.js"></script>
|
|
16
|
+
<script src="/common.js"></script>
|
|
17
|
+
<script src="/he.js"></script>
|
|
18
|
+
<script src="/opener.js"></script>
|
|
19
|
+
<script src="/nav.js"></script>
|
|
20
|
+
<script src="/autoexpand.js"></script>
|
|
21
|
+
<script src="/modalinput.js"></script>
|
|
22
|
+
<script src="/simplemodal.js"></script>
|
|
23
|
+
<script src="/waitmodal.js"></script>
|
|
24
|
+
<script src="/notifyinput.js"></script>
|
|
25
|
+
<script src="/report.js"></script>
|
|
26
|
+
<link href="/xterm.min.css" rel="stylesheet" />
|
|
27
|
+
<link href="/css/fontawesome.min.css" rel="stylesheet">
|
|
28
|
+
<link href="/css/solid.min.css" rel="stylesheet">
|
|
29
|
+
<link href="/css/regular.min.css" rel="stylesheet">
|
|
30
|
+
<link href="/css/brands.min.css" rel="stylesheet">
|
|
31
|
+
<link href="/style.css" rel="stylesheet"/>
|
|
32
|
+
<link href="/noty.css" rel="stylesheet"/>
|
|
33
|
+
<% if (agent === "electron") { %>
|
|
34
|
+
<link href="/electron.css" rel="stylesheet"/>
|
|
35
|
+
<% } %>
|
|
36
|
+
<style>
|
|
37
|
+
main {
|
|
38
|
+
max-width: 500px;
|
|
39
|
+
margin: 50px auto;
|
|
40
|
+
width: 100%;
|
|
41
|
+
}
|
|
42
|
+
h1 {
|
|
43
|
+
letter-spacing: -1px;
|
|
44
|
+
}
|
|
45
|
+
.item {
|
|
46
|
+
border-radius: 5px;
|
|
47
|
+
padding: 10px;
|
|
48
|
+
box-sizing: border-box;
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: flex-start;
|
|
51
|
+
/*
|
|
52
|
+
border: 1px solid rgba(0,0,0,0.07);
|
|
53
|
+
*/
|
|
54
|
+
background: rgba(0,0,0,0.04);
|
|
55
|
+
margin: 10px 0;
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
.item .btn {
|
|
59
|
+
width: 50px;
|
|
60
|
+
text-align: center;
|
|
61
|
+
}
|
|
62
|
+
.item img {
|
|
63
|
+
width: 100px;
|
|
64
|
+
}
|
|
65
|
+
.item .col {
|
|
66
|
+
flex-grow: 1;
|
|
67
|
+
padding: 10px;
|
|
68
|
+
box-sizing: border-box;
|
|
69
|
+
}
|
|
70
|
+
footer {
|
|
71
|
+
background: none;
|
|
72
|
+
}
|
|
73
|
+
footer a.btn {
|
|
74
|
+
text-align: center;
|
|
75
|
+
display: block;
|
|
76
|
+
width: 100%;
|
|
77
|
+
}
|
|
78
|
+
.e {
|
|
79
|
+
padding: 5px 0;
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
82
|
+
</head>
|
|
83
|
+
<body class='columns <%=theme%>'>
|
|
84
|
+
<header class='navheader grabbable'>
|
|
85
|
+
</header>
|
|
86
|
+
<main>
|
|
87
|
+
<h1>Requirements</h1>
|
|
88
|
+
<div class='e'>
|
|
89
|
+
<div>To run <%=name%>, you need to have the following programs installed.</div>
|
|
90
|
+
<div>Get the apps if you haven't already, and press "Done".</div>
|
|
91
|
+
</div>
|
|
92
|
+
<div class='items'>
|
|
93
|
+
<% items.forEach((item) => { %>
|
|
94
|
+
<div class='item'>
|
|
95
|
+
<img src="<%=item.icon%>">
|
|
96
|
+
<div class='col'>
|
|
97
|
+
<div class='title'><%=item.title%></div>
|
|
98
|
+
<div class='description'><%=item.description%></div>
|
|
99
|
+
</div>
|
|
100
|
+
<a target="_blank" features="browser" href='<%=item.href%>' class='btn'>Get</a>
|
|
101
|
+
</div>
|
|
102
|
+
<% }) %>
|
|
103
|
+
</div>
|
|
104
|
+
<footer>
|
|
105
|
+
<a class='btn' href="/env/api/<%=name%>?init=true">Done</a>
|
|
106
|
+
</footer>
|
|
107
|
+
</main>
|
|
108
|
+
</body>
|
|
109
|
+
</html>
|
|
@@ -302,7 +302,7 @@ hr {
|
|
|
302
302
|
<br>
|
|
303
303
|
<div>Edit the system ENVIRONMENT file to customize pinokio system wide settings, such as where files are stored, terminal behavior, etc.</div>
|
|
304
304
|
<br>
|
|
305
|
-
<a class='btn' href="/env
|
|
305
|
+
<a class='btn' href="/env"><i class="fa-regular fa-pen-to-square"></i> Edit ENVIRONMENT</a>
|
|
306
306
|
</div>
|
|
307
307
|
<hr/>
|
|
308
308
|
<div class='troubleshoot'>
|
|
@@ -164,7 +164,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
164
164
|
"Content-Type": "application/json"
|
|
165
165
|
},
|
|
166
166
|
body: JSON.stringify({
|
|
167
|
-
filepath: "api/" + location.pathname.slice(7)
|
|
167
|
+
filepath: "api/" + location.pathname.slice(7),
|
|
168
168
|
vals
|
|
169
169
|
})
|
|
170
170
|
}).then((res) => {
|
|
@@ -194,6 +194,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
194
194
|
<li>This will let you share your local Pinokio app to the public internet, which anyone with a URL can access.</li>
|
|
195
195
|
<li>Powered by <a href="https://www.cloudflare.com/products/tunnel/" target="_blank" features="browser">Cloudflare Tunnel</a></li>
|
|
196
196
|
<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>
|
|
197
198
|
</ul>
|
|
198
199
|
<% cloudflare_links.forEach((link) => { %>
|
|
199
200
|
<div class='shared'>
|