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
) => this.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]))
122 return StrappFileSystem
125 const StrappFile
= (() => {
126 class StrappFile
extends Object
{
127 constructor(...props
) {
129 return Object
.assign(this, new.target
.defaults
, ...props
)
131 static literal(...props
) {
132 return Object
.assign(Object
.create(null, StrappFile
.defaults
), ...props
)
134 static parse(fileLiteral
) {
135 switch(fileLiteral
.type
) {
136 case 'application/JSON':
137 return new StrappJSON(fileLiteral
)
138 case 'strapp/directory':
139 return new StrappDirectory(fileLiteral
)
142 return new StrappFile(fileLiteral
)
145 resolveRequest(method
, pubKey
, data
, locStack
) {
147 return Promise
.reject(400)
148 if (locStack
.length
> 0)
149 return Promise
.reject(404)
153 return new Promise((resolve
, reject
) => {
154 this.resolveClientPerms
.then((perms
) => {
155 let allow
= ['OPTIONS']
157 allow
.push('CONNECT')
159 allow
.push('DELETE').push('PUT').push('POST')
161 allow
.push('GET').push('HEAD')
162 resolve(allow
.join(', '))
166 return Promise
.reject(405)
180 return new Promise((resolve
, reject
) => {
181 this.resolveClientPerms
.then((perms
) => {
182 if ((reqPerms
& perms
) === reqPerms
)
183 this[method
](pubKey
, data
).then(resolve
).catch(reject
)
189 resolveClientPerms(pubKey
) {
190 return new Promise((resolve
, reject
) => {
191 if (!pubKey
|| pubKey
=== '')
192 resolve(this.perms
>>> 12 & 0xF)
193 else if (pubKey
=== this.owner
)
194 resolve((this.perms
>>> 8) & 0xF)
196 localforage
.getItem(`acct/${pubKey}`).then((account
) => {
197 let grpLen
= account
.groups
? account
.groups
.length
: 0
199 for (let i
= 0; i
< grpLen
; i
++) {
200 if (account
.groups
[i
] === this.group
) {
201 resolve((this.perms
>>> 4) & 0xF)
207 resolve(this.perms
& 0xF)
212 return Promise
.resolve()
215 return localforage
.getItem(this.path
)
218 return localforage
.setItem(this.path
, data
)
221 return new Promise((resolve
, reject
) => {
222 localforage
.getItem(this.path
)
224 this.setItem(this.path
, fData
+ data
)
232 return localforage
.removeItem(this.path
)
235 return new Promise((resolve
, reject
) => {
236 this.resolveClientPerms(pubKey
).then((perms
) => {
241 return this.GET(pubKey
)
248 StrappFile
.defaults
= {
253 changed
: StrappFileSystem
.bootTime
,
254 created
: StrappFileSystem
.bootTime
,
255 accessed
: StrappFileSystem
.bootTime
,
261 const StrappPeerConnection
= (() => {
262 class StrappPeerConnection
extends StrappFile
{
264 //get metadata (held in filesystem), with owner, usage info, etc
265 //if unauthed, send message down socket
268 //create w/ sdp, register callback (or pipe), set owner
274 //send routing message down socket
275 //POST(opts.routemessage)
278 return StrappPeerConnection
281 const StrappDirectory
= (() => {
282 const _traverse_loaded = function(method
, pubKey
, data
, locStack
) {
283 if (locStack
[0] in this.files
)
284 return this.files
[locStack
[0]].resolveRequest(method
, pubKey
, data
, locStack
.slice(1))
285 return Promise
.reject(404)
287 const _traverse_loading = function(method
, pubKey
, data
, locStack
) {
288 if (this.loadWaiters
)
289 return new Promise((resolve
, reject
) => this.loadWaiters
.push(
290 [method
, pubKey
, data
, locStack
, resolve
, reject
]
292 return _traverse_loaded(method
, pubKey
, data
, locStack
)
295 class StrappDirectory
extends StrappFile
{
296 static literal(...props
) {
297 return Object
.assign(Object
.create(null, StrappDirectory
.defaults
), ...props
)
299 loadFiles(fileData
) {
300 Object
.keys(fileData
)
301 .map((key
) => (this.files
[key
] = StrappFile
.parse(fileData
[key
])))
304 return new Promise((resolve
, reject
) => {
305 localforage
.getItem(this.path
).then((fileData
) => {
306 this.loadFiles(fileData
)
311 traverse(method
, pubKey
, data
, locStack
) {
312 this.traverse
= _traverse_loading
313 this.loadWaiters
= []
314 this.resolveOwnFiles().then(() => {
315 this.traverse
= _traverse_loaded
317 .map((w
) => _traverse_loaded(w
[0], w
[1], w
[2], w
[3]).then(w
[4], w
[5]))
318 delete this.loadWaiters
320 return _traverse_loading(method
, pubKey
, data
, locStack
)
322 request(method
, pubKey
, data
, locStack
) {
323 return new Promise((resolve
, reject
) => {
324 super.resolveRequest(method
, pubKey
, data
, locStack
)
328 return this.traverse(method
, pubKey
, data
, locStack
)
329 return Promise
.reject(err
)
334 //send routing message to the directory (handle the next part here)
337 StrappDirectory
.defaults
= {
338 type
: 'strapp/directory'
340 return StrappDirectory
343 const StrappDevice
= (() => {
344 class StrappDevice
extends StrappFile
{
349 const StrappJSON
= (() => {
350 class StrappJSON
extends StrappFile
{
352 return localforage
.assignItem(this.path
, data
)
358 export default StrappFileSystem
359 export { StrappFile
, StrappPeerConnection
, StrappDirectory
}