8101311dc5c503bac2eb7b6b0c5cd1971b8772af
2 * @file Strapp File System
3 * @author Jordan Lavatai, Ken Grimes
6 * @copyright August 2017 - Ken Grimes, Jordan Lavatai
7 * @summmary File system implementation for a strapp node
9 import strapp
from './strapp.js'
10 import localforage
from 'localforage'
11 localforage
.configure({
15 /** extend localforage with an assign operation */
16 localforage
.assignItem
= (path
, data
) => new Promise((resolve
, reject
) => {
17 localforage
.getItem(path
, data
).then((fdata
) => {
18 localforage
.setItem(path
, Object
.assign(fdata
, data
))
19 .then(resolve
).catch(reject
)
21 localforage
.setItem(path
, data
)
22 .then(resolve
).catch(reject
)
25 localforage
.getOrSetItem
= (path
, defaults
) => new Promise((resolve
, reject
) => localforage
.getItem(path
).then(resolve
).catch((err
) => localforage
.setItem(path
, defaults
).then(resolve
).catch(reject
)))
28 const strappRuntime
= {
29 run
: new StrappDirectory({
32 keyboard
: new StrappDevice(),
33 mouse
: new StrappDevice(),
34 touch
: new StrappDevice(),
35 client
: new StrappDirectory({
38 create
: new StrappFile({
40 POST(){} //TODO: create client action
44 relay
: new StrappDirectory({
47 create
: new StrappFile({
49 POST(){} //TODO: create relay
53 spc
: new StrappDirectory({
56 create
: new StrappFile({
58 POST(){} //TODO: spc creation (probably only internal)
66 const StrappFileSystem
= (() => {
69 const rootDir
= new StrappDirectory(StrappFile
.literal({
70 type
: 'strapp/directory',
75 /* Internal Functions */
76 const _genKeyPair
= () => {
83 const _request
= (location
, method
, data
) =>
84 rootDir
.request(location
.split('/'), method
, rootUser
.pubKey
, data
)
87 const StrappFileSystem
= {
88 request
: (location
, method
, data
) =>
89 new Promise((resolve
, reject
) => StrappFileSystem
.loadWaiters
.push([
90 location
, method
, data
, resolve
, reject
92 get: (location
) => StrappFileSystem
.request(location
, 'GET'),
93 set: (location
, data
) => StrappFileSystem
.request(location
, 'PUT', data
),
94 resolveRootUser
: (path
) =>
95 localforage
.getItem(path
)
96 .then((data
) => Promise
.resolve
,
97 (err
) => localforage
.setItem(path
, _genKeyPair()))
98 .then((data
) => Promise
.resolve(rootUser
= data
))
100 StrappFileSystem
.loadWaiters
= []
101 StrappFileSystem
.bootTime
= new Date()
104 StrappFileSystem
.resolveRootUser('acct/local')
105 .then((data
) => Promise
.all(
108 acct
: StrappDirectory
.literal()
111 local
: StrappFile
.literal()
113 ].map((entry
) => localforage
.getOrSetItem(entry
[0],entry
[1]))
115 .then((loadedFiles
) => {
116 rootDir
.loadFiles(loadedFiles
[0])
117 StrappFileSystem
.request
= _request
118 StrappFileSystem
.loadWaiters
119 .map((w
) => _request(w
[0], w
[1], w
[2], w
[3]).then(w
[4], w
[5]))
120 delete StrappFileSystem
.loadWaiters
123 return StrappFileSystem
126 const StrappFile
= (() => {
127 class StrappFile
extends Object
{
128 constructor(...props
) {
130 return Object
.assign(this, new.target
.defaults
, ...props
)
132 static literal(...props
) {
133 return Object
.assign(Object
.create(null, StrappFile
.defaults
), ...props
)
135 static parse(fileLiteral
) {
136 switch(fileLiteral
.type
) {
137 case 'application/JSON':
138 return new StrappJSON(fileLiteral
)
139 case 'strapp/directory':
140 return new StrappDirectory(fileLiteral
)
143 return new StrappFile(fileLiteral
)
146 resolveRequest(method
, pubKey
, data
, locStack
) {
148 return Promise
.reject(400)
149 if (locStack
.length
> 0)
150 return Promise
.reject(404)
154 return new Promise((resolve
, reject
) => {
155 this.resolveClientPerms
.then((perms
) => {
156 let allow
= ['OPTIONS']
158 allow
.push('CONNECT')
160 allow
.push('DELETE').push('PUT').push('POST')
162 allow
.push('GET').push('HEAD')
163 resolve(allow
.join(', '))
167 return Promise
.reject(405)
181 return new Promise((resolve
, reject
) => {
182 this.resolveClientPerms
.then((perms
) => {
183 if ((reqPerms
& perms
) === reqPerms
)
184 this[method
](pubKey
, data
).then(resolve
).catch(reject
)
190 resolveClientPerms(pubKey
) {
191 return new Promise((resolve
, reject
) => {
192 if (!pubKey
|| pubKey
=== '')
193 resolve(this.perms
>>> 12 & 0xF)
194 else if (pubKey
=== this.owner
)
195 resolve((this.perms
>>> 8) & 0xF)
197 localforage
.getItem(`acct/${pubKey}`).then((account
) => {
198 let grpLen
= account
.groups
? account
.groups
.length
: 0
200 for (let i
= 0; i
< grpLen
; i
++) {
201 if (account
.groups
[i
] === this.group
) {
202 resolve((this.perms
>>> 4) & 0xF)
208 resolve(this.perms
& 0xF)
213 return Promise
.resolve()
216 return localforage
.getItem(this.path
)
219 return localforage
.setItem(this.path
, data
)
222 return new Promise((resolve
, reject
) => {
223 localforage
.getItem(this.path
)
225 this.setItem(this.path
, fData
+ data
)
233 return localforage
.removeItem(this.path
)
236 return new Promise((resolve
, reject
) => {
237 this.resolveClientPerms(pubKey
).then((perms
) => {
242 return this.GET(pubKey
)
249 StrappFile
.defaults
= {
254 changed
: StrappFileSystem
.bootTime
,
255 created
: StrappFileSystem
.bootTime
,
256 accessed
: StrappFileSystem
.bootTime
,
262 const StrappPeerConnection
= (() => {
263 class StrappPeerConnection
extends StrappFile
{
265 //get metadata (held in filesystem), with owner, usage info, etc
266 //if unauthed, send message down socket
269 //create w/ sdp, register callback (or pipe), set owner
275 //send routing message down socket
276 //POST(opts.routemessage)
279 return StrappPeerConnection
282 const StrappDirectory
= (() => {
283 const _traverse_loaded = function(method
, pubKey
, data
, locStack
) {
284 if (locStack
[0] in this.files
)
285 return this.files
[locStack
[0]].resolveRequest(method
, pubKey
, data
, locStack
.slice(1))
286 return Promise
.reject(404)
288 const _traverse_loading = function(method
, pubKey
, data
, locStack
) {
289 if (this.loadWaiters
)
290 return new Promise((resolve
, reject
) => this.loadWaiters
.push(
291 [method
, pubKey
, data
, locStack
, resolve
, reject
]
293 return _traverse_loaded(method
, pubKey
, data
, locStack
)
296 class StrappDirectory
extends StrappFile
{
297 static literal(...props
) {
298 return Object
.assign(Object
.create(null, StrappDirectory
.defaults
), ...props
)
300 loadFiles(fileData
) {
301 Object
.keys(fileData
)
302 .map((key
) => (this.files
[key
] = StrappFile
.parse(fileData
[key
])))
305 return new Promise((resolve
, reject
) => {
306 localforage
.getItem(this.path
).then((fileData
) => {
307 this.loadFiles(fileData
)
312 traverse(method
, pubKey
, data
, locStack
) {
313 this.traverse
= _traverse_loading
314 this.loadWaiters
= []
315 this.resolveOwnFiles().then(() => {
316 this.traverse
= _traverse_loaded
318 .map((w
) => _traverse_loaded(w
[0], w
[1], w
[2], w
[3]).then(w
[4], w
[5]))
319 delete this.loadWaiters
321 return _traverse_loading(method
, pubKey
, data
, locStack
)
323 request(method
, pubKey
, data
, locStack
) {
324 return new Promise((resolve
, reject
) => {
325 super.resolveRequest(method
, pubKey
, data
, locStack
)
329 return this.traverse(method
, pubKey
, data
, locStack
)
330 return Promise
.reject(err
)
335 //send routing message to the directory (handle the next part here)
338 StrappDirectory
.defaults
= {
339 type
: 'strapp/directory'
341 return StrappDirectory
344 const StrappDevice
= (() => {
345 class StrappDevice
extends StrappFile
{
350 const StrappJSON
= (() => {
351 class StrappJSON
extends StrappFile
{
353 return localforage
.assignItem(this.path
, data
)
359 export default StrappFileSystem
360 export { StrappFile
, StrappPeerConnection
, StrappDirectory
}