@naanlang/naan 1.0.1 → 1.0.4
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/LICENSE.md +1 -1
- package/README.md +5 -5
- package/bin/index.js +18 -15
- package/dist/env_web.js +146 -16
- package/dist/naan.min.js +5 -5
- package/frameworks/browser/sworker.js +299 -91
- package/frameworks/browser/terminals.nlg +23 -10
- package/frameworks/browser/workers.nlg +26 -12
- package/frameworks/client/apiclient.nlg +17 -4
- package/frameworks/client/psm_client.nlg +178 -81
- package/frameworks/common/common.nlg +14 -4
- package/frameworks/node/apiserver.nlg +31 -15
- package/frameworks/node/filesystem.nlg +230 -46
- package/frameworks/node/gitter.nlg +27 -6
- package/frameworks/node/psm_server.nlg +113 -37
- package/frameworks/project/build.nlg +40 -23
- package/frameworks/project/projects.nlg +50 -27
- package/frameworks/running/debugnub.nlg +2 -5
- package/frameworks/storage/csv.nlg +120 -0
- package/frameworks/storage/dbt_pouch.nlg +41 -25
- package/frameworks/storage/psm.nlg +108 -28
- package/frameworks/storage/psm_dbtables.nlg +7 -3
- package/frameworks/storage/resources.nlg +30 -2
- package/lib/browser/env_web.js +148 -18
- package/lib/browser/env_webworker.js +1 -1
- package/lib/browser/require.js +1 -1
- package/lib/core/naanlib.js +5 -5
- package/lib/env_node.js +9 -1
- package/package.json +2 -1
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +1 -1
- package/plugins/serviceAws/aws_dynamo.nlg +625 -141
- package/plugins/serviceAws/aws_dynextra.nlg +294 -0
- package/plugins/serviceAws/dbt_aws.nlg +31 -11
- package/plugins/serviceAws/psm_aws.nlg +42 -26
- package/plugins/serviceAws/serviceAws.nlg +1 -0
- package/plugins/serviceGitHub/psm_github.nlg +41 -25
- package/plugins/serviceGitLab/psm_gitlab.nlg +41 -25
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2017-
|
|
9
|
+
* Copyright (c) 2017-2022 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -20,9 +20,10 @@
|
|
|
20
20
|
*
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
closure MakeNideAPIclient(url, local client) {
|
|
23
|
+
closure MakeNideAPIclient(url, guid, local client) {
|
|
24
24
|
client = new(object, this)
|
|
25
25
|
client.url = url
|
|
26
|
+
client.guid = guid
|
|
26
27
|
client.notifyAfter = 0
|
|
27
28
|
|
|
28
29
|
// clientRequest() - general request routine
|
|
@@ -51,6 +52,8 @@ closure MakeNideAPIclient(url, local client) {
|
|
|
51
52
|
request.responseType = options.responseType
|
|
52
53
|
if options.contentType
|
|
53
54
|
request.setRequestHeader("Content-Type", options.contentType)
|
|
55
|
+
if guid
|
|
56
|
+
request.setRequestHeader("x-naanlang-api-guid", guid)
|
|
54
57
|
request.send(options.putdata)
|
|
55
58
|
}
|
|
56
59
|
|
|
@@ -117,7 +120,7 @@ closure MakeNideAPIclient(url, local client) {
|
|
|
117
120
|
// Otherwise if communication failed then the result is:
|
|
118
121
|
// callback(<error>).
|
|
119
122
|
|
|
120
|
-
client.psmRemote = closure psmRemote(options, data, callback, local query, method, reqOptions) {
|
|
123
|
+
client.psmRemote = closure psmRemote(options, data, callback, local query, method, reqOptions, error) {
|
|
121
124
|
query = EncodeQuery("/psm/?", options)
|
|
122
125
|
if data
|
|
123
126
|
method = "PUT"
|
|
@@ -131,6 +134,16 @@ closure MakeNideAPIclient(url, local client) {
|
|
|
131
134
|
reqOptions.contentType = "application/octet-stream" // we are sending binary data
|
|
132
135
|
else
|
|
133
136
|
reqOptions.responseType = "arraybuffer" // we are requesting binary data
|
|
137
|
+
} else if options.encoding == "json" {
|
|
138
|
+
if data {
|
|
139
|
+
reqOptions.contentType = "application/json;charset=UTF-8" // we are sending JSON
|
|
140
|
+
`(error, data) = JsonStringify(data)
|
|
141
|
+
if error
|
|
142
|
+
return (callback(Error("psmRemote failed:", error)))
|
|
143
|
+
reqOptions.putdata = data
|
|
144
|
+
}
|
|
145
|
+
else
|
|
146
|
+
reqOptions.responseType = "application/json" // we are requesting JSON
|
|
134
147
|
}
|
|
135
148
|
clientRequest(method, url.concat(query), reqOptions, function (error, req, event, local data) {
|
|
136
149
|
if !error && req.status != 200
|
|
@@ -172,7 +185,7 @@ closure MakeNideAPIclient(url, local client) {
|
|
|
172
185
|
|
|
173
186
|
function apicInit(local manifest) {
|
|
174
187
|
|
|
175
|
-
manifest = `(
|
|
188
|
+
manifest = `(MakeNideAPIclient, apicInit)
|
|
176
189
|
|
|
177
190
|
Naan.module.build(module.id, "apiclient", function(modobj, compobj) {
|
|
178
191
|
Naan.module.require("./client.nlg")
|
|
@@ -22,6 +22,44 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
|
|
|
22
22
|
view = new(object, this)
|
|
23
23
|
view.api = api
|
|
24
24
|
view.rootpath = rootpath
|
|
25
|
+
|
|
26
|
+
// data
|
|
27
|
+
//
|
|
28
|
+
// Return the filesystem data, i.e. the tree data field. The minimum information is:
|
|
29
|
+
// platform -- e.g. "darwin" or "win32"
|
|
30
|
+
// semantics -- e.g. "win32" or "posix"
|
|
31
|
+
// pathsep -- e.g. / or \
|
|
32
|
+
|
|
33
|
+
view.data = closure data(callback) {
|
|
34
|
+
if !callback
|
|
35
|
+
return (syncAdapter(data))
|
|
36
|
+
params = {
|
|
37
|
+
path: ""
|
|
38
|
+
op: "tree"
|
|
39
|
+
depthlimit: 0
|
|
40
|
+
}
|
|
41
|
+
api.psmRemote(params, false, function(error, tree) {
|
|
42
|
+
view.fsdata = tree.data
|
|
43
|
+
callback(error, tree.data)
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// pathInit
|
|
48
|
+
//
|
|
49
|
+
// Initialize our path module for the applicable path type.
|
|
50
|
+
|
|
51
|
+
closure pathInit(local result) {
|
|
52
|
+
result = view.data()
|
|
53
|
+
if result.0
|
|
54
|
+
pathmod = false
|
|
55
|
+
else {
|
|
56
|
+
if view.fsdata.pathsep == "\\"
|
|
57
|
+
pathmod = JSpath.win32
|
|
58
|
+
else
|
|
59
|
+
pathmod = JSpath.posix
|
|
60
|
+
false
|
|
61
|
+
}
|
|
62
|
+
}
|
|
25
63
|
|
|
26
64
|
// psmRemote
|
|
27
65
|
//
|
|
@@ -30,10 +68,14 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
|
|
|
30
68
|
closure psmRemote(path, op, options, putdata, callback, local params, item) {
|
|
31
69
|
if !callback
|
|
32
70
|
return (syncAdapter(psmRemote, path, op, options, putdata))
|
|
71
|
+
if !pathmod && pathInit()
|
|
72
|
+
return // failed to initialize path module
|
|
33
73
|
if !string(path)
|
|
34
74
|
return (asyncResult(callback, Error("invalid path:", typeof(path))))
|
|
75
|
+
if rootpath != ""
|
|
76
|
+
path = pathmod.resolve(rootpath, path)
|
|
35
77
|
params = {
|
|
36
|
-
path:
|
|
78
|
+
path: path
|
|
37
79
|
op: op
|
|
38
80
|
}
|
|
39
81
|
for item in options
|
|
@@ -59,38 +101,30 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
|
|
|
59
101
|
|
|
60
102
|
// path
|
|
61
103
|
//
|
|
62
|
-
//
|
|
104
|
+
// Redirect path calls to module chosen for filesystem semantics. It is possible for this to fail
|
|
105
|
+
// on first connection to the filesystem, in which case we return false for the path result.
|
|
106
|
+
// Throwing an error would complicate our callers and testing. This way there's a small chance
|
|
107
|
+
// that a transient error will give a bad path, but that will be detected downstream.
|
|
63
108
|
|
|
64
109
|
view.path = new(object, this)
|
|
65
|
-
view.path[".undefined"] =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if error
|
|
70
|
-
return (list(error))
|
|
71
|
-
if info.pathsep == "\\"
|
|
72
|
-
pathmod = JSpath.win32
|
|
73
|
-
else
|
|
74
|
-
pathmod = JSpath.posix
|
|
75
|
-
}
|
|
76
|
-
xapply(pathmod, selector, arglist)
|
|
77
|
-
} (args.0, args.-1)
|
|
110
|
+
view.path[".undefined"] = function path args {
|
|
111
|
+
if !pathmod
|
|
112
|
+
pathInit()
|
|
113
|
+
xapply(pathmod, args.0, args.-1)
|
|
78
114
|
}
|
|
79
115
|
|
|
80
|
-
//
|
|
116
|
+
// folderPath
|
|
81
117
|
//
|
|
82
|
-
// Return the
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
callback(error, tree.data)
|
|
93
|
-
})
|
|
118
|
+
// Return the path of the specified special folder:
|
|
119
|
+
// "HomeDir"
|
|
120
|
+
// "TempDir"
|
|
121
|
+
// "PackageDir"
|
|
122
|
+
// The returned path is relative to our root if within it, or absolute otherwise.
|
|
123
|
+
|
|
124
|
+
view.folderPath = function folderPath(folderID, callback) {
|
|
125
|
+
psmRemote("", "folderPath", {
|
|
126
|
+
folderid: folderID
|
|
127
|
+
}, false, callback)
|
|
94
128
|
}
|
|
95
129
|
|
|
96
130
|
// readLines
|
|
@@ -123,7 +157,28 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
|
|
|
123
157
|
view.writeFile = function writeFile(path, data, options, callback) {
|
|
124
158
|
psmRemote(path, "writeFile", options, data, callback)
|
|
125
159
|
}
|
|
126
|
-
|
|
160
|
+
|
|
161
|
+
// readlink
|
|
162
|
+
//
|
|
163
|
+
// Read the contents of a symbolic link at the specified path
|
|
164
|
+
|
|
165
|
+
view.readlink = function readlink(path, callback) {
|
|
166
|
+
psmRemote(path, "readlink", { }, false, callback)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// symlink
|
|
170
|
+
//
|
|
171
|
+
// Create a symbolic link path pointing to (containing the string) target
|
|
172
|
+
|
|
173
|
+
view.symlink = function symlink(target, path, options, callback) {
|
|
174
|
+
if options
|
|
175
|
+
options = new(options)
|
|
176
|
+
else
|
|
177
|
+
options = { }
|
|
178
|
+
options.target = target
|
|
179
|
+
psmRemote(path, "symlink", options, false, callback)
|
|
180
|
+
}
|
|
181
|
+
|
|
127
182
|
// delete
|
|
128
183
|
//
|
|
129
184
|
// Delete a file or directory at the specified path
|
|
@@ -134,12 +189,45 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
|
|
|
134
189
|
|
|
135
190
|
// info
|
|
136
191
|
//
|
|
137
|
-
// Get file info at the specified path
|
|
192
|
+
// Get file or directory info at the specified path
|
|
193
|
+
|
|
194
|
+
view.info = function info(path, options, callback) {
|
|
195
|
+
if !options
|
|
196
|
+
options = { }
|
|
197
|
+
psmRemote(path, "info", options, false, callback)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// setInfo
|
|
201
|
+
//
|
|
202
|
+
// Set file or directory info obtained from info() call.
|
|
138
203
|
|
|
139
|
-
view.
|
|
140
|
-
psmRemote(path, "
|
|
204
|
+
view.setInfo = function setInfo(path, info, callback) {
|
|
205
|
+
psmRemote(path, "setInfo", {
|
|
206
|
+
encoding: "json"
|
|
207
|
+
}, info, callback)
|
|
141
208
|
}
|
|
142
209
|
|
|
210
|
+
// setOwnerGroup
|
|
211
|
+
//
|
|
212
|
+
// Set file or directory owner or group at the specified path
|
|
213
|
+
|
|
214
|
+
view.setOwnerGroup = function setOwnerGroup(path, uid, gid, callback) {
|
|
215
|
+
psmRemote(path, "setOwnerGroup", {
|
|
216
|
+
uid: uid
|
|
217
|
+
gid: gid
|
|
218
|
+
}, false, callback)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// setMode
|
|
222
|
+
//
|
|
223
|
+
// Set file or directory mode at the specified path
|
|
224
|
+
|
|
225
|
+
view.setMode = function setMode(path, mode, callback) {
|
|
226
|
+
psmRemote(path, "setMode", {
|
|
227
|
+
mode: mode
|
|
228
|
+
}, false, callback)
|
|
229
|
+
}
|
|
230
|
+
|
|
143
231
|
// setTimes
|
|
144
232
|
//
|
|
145
233
|
// Set file modify and access times at the specified path
|
|
@@ -157,7 +245,7 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
|
|
|
157
245
|
|
|
158
246
|
view.dirList = function dirList(path, options, callback) {
|
|
159
247
|
psmRemote(path, "dirList", {
|
|
160
|
-
|
|
248
|
+
stat: options.stat
|
|
161
249
|
}, false, callback)
|
|
162
250
|
}
|
|
163
251
|
|
|
@@ -167,7 +255,7 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
|
|
|
167
255
|
|
|
168
256
|
view.readir = function readir(path, callback) {
|
|
169
257
|
psmRemote(path, "dirList", {
|
|
170
|
-
|
|
258
|
+
stat: true
|
|
171
259
|
}, false, callback)
|
|
172
260
|
}
|
|
173
261
|
|
|
@@ -209,7 +297,9 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
|
|
|
209
297
|
// delete the source after the copy.
|
|
210
298
|
|
|
211
299
|
view.copy = closure copy(srcpath, dest, options, callback, local parameters) {
|
|
212
|
-
if
|
|
300
|
+
if options
|
|
301
|
+
options = new(options)
|
|
302
|
+
else
|
|
213
303
|
options = { }
|
|
214
304
|
options.destination = JSpath.resolve(rootpath, dest)
|
|
215
305
|
psmRemote(srcpath, "copy", options, false, callback)
|
|
@@ -325,7 +415,7 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
|
|
|
325
415
|
connector.psm = psm
|
|
326
416
|
connector.api = nicc.api
|
|
327
417
|
connector.vault = psm.vault(connClassID)
|
|
328
|
-
connector.label = "Naan
|
|
418
|
+
connector.label = "Naan-Server"
|
|
329
419
|
watch = psm.watchable(connector)
|
|
330
420
|
|
|
331
421
|
// uifields
|
|
@@ -337,57 +427,44 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
|
|
|
337
427
|
placeholder: "http://host.com/endpoint"
|
|
338
428
|
key: "urlName" },
|
|
339
429
|
{
|
|
340
|
-
label: "Auth
|
|
341
|
-
placeholder: "
|
|
342
|
-
key: "
|
|
343
|
-
{
|
|
344
|
-
label: "Auth Password"
|
|
345
|
-
placeholder: "account password"
|
|
346
|
-
key: "authPW"
|
|
347
|
-
type: "password" },
|
|
430
|
+
label: "Auth Secret"
|
|
431
|
+
placeholder: "guid"
|
|
432
|
+
key: "authSecret" }
|
|
348
433
|
]
|
|
349
434
|
}
|
|
350
|
-
|
|
351
|
-
// hashResourceID
|
|
352
|
-
//
|
|
353
|
-
// Make a stable hash based on the resource credentials that we can persist or share between
|
|
354
|
-
// different domains accessing the same resource.
|
|
355
|
-
connector.hashResourceID = function hashResourceID(resource, local ident) {
|
|
356
|
-
ident = resource.urlName
|
|
357
|
-
if resource.label
|
|
358
|
-
ident = ident.concat(resource.label)
|
|
359
|
-
HashSHA256(ident)
|
|
360
|
-
}
|
|
361
435
|
|
|
362
436
|
// findResource
|
|
363
437
|
//
|
|
364
|
-
// Find a resource with the specified contents, returning the resID or false.
|
|
365
|
-
// is: if added, would the specified contents be redundant to an existing resource?
|
|
438
|
+
// Find a resource with the specified contents, returning the resID or false.
|
|
366
439
|
connector.findResource = function findResource(resource, local resID, creds) {
|
|
367
440
|
for resID in listResources().1 {
|
|
368
441
|
creds = access(resID).1
|
|
369
|
-
if resource.urlName == creds.urlName
|
|
370
|
-
&& (!resource.label || resource.label == creds.label)
|
|
371
|
-
&& resource.authName == creds.authName
|
|
442
|
+
if ((!resource.urlName || resource.urlName == creds.urlName)
|
|
443
|
+
&& (!resource.label || resource.label == creds.label))
|
|
372
444
|
return (resID)
|
|
373
445
|
}
|
|
374
446
|
false
|
|
375
447
|
}
|
|
376
448
|
|
|
377
|
-
|
|
449
|
+
|
|
450
|
+
// writeResource
|
|
378
451
|
//
|
|
379
|
-
// Add credentials for a named resource.
|
|
452
|
+
// Add or update credentials for a named resource. If no resID is specified this adds a new
|
|
453
|
+
// resource; otherwise it updates an existing one. The resource is a dictionary comprising these
|
|
454
|
+
// keys:
|
|
380
455
|
// label - [optional] label we use for this resource
|
|
381
456
|
// urlName - URL (e.g. "http://host.com/node/fsapi")
|
|
382
|
-
//
|
|
383
|
-
// authPW - [optional] the autentication password, required if authname is provided
|
|
457
|
+
// authSecret - access-control token for this URL
|
|
384
458
|
// hidden - [optional] don't enumerate to user
|
|
385
459
|
// locked - [optional] don't allow user delete
|
|
386
460
|
//
|
|
387
|
-
// The return value is (error, resID) tuple. Error, if non-false, is a dictionary of field
|
|
461
|
+
// The return value is an (error, resID) tuple. Error, if non-false, is a dictionary of field
|
|
388
462
|
// keys and error diagnostic strings.
|
|
389
|
-
connector.
|
|
463
|
+
connector.writeResource = function writeResource(resource, resID,
|
|
464
|
+
local creds, errors, error, data, change) {
|
|
465
|
+
// badField - test for a bad string
|
|
390
466
|
function badField(str) { !string(str) || str.trim().length == 0 }
|
|
467
|
+
|
|
391
468
|
creds = { urlName: resource.urlName }
|
|
392
469
|
errors = { }
|
|
393
470
|
if resource.label
|
|
@@ -396,13 +473,13 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
|
|
|
396
473
|
creds.label = resource.urlName
|
|
397
474
|
if badField(creds.label)
|
|
398
475
|
errors.label = "invalid label"
|
|
476
|
+
else if !resID && findResource({ label: creds.label })
|
|
477
|
+
errors.label = "duplicate name"
|
|
399
478
|
if badField(resource.urlName)
|
|
400
479
|
errors.urlName = "required field"
|
|
401
|
-
if
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
if badField(resource.authPW)
|
|
405
|
-
errors.authPW = "required if Auth Name specified" }
|
|
480
|
+
if badField(resource.authSecret)
|
|
481
|
+
errors.authSecret = "required field"
|
|
482
|
+
creds.authSecret = resource.authSecret
|
|
406
483
|
if length(errors) > 0
|
|
407
484
|
error = errors
|
|
408
485
|
else {
|
|
@@ -410,14 +487,36 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
|
|
|
410
487
|
creds.hidden = resource.hidden
|
|
411
488
|
if resource.locked
|
|
412
489
|
creds.locked = resource.locked
|
|
413
|
-
resID
|
|
414
|
-
|
|
490
|
+
if resID {
|
|
491
|
+
`(error, data) = connector.vault.updateResource(resID, creds)
|
|
492
|
+
change = { changed: [resID] }
|
|
493
|
+
} else {
|
|
494
|
+
resID = UUID()
|
|
495
|
+
`(error, data) = connector.vault.addResource(resID, creds)
|
|
496
|
+
change = { added: [resID] }
|
|
497
|
+
}
|
|
415
498
|
if data {
|
|
416
|
-
watch.notify(connClassID,
|
|
417
|
-
data = resID
|
|
499
|
+
watch.notify(connClassID, change)
|
|
500
|
+
data = resID
|
|
501
|
+
}
|
|
502
|
+
}
|
|
418
503
|
list(error, data)
|
|
419
504
|
}
|
|
420
|
-
|
|
505
|
+
|
|
506
|
+
// addResource
|
|
507
|
+
//
|
|
508
|
+
// Add a new resource, returning a standard error tuple per writeResource.
|
|
509
|
+
connector.addResource = function addResource(resource) {
|
|
510
|
+
writeResource(resource)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// updateResource
|
|
514
|
+
//
|
|
515
|
+
// Update an existing resource, returning a standard error tuple per writeResource.
|
|
516
|
+
connector.updateResource = function updateResource(resID, resource) {
|
|
517
|
+
writeResource(resource, resID)
|
|
518
|
+
}
|
|
519
|
+
|
|
421
520
|
// getResource
|
|
422
521
|
//
|
|
423
522
|
// Get credentials for a named resource, matching the dictionary semantics of addResource. The
|
|
@@ -432,10 +531,8 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
|
|
|
432
531
|
}
|
|
433
532
|
if string(creds.urlName)
|
|
434
533
|
resource.urlName = creds.urlName
|
|
435
|
-
if string(creds.
|
|
436
|
-
resource.
|
|
437
|
-
if string(creds.authPW)
|
|
438
|
-
resource.authPW = creds.authPW
|
|
534
|
+
if string(creds.authSecret)
|
|
535
|
+
resource.authSecret = creds.authSecret
|
|
439
536
|
list(false, resource)
|
|
440
537
|
}
|
|
441
538
|
|
|
@@ -510,7 +607,7 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
|
|
|
510
607
|
if creds == "HostFS"
|
|
511
608
|
api = connector.api
|
|
512
609
|
else
|
|
513
|
-
|
|
610
|
+
api = MakeNideAPIclient(creds.urlName, creds.authSecret) // API to remote NodeJS filesystem
|
|
514
611
|
}
|
|
515
612
|
if error
|
|
516
613
|
list(error)
|
|
@@ -66,7 +66,7 @@ function JsonParse(data) {
|
|
|
66
66
|
list(false, data)
|
|
67
67
|
} catch {
|
|
68
68
|
if true
|
|
69
|
-
|
|
69
|
+
list(exception)
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
72
|
|
|
@@ -84,7 +84,7 @@ function JsonStringify(data) {
|
|
|
84
84
|
list(false, data)
|
|
85
85
|
} catch {
|
|
86
86
|
if true
|
|
87
|
-
|
|
87
|
+
list(exception)
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
90
|
|
|
@@ -256,6 +256,8 @@ TextEncoder = false;
|
|
|
256
256
|
TextDecoder = false;
|
|
257
257
|
EncodeURIComponent = false;
|
|
258
258
|
DecodeURIComponent = false;
|
|
259
|
+
EncodeBase64 = false;
|
|
260
|
+
DecodeBase64 = false;
|
|
259
261
|
JSONstringify = false;
|
|
260
262
|
JSONparse = false;
|
|
261
263
|
|
|
@@ -289,6 +291,10 @@ function comrInit(local manifest) {
|
|
|
289
291
|
EncodeURIComponent = wsg.encodeURIComponent
|
|
290
292
|
DecodeURIComponent = wsg.decodeURIComponent
|
|
291
293
|
//
|
|
294
|
+
// everywhere: Base65
|
|
295
|
+
EncodeBase64 = wsg.btoa
|
|
296
|
+
DecodeBase64 = wsg.atob
|
|
297
|
+
//
|
|
292
298
|
// everywhere: JSON
|
|
293
299
|
JSONstringify = wsg.JSON.stringify
|
|
294
300
|
JSONparse = wsg.JSON.parse
|
|
@@ -338,7 +344,7 @@ function comrInit(local manifest) {
|
|
|
338
344
|
//
|
|
339
345
|
DecodeBase64url = function decodeBase64url(base64url, local padding) {
|
|
340
346
|
base64url = base64url.replace(RegExp("-", "g"), '+').replace(RegExp("_", "g"), '/')
|
|
341
|
-
|
|
347
|
+
DecodeBase64(base64url)
|
|
342
348
|
}
|
|
343
349
|
//
|
|
344
350
|
// For browser windows and web workers
|
|
@@ -358,7 +364,7 @@ function comrInit(local manifest) {
|
|
|
358
364
|
//
|
|
359
365
|
// HashMD5 in base64
|
|
360
366
|
HashMD5_base64 = function browserMD5_base64(data) {
|
|
361
|
-
|
|
367
|
+
EncodeBase64(browserMD5(data, true)) }
|
|
362
368
|
//
|
|
363
369
|
// browserDigest - only in secure contexts (localhost and HTTPS)
|
|
364
370
|
//
|
|
@@ -431,6 +437,8 @@ function comrInit(local manifest) {
|
|
|
431
437
|
HashMD5 =
|
|
432
438
|
HashMD5_base64 =
|
|
433
439
|
HashSHA1 =
|
|
440
|
+
HashSHA256 =
|
|
441
|
+
HashSHA256_base64 =
|
|
434
442
|
RandomBytes = function() { throw(Error("crypto functions unavailable")) }
|
|
435
443
|
}
|
|
436
444
|
}
|
|
@@ -442,6 +450,8 @@ function comrInit(local manifest) {
|
|
|
442
450
|
modobj.exports.TextDecoder = TextDecoder
|
|
443
451
|
modobj.exports.EncodeURIComponent = EncodeURIComponent
|
|
444
452
|
modobj.exports.DecodeURIComponent = DecodeURIComponent
|
|
453
|
+
modobj.exports.EncodeBase64 = EncodeBase64
|
|
454
|
+
modobj.exports.DecodeBase64 = DecodeBase64
|
|
445
455
|
modobj.exports.JSONstringify = JSONstringify
|
|
446
456
|
modobj.exports.JSONparse = JSONparse
|
|
447
457
|
modobj.exports.JsonStringify = JsonStringify
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2017-
|
|
9
|
+
* Copyright (c) 2017-2022 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -36,6 +36,7 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
36
36
|
server.psm = require("./psm_server.nlg").MakeServerPSM(server)
|
|
37
37
|
server.root = chroot
|
|
38
38
|
server.app = express()
|
|
39
|
+
server.guid = options.guid
|
|
39
40
|
server.notifyq = []
|
|
40
41
|
server.responseq = []
|
|
41
42
|
server.projects = { }
|
|
@@ -99,8 +100,9 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
99
100
|
// body parsing
|
|
100
101
|
//
|
|
101
102
|
|
|
102
|
-
server.app.use(bodyParser.
|
|
103
|
-
server.app.use(bodyParser.
|
|
103
|
+
server.app.use(bodyParser.json({limit: "5mb"}))
|
|
104
|
+
server.app.use(bodyParser.text({limit: "25mb"}))
|
|
105
|
+
server.app.use(bodyParser.raw({limit: "25mb"}))
|
|
104
106
|
|
|
105
107
|
//
|
|
106
108
|
// Express.js endpoints
|
|
@@ -108,8 +110,12 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
108
110
|
|
|
109
111
|
server.app.use(function(req,res,next) {
|
|
110
112
|
res.header("Access-Control-Allow-Origin", "*")
|
|
111
|
-
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
|
|
112
|
-
|
|
113
|
+
res.header("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS")
|
|
114
|
+
res.header("Access-Control-Allow-Headers", "x-naanlang-api-guid")
|
|
115
|
+
if req.method == "OPTIONS"
|
|
116
|
+
res.sendStatus(200)
|
|
117
|
+
else
|
|
118
|
+
next()
|
|
113
119
|
})
|
|
114
120
|
|
|
115
121
|
server.app.use("", express.static(chroot))
|
|
@@ -121,15 +127,18 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
121
127
|
// "/"
|
|
122
128
|
|
|
123
129
|
server.app.get("/", function(req, res) {
|
|
124
|
-
res.send("<u>
|
|
130
|
+
res.send("<u>naan-server API</u><br>\r\n"
|
|
125
131
|
" /readfile?path=<i>filepath</i><br>\r\n"
|
|
126
132
|
" /status?timeout=<i>msec</i>&after=<i>stamp</i><br>\r\n")
|
|
127
133
|
})
|
|
128
134
|
|
|
129
135
|
// "/readfile"
|
|
130
136
|
|
|
131
|
-
server.app.get("/readfile", closure(req, res) {
|
|
132
|
-
|
|
137
|
+
server.app.get("/readfile", closure(req, res, local guid) {
|
|
138
|
+
guid = req.get("x-naanlang-api-guid")
|
|
139
|
+
if server.guid && guid != server.guid
|
|
140
|
+
res.status(403).send("unauthorized")
|
|
141
|
+
else if not string(req.query.path)
|
|
133
142
|
res.status(400).send("Invalid filename")
|
|
134
143
|
else if RegExp("^\\.\\.[/\\\\]|[/\\\\]\\.\\.$|[/\\\\]\\.\\.[/\\\\]").test(req.query.path)
|
|
135
144
|
res.status(403).send("\"Parent path references forbidden\"")
|
|
@@ -156,7 +165,10 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
156
165
|
|
|
157
166
|
// "/status"
|
|
158
167
|
|
|
159
|
-
server.app.get("/status", closure(req,res, local timeoutms, after, responder) {
|
|
168
|
+
server.app.get("/status", closure(req, res, local guid, timeoutms, after, responder) {
|
|
169
|
+
guid = req.get("x-naanlang-api-guid")
|
|
170
|
+
if server.guid && guid != server.guid
|
|
171
|
+
res.status(403).send("unauthorized")
|
|
160
172
|
timeoutms = toint(req.query.timeout)
|
|
161
173
|
after = toint(req.query.after)
|
|
162
174
|
if not integer(timeoutms) or timeoutms > 10000
|
|
@@ -187,8 +199,11 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
187
199
|
|
|
188
200
|
// "/psm"
|
|
189
201
|
|
|
190
|
-
server.app.all("/psm", closure(req, res) {
|
|
191
|
-
|
|
202
|
+
server.app.all("/psm", closure(req, res, local guid) {
|
|
203
|
+
guid = req.get("x-naanlang-api-guid")
|
|
204
|
+
if server.guid && guid != server.guid && req.method !== "OPTIONS"
|
|
205
|
+
res.status(403).send("unauthorized")
|
|
206
|
+
else if !server.hostfs
|
|
192
207
|
res.status(503).send("host filesystem not available")
|
|
193
208
|
else
|
|
194
209
|
server.hostfs.api(req, res)
|
|
@@ -227,7 +242,6 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
227
242
|
server.ws = WebSocketServer(server)
|
|
228
243
|
server.port = port
|
|
229
244
|
callback(false, port, server) // notify of success
|
|
230
|
-
debuglog("server open on port", port)
|
|
231
245
|
result = server.psm.conns.NodeFS.connect("Host", "NideFS", list("")) // ### this should go through resource tracker
|
|
232
246
|
if result.0
|
|
233
247
|
ErrorDebuglog(Error("can't access HostFS", result.0))
|
|
@@ -264,7 +278,7 @@ closure WebSocketServer(server, local websocks) {
|
|
|
264
278
|
// Event handlers
|
|
265
279
|
|
|
266
280
|
websocks.wss.on("connection", function(socket, request) {
|
|
267
|
-
websocks.conns.push(WesCon(websocks, socket, request))
|
|
281
|
+
websocks.conns.push(WesCon(server, websocks, socket, request))
|
|
268
282
|
})
|
|
269
283
|
|
|
270
284
|
server.wss.on("error", function(error) {
|
|
@@ -303,7 +317,7 @@ closure WebSocketServer(server, local websocks) {
|
|
|
303
317
|
*
|
|
304
318
|
*/
|
|
305
319
|
|
|
306
|
-
closure WesCon(websocks, socket, request, local wescon) {
|
|
320
|
+
closure WesCon(server, websocks, socket, request, local wescon) {
|
|
307
321
|
wescon = new(object, this)
|
|
308
322
|
wescon.socket = socket
|
|
309
323
|
wescon.serverIDs = []
|
|
@@ -315,7 +329,9 @@ closure WesCon(websocks, socket, request, local wescon) {
|
|
|
315
329
|
socket.on("message", function onmessage(text, local message, error) {
|
|
316
330
|
try {
|
|
317
331
|
message = new(js.g.JSON.parse(text))
|
|
318
|
-
if
|
|
332
|
+
if server.guid && message.guid != server.guid
|
|
333
|
+
error = Error("unauthorized")
|
|
334
|
+
else if message.clientID && websocks.subsystems[message.serverID] {
|
|
319
335
|
wescon.serverIDs.push(message.serverID) // ### track subsystems for closing
|
|
320
336
|
websocks.subsystems[message.serverID].wsReceive(message, wescon)
|
|
321
337
|
}
|